diff --git a/.gitignore b/.gitignore index ece91fd..a9efc76 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,5 @@ -/vendor/ -composer.lock -composer.phar -/var/ *.swp .idea/ -/config/config.yml -/web/js/config.js -/web/s/ \ No newline at end of file +/gen/ +/tmp/ +*-linux-amd64 diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5faca5e --- /dev/null +++ b/Makefile @@ -0,0 +1,21 @@ +GOFLAGS := --ldflags '-w -linkmode external' +CC := $(shell which musl-clang) + +all: images + +STATIC_FILES := $(wildcard static/*) + +images: coverservice/coverservice-linux-amd64 \ + coverservice/Dockerfile \ + nginx/Dockerfile \ + docker-compose.yml + docker-compose build + +COMMON_FILES := $(wildcard common/*.go) + +coverservice/coverservice-linux-amd64: \ + $(COMMON_FILES) \ + coverservice/main.go \ + $(wildcard coverservice/service/*.go) + cd coverservice && \ + CC=${CC} go build ${GOFLAGS} -o coverservice-linux-amd64 diff --git a/bin/console b/bin/console deleted file mode 100755 index 8ff5b7c..0000000 --- a/bin/console +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env php -getParameterOption(array('--env', '-e'), getenv('SYMFONY_ENV') ?: 'dev'); - -$app = require __DIR__.'/../src/app.php'; -require __DIR__.'/../config/'.$env.'.php'; -$console = require __DIR__.'/../src/console.php'; -$console->run(); diff --git a/common/handlers.go b/common/handlers.go new file mode 100644 index 0000000..dcadaa2 --- /dev/null +++ b/common/handlers.go @@ -0,0 +1,27 @@ +package common + +import ( + "net/http" + "strconv" + "encoding/json" +) + +const kInternalError = "An internal error occurred" + +type ErrorResponse struct { + Error string `json:"error"` +} + + +func WriteJsonResponse(w http.ResponseWriter, status int, data []byte) { + w.Header().Set("Content-Type", "application/json") + w.Header().Set("Content-Length", strconv.Itoa(len(data))) + w.WriteHeader(status) + w.Write(data) +} + + +func WriteInternalErrorResponse(w http.ResponseWriter) { + data, _ := json.Marshal(ErrorResponse{kInternalError}) + WriteJsonResponse(w, http.StatusInternalServerError, data) +} diff --git a/common/router.go b/common/router.go new file mode 100644 index 0000000..249996d --- /dev/null +++ b/common/router.go @@ -0,0 +1,18 @@ +package common + + +import "github.com/gorilla/mux" + + +func NewRouter(routes Routes) *mux.Router { + router := mux.NewRouter().StrictSlash(true) + + for _, route := range routes { + router.Methods(route.Method). + Path(route.Pattern). + Name(route.Name). + Handler(route.HandlerFunc) + } + + return router +} diff --git a/common/routes.go b/common/routes.go new file mode 100644 index 0000000..c271d50 --- /dev/null +++ b/common/routes.go @@ -0,0 +1,13 @@ +package common + +import "net/http" + + +type Route struct { + Name string + Method string + Pattern string + HandlerFunc http.HandlerFunc +} + +type Routes []Route diff --git a/common/utils.go b/common/utils.go new file mode 100644 index 0000000..9f3fa02 --- /dev/null +++ b/common/utils.go @@ -0,0 +1,36 @@ +package common + +import ( + "os" + "io" +) + +func CopyFile(src, dst string) (int64, error) { + sf, err := os.Open(src) + if err != nil { + return 0, err + } + defer sf.Close() + df, err := os.Create(dst) + if err != nil { + return 0, err + } + defer df.Close() + return io.Copy(df, sf) +} + + +func MoveFile(src, dst string) error { + err := os.Rename(src, dst) + if err != nil { + _, err = CopyFile(src, dst) + if err != nil { + return err + } + err = os.Remove(src) + if err != nil { + return err + } + } + return nil +} diff --git a/common/webserver.go b/common/webserver.go new file mode 100644 index 0000000..662b052 --- /dev/null +++ b/common/webserver.go @@ -0,0 +1,19 @@ +package common + + +import ( + "net/http" + "github.com/sirupsen/logrus" +) + + +func StartWebServer(port string, routes Routes) { + r := NewRouter(routes) + http.Handle("/", r) + logrus.Infof("Starting HTTP service at port %s", port) + err := http.ListenAndServe(":" + port, nil) + if err != nil { + logrus.Errorf("An error ocurred starting HTTP listener at port %s", port) + logrus.Error("Error: " + err.Error()) + } +} diff --git a/composer.json b/composer.json deleted file mode 100644 index 110df52..0000000 --- a/composer.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "name": "fabpot/silex-skeleton", - "description": "A pre-configured skeleton for the Silex microframework", - "license": "MIT", - "type": "project", - "require": { - "php": ">=5.3.3", - "silex/silex": "~1.0", - "silex/web-profiler": "~1.0", - "symfony/browser-kit": "~2.3", - "symfony/class-loader": "~2.3", - "symfony/config": "~2.3", - "symfony/console": "~2.3", - "symfony/css-selector": "~2.3", - "symfony/debug": "~2.3", - "symfony/finder": "~2.3", - "symfony/form": "~2.3", - "symfony/monolog-bridge": "~2.3", - "symfony/process": "~2.3", - "symfony/security": "~2.3", - "symfony/translation": "~2.3", - "symfony/twig-bridge": "~2.3", - "symfony/validator": "~2.3", - "symfony/yaml": "~2.3", - "deralex/yaml-config-service-provider": "1.0.x-dev" - - }, - "autoload": { - "psr-4": { "Caratula\\": "src/" } - }, - "extra": { - "branch-alias": { - "dev-master": "1.1.x-dev" - } - } -} diff --git a/config/config.yml.dist b/config/config.yml.dist deleted file mode 100644 index eecd1e0..0000000 --- a/config/config.yml.dist +++ /dev/null @@ -1,3 +0,0 @@ -storage: - dir: /home/something/path/s/ - path: http://domain.com/path/s/ \ No newline at end of file diff --git a/config/dev.php b/config/dev.php deleted file mode 100644 index 4eaa9f8..0000000 --- a/config/dev.php +++ /dev/null @@ -1,19 +0,0 @@ -register(new MonologServiceProvider(), array( - 'monolog.logfile' => __DIR__.'/../var/logs/silex_dev.log', -)); - -$app->register($p = new WebProfilerServiceProvider(), array( - 'profiler.cache_dir' => __DIR__.'/../var/cache/profiler', -)); -$app->mount('/_profiler', $p); diff --git a/config/prod.php b/config/prod.php deleted file mode 100644 index 26a5a51..0000000 --- a/config/prod.php +++ /dev/null @@ -1,6 +0,0 @@ - __DIR__.'/../var/cache/twig'); diff --git a/coverservice/Dockerfile b/coverservice/Dockerfile new file mode 100644 index 0000000..dadea2a --- /dev/null +++ b/coverservice/Dockerfile @@ -0,0 +1,17 @@ +FROM ubuntu:16.04 + +EXPOSE 8000 + +RUN apt update && \ + apt install -y --no-install-recommends texlive-latex-base texlive-fonts-recommended musl && \ + rm -rf /var/lib/apt/lists/* + +WORKDIR "/srv/coverservice" + +COPY coverservice/docker-entrypoint.sh /entrypoint.sh +COPY img/ucsp.png img/ +COPY templates/cover.tex templates/ +COPY coverservice/coverservice-linux-amd64 server + +ENTRYPOINT ["/entrypoint.sh"] +CMD ["./server"] diff --git a/coverservice/docker-entrypoint.sh b/coverservice/docker-entrypoint.sh new file mode 100755 index 0000000..a9545ac --- /dev/null +++ b/coverservice/docker-entrypoint.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +mkdir -p tmp gen + +exec "$@" diff --git a/coverservice/main.go b/coverservice/main.go new file mode 100644 index 0000000..e10315f --- /dev/null +++ b/coverservice/main.go @@ -0,0 +1,15 @@ +package main + +import ( + "math/rand" + "time" + "github.com/sirupsen/logrus" + "github.com/ACMUCSP/mkcaratula/common" + "github.com/ACMUCSP/mkcaratula/coverservice/service" +) + +func main() { + logrus.Infof("Starting %s", service.AppName) + rand.Seed(time.Now().UnixNano()) + common.StartWebServer("8000", service.Routes) +} diff --git a/coverservice/service/generator.go b/coverservice/service/generator.go new file mode 100644 index 0000000..924d1bc --- /dev/null +++ b/coverservice/service/generator.go @@ -0,0 +1,129 @@ +package service + +import ( + "io/ioutil" + "os/exec" + "io" + "text/template" + "math/rand" + "os" + "path" + "bytes" + "github.com/sirupsen/logrus" + "github.com/ACMUCSP/mkcaratula/common" +) + +type CoverContext struct { + Career string `json:"career"` + Title string `json:"title"` + Course string `json:"course"` + Gender string `json:"gender"` + Names []string `json:"names"` + Semester string `json:"semester"` +} + +func (ctx CoverContext) StudentsDeclaration() string { + var students string + if len(ctx.Names) > 1 { + if ctx.Gender == "f" { + students = "Las alumnas" + } else { + students = "Los alumnos" + } + return students + " declaran" + } else { + if ctx.Gender == "f" { + students = "La alumna" + } else { + students = "El alumno" + } + return students + " declara" + } +} + +const kKeyLength = 4 + +var kAlphabet = [][]byte{ + []byte("bcdfghjklmnpqrstvwxyz"), + []byte("aeiou"), +} + +func easyRandom() string { + buffer := make([]byte, kKeyLength) + for i := 0; i < kKeyLength; i++ { + buffer[i] = kAlphabet[i & 1][rand.Intn(len(kAlphabet[i & 1]))] + } + return string(buffer) +} + + +func storeCoverPage(tmpDir, genDir string) (string, error) { + filename := easyRandom() + ".pdf" + err := common.MoveFile(path.Join(tmpDir, "texput.pdf"), path.Join(genDir, filename)) + if err != nil { + return "", err + } + return filename, nil +} + + +func GenerateCoverPage(tmpDir, genDir string, ctx CoverContext) (filename string, err error) { + var tmpl *template.Template + tmpl, err = GetCoverTemplate() + if err != nil { + return + } + + cmd := exec.Command("pdflatex", "-halt-on-error") + cmd.Dir, err = ioutil.TempDir(tmpDir, "") + if err != nil { + logrus.Errorf("Failed to create temporary directory: %s", err) + return + } + var texStdin io.WriteCloser + texStdin, err = cmd.StdinPipe() + if err != nil { + logrus.Errorf("Failed to open pipe for pdflatex: %s", err) + return + } + go func() { + defer texStdin.Close() + tmpl.Execute(texStdin, ctx) + }() + + var out []byte + out, err = cmd.CombinedOutput() + if err != nil { + logrus.WithField("output", string(out)).Errorf("Failed to run pdflatex: %s", err) + return + } + + filename, err = storeCoverPage(cmd.Dir, genDir) + + if err != nil { + logrus.Errorf("Failed to store file: %s", err) + return + } + + err = os.RemoveAll(cmd.Dir) + + if err != nil { + filename = "" + logrus.Errorf("Failed to remove temporary directory: %s", err) + } + + return +} + + +func GetTexOutput(ctx CoverContext) (tex string, err error) { + var tmpl *template.Template + tmpl, err = GetCoverTemplate() + if err != nil { + return + } + var buffer bytes.Buffer + tmpl.Execute(&buffer, ctx) + tex = buffer.String() + return +} diff --git a/coverservice/service/handlers.go b/coverservice/service/handlers.go new file mode 100644 index 0000000..b19a163 --- /dev/null +++ b/coverservice/service/handlers.go @@ -0,0 +1,118 @@ +package service + +import ( + "path" + "os" + "bytes" + "net/http" + "encoding/json" + "github.com/gorilla/mux" + "github.com/ACMUCSP/mkcaratula/common" +) + +const AppName = "coverservice" + +type CoverResponse struct { + Url string `json:"url"` +} + +type TexResponse struct { + Tex string `json:"tex"` +} + +const kFileNotFound = "File not found" +const kFailedToProcessIncomingData = "Failed to process incoming data" +const kTmpDir = "tmp" +const kGenDir = "gen" + + +func filterSpecialCharacters(str string) string { + var buffer bytes.Buffer + openQuotes := false + for _, c := range str { + switch c { + case '"': + if openQuotes { + buffer.WriteString("''") + } else { + buffer.WriteString("``") + } + openQuotes = !openQuotes + case '&', '%', '$', '#', '_', '{', '}': + buffer.WriteRune('\\') + buffer.WriteRune(c) + case '~': + buffer.WriteString("\textasciitilde") + case '^': + buffer.WriteString("\textasciicircum") + case '\\': + buffer.WriteString("\textbackslash") + default: + buffer.WriteRune(c) + } + } + return buffer.String() +} + + +func processContext(ctx *CoverContext) { + ctx.Career = filterSpecialCharacters(ctx.Career) + ctx.Title = filterSpecialCharacters(ctx.Title) + ctx.Course = filterSpecialCharacters(ctx.Course) + ctx.Semester = filterSpecialCharacters(ctx.Semester) + for i, name := range ctx.Names { + ctx.Names[i] = filterSpecialCharacters(name) + } +} + + +func GenerateCoverHandler(w http.ResponseWriter, r *http.Request) { + decoder := json.NewDecoder(r.Body) + var ctx CoverContext + err := decoder.Decode(&ctx) + if err != nil { + data, _ := json.Marshal(common.ErrorResponse{kFailedToProcessIncomingData}) + common.WriteJsonResponse(w, http.StatusBadRequest, data) + return + } + processContext(&ctx) + + if _, ok := r.URL.Query()["tex"]; ok { + + tex, err := GetTexOutput(ctx) + + if err != nil { + common.WriteInternalErrorResponse(w) + } + + data, _ := json.Marshal(TexResponse{tex}) + common.WriteJsonResponse(w, http.StatusOK, data) + } else { + + filename, err := GenerateCoverPage(kTmpDir, kGenDir, ctx) + + if err != nil { + common.WriteInternalErrorResponse(w) + return + } + pdfUrl := *r.URL + pdfUrl.Path = path.Join(path.Dir(r.URL.Path), filename) + + data, _ := json.Marshal(CoverResponse{pdfUrl.String()}) + common.WriteJsonResponse(w, http.StatusOK, data) + } +} + + +func GetCoverHandler(w http.ResponseWriter, r *http.Request) { + key := mux.Vars(r)["key"] + filename := path.Join(kGenDir, key + ".pdf") + if _, err := os.Stat(filename); err == nil { + w.Header().Set("Content-Type", "application/pdf") + w.Header().Set("X-Accel-Redirect", "/" + path.Join(AppName, filename)) + w.WriteHeader(http.StatusOK) + } else { + w.WriteHeader(http.StatusNotFound) + w.Write([]byte(kFileNotFound)) + } +} diff --git a/coverservice/service/routes.go b/coverservice/service/routes.go new file mode 100644 index 0000000..e008fef --- /dev/null +++ b/coverservice/service/routes.go @@ -0,0 +1,21 @@ +package service + +import ( + "github.com/ACMUCSP/mkcaratula/common" +) + + +var Routes = common.Routes{ + common.Route{ + "GenerateCover", + "POST", + "/caratula/generate", + GenerateCoverHandler, + }, + common.Route{ + "GetCover", + "GET", + "/caratula/{key:[a-z]+}.pdf", + GetCoverHandler, + }, +} diff --git a/coverservice/service/template.go b/coverservice/service/template.go new file mode 100644 index 0000000..7efb524 --- /dev/null +++ b/coverservice/service/template.go @@ -0,0 +1,22 @@ +package service + +import ( + "text/template" + "github.com/sirupsen/logrus" +) + +var coverTemplate *template.Template + + +func GetCoverTemplate() (tmpl *template.Template, err error) { + if coverTemplate == nil { + coverTemplate, err = template.ParseFiles("templates/cover.tex") + if err != nil { + logrus.Errorf("Failed to process template: %s", err) + return + } + } + + tmpl = coverTemplate + return +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..49b20d1 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,23 @@ +version: "3.3" + +services: + coverservice: + build: + context: . + dockerfile: coverservice/Dockerfile + image: acmucsp/mkcaratula_coverservice + volumes: + - cover_gen:/srv/coverservice/gen + + nginx: + build: + context: . + dockerfile: nginx/Dockerfile + image: acmucsp/mkcaratula_nginx + volumes: + - cover_gen:/srv/coverservice/gen + ports: + - 80:80 + +volumes: + cover_gen: diff --git a/web/tmp/ucsp.png b/img/ucsp.png similarity index 100% rename from web/tmp/ucsp.png rename to img/ucsp.png diff --git a/nginx/Dockerfile b/nginx/Dockerfile new file mode 100644 index 0000000..2f1d6bf --- /dev/null +++ b/nginx/Dockerfile @@ -0,0 +1,4 @@ +FROM nginx:stable-alpine + +COPY nginx/default.conf /etc/nginx/conf.d/ +COPY static /srv/static \ No newline at end of file diff --git a/nginx/default.conf b/nginx/default.conf new file mode 100644 index 0000000..c3ae4c1 --- /dev/null +++ b/nginx/default.conf @@ -0,0 +1,23 @@ +server { + listen 80; + + location ~ ^/caratula$ { + return 301 /caratula/; + } + + location ~ ^/caratula(.*) { + root /srv/static/; + try_files $1 $1/index.html @proxy; + } + + location @proxy { + proxy_pass http://coverservice:8000; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $remote_addr; + } + + location /coverservice/gen/ { + internal; + alias /srv/coverservice/gen/; + } +} \ No newline at end of file diff --git a/src/Utils/FileManager.php b/src/Utils/FileManager.php deleted file mode 100644 index b58b660..0000000 --- a/src/Utils/FileManager.php +++ /dev/null @@ -1,55 +0,0 @@ -path = $storage_config['path']; - $this->dir = $storage_config['dir']; - } - - public function upload($file, &$key) - { - $rfile = ""; - do { - $rfile = $this->easyRandom(); - } while($this->exist($rfile)); - - $result = copy($file, $this->dir . $rfile . '.pdf'); - - $key = $rfile; - return $result; - } - - public function exist($file) - { - return file_exists($this->dir . $file . '.pdf'); - } - - public function getURL($key) - { - return ($this->exist($key)) ? $this->path . $key . '.pdf' : false; - } - - public function easyRandom() - { - $alphabet = array( - "bcdfghjklmnpqrstvwxyz", - "aeiou" - ); - - $string = ""; - for($i = 0; $i < 6; ++$i) - $string .= $alphabet[$i%2][rand(0,strlen($alphabet[$i%2])-1)]; - - return $string; - } -} \ No newline at end of file diff --git a/src/app.php b/src/app.php deleted file mode 100644 index 944105a..0000000 --- a/src/app.php +++ /dev/null @@ -1,25 +0,0 @@ -register(new UrlGeneratorServiceProvider()); -$app->register(new ValidatorServiceProvider()); -$app->register(new ServiceControllerServiceProvider()); -$app->register(new TwigServiceProvider()); -$app->register(new DerAlex\Silex\YamlConfigServiceProvider(__DIR__ .'/../config/config.yml')); -$app['twig'] = $app->share($app->extend('twig', function($twig, $app) { - // add custom globals, filters, tags, ... - - return $twig; -})); - -return $app; diff --git a/src/console.php b/src/console.php deleted file mode 100644 index 465e14f..0000000 --- a/src/console.php +++ /dev/null @@ -1,23 +0,0 @@ -getDefinition()->addOption(new InputOption('--env', '-e', InputOption::VALUE_REQUIRED, 'The Environment name.', 'dev')); -$console->setDispatcher($app['dispatcher']); -$console - ->register('my-command') - ->setDefinition(array( - // new InputOption('some-option', null, InputOption::VALUE_NONE, 'Some help'), - )) - ->setDescription('My command description') - ->setCode(function (InputInterface $input, OutputInterface $output) use ($app) { - // do something - }) -; - -return $console; diff --git a/src/controllers.php b/src/controllers.php deleted file mode 100644 index cdf34df..0000000 --- a/src/controllers.php +++ /dev/null @@ -1,128 +0,0 @@ -before(function (Request $request) { - if (0 === strpos($request->headers->get('Content-Type'), 'application/json')) { - $data = json_decode($request->getContent(), true); - $request->request->replace(is_array($data) ? $data : array()); - } -}); - -$app->get('/', function (Request $request) use ($app) { - return $app->redirect($request->getBasePath() . '/app.html'); -})->bind('homepage'); - -$app->get('/d/{key}', function ($key) use ($app) { - $fs = new FileManager($app); - $url = $fs->getURL($key); - if ($url) - return $app->redirect($url,302); - - return $app->abort(404, "Carátula no encontrada."); -})->bind('download'); - -function joinNames($L) { - $n = count($L); - if ($n) { - $s = $L[0]; - for ($i = 1; $i < $n; ++$i) - $s .= '\\\\ ' . trim($L[$i]); - return $s; - } else { - return ''; - } -} - -function filterQuote($s) { - $single = false; - $double = false; - $r = ''; - for ($i = 0; $i < strlen($s); ++$i) { - if ($s[$i] == '"') { - $r .= $double ? "''" : "``"; - $double = !$double; - } elseif ($s[$i] == "'") { - $r .= $single ? "'" : "`"; - $single = !$single; - } else { - $r .= $s[$i]; - } - } - return $r; -} - -function processContext($context) { - if (array_key_exists('name', $context)) { - $L = explode('/', $context['name']); - $context['name'] = joinNames($L); - $context['number'] = count($L); - } else { - $context['number'] = 1; - } - if (array_key_exists('title', $context)) { - $context['title'] = filterQuote($context['title']); - } - $categories = array( - 'inmasc' => 'El alumno declara', - 'infem' => 'La alumna declara', - 'grupal' => 'Los alumnos declaran'); - $context['cat'] = - array_key_exists('cat', $context) && - array_key_exists($context['cat'], $categories) ? - $categories[$context['cat']] : $categories['inmasc']; - return $context; -} - -$app->post('/gen', function(Request $request) use ($app) { - $context = processContext($request->request->all()); - $tex = $app['twig']->render('caratula.tex', $context); - if ($context['tex']) - return new Response( - $tex, 200, array('Content-Type' => 'text/plain')); - $location = __DIR__ . '/../web/tmp/'; - $tmpdir = exec('mktemp -d -p ' . $location); - $comp_pr = new Process('pdflatex -halt-on-error', - $tmpdir, array('PATH' => '/usr/bin'), $tex); - $comp_pr->run(); - $response = null; - if ($comp_pr->isSuccessful()) { - $file = fopen($tmpdir . '/texput.pdf', 'r'); - $pdf = fread($file, filesize($tmpdir . '/texput.pdf')); - fclose($file); - - $key = ""; - $fs = new FileManager($app); - $fs->upload($tmpdir . '/texput.pdf', $key); - - $response = new Response( - $app->url('download', array('key' => $key)), - 201, - array('Content-Type' => 'text/plain', 'Location' => $fs->getURL($key))); - } else { - $response = new Response( - $comp_pr->getOutput(), 400, array('Content-Type' => 'text/plain')); - } - exec('rm -r ' . $tmpdir); - return $response; -})->bind('caratula'); - - -$app->error(function (\Exception $e, $code) use ($app) { - if ($app['debug']) { - return; - } - - // 404.html, or 40x.html, or 4xx.html, or error.html - $templates = array( - 'errors/'.$code.'.html', - 'errors/'.substr($code, 0, 2).'x.html', - 'errors/'.substr($code, 0, 1).'xx.html', - 'errors/default.html', - ); - - return new Response($app['twig']->resolveTemplate($templates)->render(array('code' => $code)), $code); -}); diff --git a/web/css/main.css b/static/css/main.css similarity index 60% rename from web/css/main.css rename to static/css/main.css index 00a95f7..ca67dd0 100644 --- a/web/css/main.css +++ b/static/css/main.css @@ -2,13 +2,12 @@ body { padding-top: 10px; margin: auto; } -.erase-non-clickable { - cursor: default; +.input-group-addon { color: #777; } .erase-clickable { + color: #d9534f !important; cursor: pointer; - color: #d9534f; } .error-msg { color: #d9534f; @@ -17,3 +16,8 @@ pre { max-height: 350px; overflow: auto; } +.modal-icon { + color: #ccc; + font-size: 6em; + text-align: center; +} diff --git a/web/app.html b/static/index.html similarity index 85% rename from web/app.html rename to static/index.html index 4749210..0a472cf 100644 --- a/web/app.html +++ b/static/index.html @@ -5,7 +5,7 @@ Generador de Carátulas | UCSP ACM Student Chapter - + @@ -58,16 +58,15 @@ - - - + + - - + + diff --git a/web/js/main.js b/static/js/main.js similarity index 57% rename from web/js/main.js rename to static/js/main.js index d1829af..75ae04b 100644 --- a/web/js/main.js +++ b/static/js/main.js @@ -40,12 +40,19 @@ caratulaControllers.controller('MainController', function ($scope, $http, $locat career: '', title: '', course: '', - cat: 'inmasc', - name: '', - sem: '1er', - tex: false + gender: 'm', + names: [], + semester: '1er' }; - $scope.names = [{id: 1, name: ''}, {id: 2, name: ''}]; + if ($scope.data.names.length > 0) { + $scope.names = []; + for (var i = 0; i < $scope.data.names.length; ++i) { + $scope.names.push({id: i + 1, name: $scope.data.names[i]}) + } + $scope.data.names = []; + } else { + $scope.names = [{id: 1, name: ''}]; + } $scope.showNamesLabel = function (item) { return item.id === 1; }; @@ -56,7 +63,7 @@ caratulaControllers.controller('MainController', function ($scope, $http, $locat } }; $scope.removeNameBox = function (item) { - if ($scope.names.length <= 2) { + if ($scope.names.length === 1) { item.name = ''; } else { var cnt = 0, newNames = []; @@ -71,43 +78,52 @@ caratulaControllers.controller('MainController', function ($scope, $http, $locat } }; $scope.run = function (getLatexCode) { - var previousName = $scope.data.name; + var url = "generate", newWindow; if ($scope.form.$invalid) { return; } - $scope.data.tex = getLatexCode; - if ($scope.data.cat === 'grupal') { - var names = $scope.names[0].name; - for (var i = 1; i < $scope.names.length; ++i) { - if ($scope.names[i].name !== '') { - names += '/' + $scope.names[i].name; - } + $scope.data.names = []; + for (var i = 0; i < $scope.names.length; ++i) { + var name = $scope.names[i].name.trim(); + if (name !== '') { + $scope.data.names.push(name); } - $scope.data.name = names; } $rootScope.data = $scope.data; $scope.loading = true; - $http.post(genUrl, $scope.data). - success(function (data, status, headers, config) { + if (getLatexCode) { + url = url + "?tex"; + } else { + newWindow = window.open() + } + $http.post(url, $scope.data). + then(function (response) { $scope.loading = false; - if (status === 201) { - var newWindow = window.open(headers('Location'), '_blank'); - $scope.url = data; - } else { - $rootScope.code = data; + if (getLatexCode) { + console.log(response.data); + $rootScope.code = response.data.tex; $location.path('/tex'); + } else { + $scope.url = newWindow.location = response.data.url; + } + }, function (response) { + if (newWindow !== null) { + newWindow.close(); } - $scope.data.name = previousName; - }). - error(function (data, status, headers, config) { $scope.loading = false; - if (status === 400) { - $rootScope.code = data; - $location.path('/error'); + $scope.modal = { + title: "Algo salió mal ☹" + }; + if (response.status === 400) { + $scope.modal.message = "Al parecer algún campo fue llenado incorrectamente. " + + "Por favor revisa el formulario e intenta nuevamente."; + $scope.modal.glyphicon = "glyphicon-eye-open"; } else { - // inform to dev + $scope.modal.message = "Un error ocurrió durante la generación de la carátula. " + + "Lo arreglaremos a la brevedad."; + $scope.modal.glyphicon = "glyphicon-wrench"; } - $scope.data.name = previousName; + $("#message-modal").modal(); }); } }); diff --git a/web/loader.gif b/static/loader.gif similarity index 100% rename from web/loader.gif rename to static/loader.gif diff --git a/web/logo.png b/static/logo.png similarity index 100% rename from web/logo.png rename to static/logo.png diff --git a/web/partials/error.html b/static/partials/error.html similarity index 100% rename from web/partials/error.html rename to static/partials/error.html diff --git a/web/partials/main.html b/static/partials/main.html similarity index 54% rename from web/partials/main.html rename to static/partials/main.html index 24287d3..71bfa21 100644 --- a/web/partials/main.html +++ b/static/partials/main.html @@ -20,54 +20,39 @@ +
+ +
+ +
+
- -
- -
- -
-
-
-
-
- -
- -
-
-
+
- + ng-required="item.id == 1 || item.id !== names.length"> +
@@ -75,40 +60,40 @@
@@ -122,10 +107,22 @@ Ver Latex - - - {{ url | noscheme }} - + + + diff --git a/web/partials/tex.html b/static/partials/tex.html similarity index 100% rename from web/partials/tex.html rename to static/partials/tex.html diff --git a/templates/caratula.tex b/templates/caratula.tex deleted file mode 100644 index 2da66fc..0000000 --- a/templates/caratula.tex +++ /dev/null @@ -1,73 +0,0 @@ -\documentclass[pdftex,12pt,a4paper]{article} - -\usepackage[pdftex]{graphicx} -\usepackage[utf8]{inputenc} -\usepackage[spanish]{babel} -\usepackage[a4paper]{geometry} -\linespread{1.5}\selectfont -\usepackage{setspace} -\usepackage{helvet} - -\begin{document} - -\newgeometry{right=2.5cm,left=2.5cm,top=3.5cm,bottom=2.5cm} -\begin{titlepage} -\fontfamily{phv}\selectfont - -\begin{minipage}[t][0.99\textheight][t]{0.9\textwidth} - - \begin{center} - - \begin{flushright} - \includegraphics[width=0.5\textwidth]{../ucsp.png}\\[1cm] - \end{flushright} - \textsc{\Huge {{ career|raw }} }\\[0.8cm] - \textsc{\LARGE {{ title|raw }} }\\[0.8cm] - \textsc{\huge {{ course|raw }} }\\[1.2cm] - \end{center} - - \begin{flushright} - \begin{minipage}{0.5\textwidth} - \begin{flushleft} - \textsc{\Large {{ name|raw }}\\[0.5cm]} - \textsc{\Large {{ sem }} Semestre}\\[0.5cm] - \textsc{\Large \the\year}\\ - \end{flushleft} - \end{minipage} - \end{flushright} - - \vfill - \begin{center} - \begin{minipage}{0.95\textwidth} - \large{ - ``{{ cat }} haber realizado el presente trabajo de acuerdo - a las normas de la Universidad Católica San Pablo''} - \end{minipage} - - \vspace{1.8cm} - - \line(1,0){70} - {% if number > 1 %} - {% for i in 2..min(number,4) %} - \hspace{0.8em} - \line(1,0){80} - {% endfor %} - {% if number > 4 %} - - \vspace{0.7cm} - - {% for i in 5..number %} - \hspace{0.8em} - \line(1,0){70} - {% endfor %} - {% endif %} - {% endif %} - - \end{center} - -\end{minipage} - -\end{titlepage} -\restoregeometry - -\end{document} diff --git a/templates/cover.tex b/templates/cover.tex new file mode 100644 index 0000000..d6bfbda --- /dev/null +++ b/templates/cover.tex @@ -0,0 +1,65 @@ +\documentclass[pdftex,12pt,a4paper]{article} + +\usepackage[pdftex]{graphicx} +\usepackage[T1]{fontenc} +\usepackage[utf8]{inputenc} +\usepackage[a4paper]{geometry} +\linespread{1.5}\selectfont +\usepackage{setspace} +\usepackage{helvet} + +\begin{document} + +\newgeometry{right=2.5cm,left=2.5cm,top=3.5cm,bottom=2.5cm} + +\begin{titlepage} +\fontfamily{phv}\selectfont + + \begin{center} + + \begin{flushright} + \includegraphics[width=0.4\textwidth]{../../img/ucsp.png}\\[1cm] + \end{flushright} + \textsc{\Huge {{ .Career }} }\\[0.8cm] + \textsc{\LARGE {{ .Title }} }\\[0.8cm] + \textsc{\huge {{ .Course }} }\\[1.2cm] + \end{center} + + \begin{flushright} + \begin{minipage}{0.5\textwidth} + \begin{flushleft} + \textsc{\Large + {{- range $i, $name := .Names }} + {{- if $i }}\\{{ end }} + {{ $name -}} + {{ end -}}\\[0.5cm]} + \textsc{\Large {{ .Semester }} Semestre}\\[0.5cm] + \textsc{\Large \the\year}\\ + \end{flushleft} + \end{minipage} + \end{flushright} + + \vfill + \begin{center} + \begin{minipage}{0.95\textwidth} + \large{``{{ .StudentsDeclaration }} haber realizado el presente trabajo de acuerdo + a las normas de la Universidad Católica San Pablo''} + \end{minipage} + + \vspace{1.8cm} + + {{ range $i, $name := .Names }} + {{- if and (ne $i 3) $i }} \hspace{1em} {{ else if eq $i 3 }} + + \vspace{0.7cm} + + {{ end -}} + \line(1,0){100} + {{- end }} + + \end{center} + +\end{titlepage} +\restoregeometry + +\end{document} diff --git a/templates/errors/404.html b/templates/errors/404.html deleted file mode 100644 index 3fadedd..0000000 --- a/templates/errors/404.html +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} -Página no encontrada. -{% endblock %} diff --git a/templates/errors/4xx.html b/templates/errors/4xx.html deleted file mode 100644 index df14b02..0000000 --- a/templates/errors/4xx.html +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} -Un error ocurrió en el cliente. -{% endblock %} diff --git a/templates/errors/500.html b/templates/errors/500.html deleted file mode 100644 index d363343..0000000 --- a/templates/errors/500.html +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} -Error interno en el servidor. -{% endblock %} diff --git a/templates/errors/5xx.html b/templates/errors/5xx.html deleted file mode 100644 index 3593d97..0000000 --- a/templates/errors/5xx.html +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} -Un error ocurrió en el servidor. -{% endblock %} diff --git a/templates/errors/default.html b/templates/errors/default.html deleted file mode 100644 index f14f619..0000000 --- a/templates/errors/default.html +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "layout.html" %} - -{% block content %} -Un error ocurrió. -{% endblock %} diff --git a/templates/layout.html b/templates/layout.html deleted file mode 100644 index efb49e2..0000000 --- a/templates/layout.html +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - Generador de Carátulas | UCSP ACM Student Chapter - - - - - - -
- - -
-
- -
-
- {% block content %} - {% endblock %} -
-
- - -
- -
- -{% block js %} {% endblock %} - - - - \ No newline at end of file diff --git a/web/index.php b/web/index.php deleted file mode 100644 index 522c5ba..0000000 --- a/web/index.php +++ /dev/null @@ -1,10 +0,0 @@ -run(); diff --git a/web/index_dev.php b/web/index_dev.php deleted file mode 100644 index cfdf365..0000000 --- a/web/index_dev.php +++ /dev/null @@ -1,22 +0,0 @@ -run(); diff --git a/web/js/config.js.dist b/web/js/config.js.dist deleted file mode 100644 index 8943125..0000000 --- a/web/js/config.js.dist +++ /dev/null @@ -1 +0,0 @@ -var genUrl = 'http://localhost:8000/index_dev.php/gen'; \ No newline at end of file