From d2659172f7532ca04fd98449b4d74a0cc49d29e6 Mon Sep 17 00:00:00 2001 From: "R. Eric Wheeler" Date: Sun, 17 Jul 2016 17:02:58 -0700 Subject: [PATCH] Finished auto pdf generation --- app/App.php | 257 ++++++++-------- app/providers.php | 19 +- app/themes/default/base.html.twig | 2 + app/themes/default/cover.html.twig | 66 ++++ app/themes/default/index.html.twig | 156 +++++----- app/themes/default/references.html.twig | 3 +- app/themes/default/skills.html.twig | 15 +- data/resume.json | 13 +- src/Sikofitt/Command/CreatePdfCommand.php | 43 ++- src/Sikofitt/Config/ConfigServiceProvider.php | 48 +-- .../Controller/ApiControllerProvider.php | 78 +++-- .../Controller/ResumeControllerProvider.php | 224 ++++---------- src/Sikofitt/Form/Type/ContactType.php | 3 +- src/Sikofitt/Resume/Pdf.php | 147 +++++++++ src/Sikofitt/Resume/ResumeParser.php | 283 ++++++++++++++++-- src/Sikofitt/Twig/RenderProfile.php | 8 +- src/Sikofitt/js/resume.js | 2 + src/Sikofitt/less/resume.less | 14 + web/.htaccess | 9 + web/index.php | 1 - web/robots.txt | 5 + 21 files changed, 912 insertions(+), 484 deletions(-) create mode 100644 app/themes/default/cover.html.twig create mode 100644 src/Sikofitt/Resume/Pdf.php create mode 100644 web/.htaccess create mode 100644 web/robots.txt diff --git a/app/App.php b/app/App.php index 4d6b148..76434e6 100644 --- a/app/App.php +++ b/app/App.php @@ -13,7 +13,6 @@ use Sikofitt\Config\ConfigTrait; use Sikofitt\Json\JsonFileTrait; use Sikofitt\Json\JsonTrait; use Silex\Application; -use Symfony\Component\HttpFoundation\Request; require __DIR__ . '/../vendor/autoload.php'; @@ -33,75 +32,72 @@ class App extends Application use Application\UrlGeneratorTrait; private $debug; - /** - * Returns the application directory. - * - * @return string - * The main application directory. - */ - public function getAppDirectory() - { - $r = new ReflectionClass($this); - return dirname($r->getFileName()); - } - /** - * Returns the root directory of the application. - * - * @return string - * The root directory of the application. - */ - public function getRootDirectory() - { - return dirname($this->getAppDirectory()); - } + /** + * @return string + */ + public function getConfDirectory() + { + return $this->getAppDirectory() . '/config'; + } - /** - * @return string - */ - public function getConfDirectory() - { - return $this->getAppDirectory() . '/config'; - } + /** + * Returns the application directory. + * + * @return string + * The main application directory. + */ + public function getAppDirectory() + { + $r = new ReflectionClass($this); + return dirname($r->getFileName()); + } - /** - * @return string - */ - public function getDataDirectory() - { - return $this->getRootDirectory() . '/data'; - } + /** + * @return string + */ + public function getResumeJson() + { + return $this->getDataDirectory() . '/resume.json'; + } - /** - * @return string - */ - public function getResumeJson() - { - return $this->getDataDirectory() . '/resume.json'; - } + /** + * @return string + */ + public function getDataDirectory() + { + return $this->getRootDirectory() . '/data'; + } - /** - * @return string - */ - public function getResumeSchema() - { - return $this->getDataDirectory() . '/schema/schema.v1.json'; - } + /** + * Returns the root directory of the application. + * + * @return string + * The root directory of the application. + */ + public function getRootDirectory() + { + return dirname($this->getAppDirectory()); + } + + /** + * @return string + */ + public function getResumeSchema() + { + return $this->getDataDirectory() . '/schema/schema.v1.json'; + } public function getLogDirectory() { return $this->getDataDirectory() . '/logs'; } - /** - * Registers media icons - * - * @param \Sikofitt\Image\Profile\ProfileIconInterface $icon - */ - public function registerIcon(\Sikofitt\Image\Profile\ProfileIconInterface $icon) - { - $this->config(sprintf('app.icons.%s', $icon->getName()), ['icon' => $icon->getIcon(), 'url' => $icon->getDefaultUrl()]); - } + public function getDebug() + { + return $this['debug']; + } + public function setDebug() { $this['debug'] = (null !== $this->config('app.debug') ? $this->config('app.debug') : true); @@ -111,77 +107,80 @@ class App extends Application } } - public function getDebug() - { - return $this['debug']; - } - public function registerExtenders() - { - if (!$this['debug']) { - $this->log('In Error handler.'); - $this->error(function (\Exception $e, \Symfony\Component\HttpFoundation\Request $request, $code) { - switch ($code) { - case 405: - preg_match('/\(Allow\:(.+)\)/', $e->getMessage(), $matches); - if (isset($matches[1])) { - $matches = trim($matches[1]); - } elseif (isset($matches[0])) { - $matches = trim($matches[0]); - } else { - $matches = 'Available methods are unknown.'; - } - $message = [ - 'status' => 'error', - 'message' => 'Method not allowed', - 'allowedMethods' => $matches, - 'requestedMethod' => $request->getMethod(), - 'code' => $code, - ]; - if($request->isXmlHttpRequest()) { - $message = json_encode($message); - } else { - $message = $this->renderView('error.405.html.twig', $message); - } - $this->log($e->getMessage(), ['code' => $code], \Symfony\Bridge\Monolog\Logger::WARNING); - break; - case 500: - $message = json_encode(['status' => 'error', 'message' => 'Critical Error', 'code' => $code]); - $this->log($e->getMessage(), ['code' => $code], \Symfony\Bridge\Monolog\Logger::CRITICAL); - break; - default: - $message = ['status' => 'error', 'message' => $e->getMessage(), 'code' => $code, 'requestUri' => $request->getRequestUri()]; - if($request->isXmlHttpRequest()) { - $message = json_decode($message); - } else { - $message = $this->renderView('error.html.twig', $message); - } - - $this->log($e->getMessage(), ['code' => $code], \Symfony\Bridge\Monolog\Logger::ERROR); - break; - } - - return new \Symfony\Component\HttpFoundation\Response($message, $code); - }); - } - $this->extend('twig', function (\Twig_Environment $twig) { - if ($this['debug']) { - $twig->enableDebug(); - } - $twig->addExtension(new Twig_Extensions_Extension_Date()); - $twig->addExtension(new Sikofitt\Twig\Date()); - $twig->addExtension(new Sikofitt\Twig\RenderProfile()); - $twig->addGlobal('config', $this->config('all')); - return $twig; - }); - } public function boot() { $this->registerExtenders(); - // register default icons - $this->registerDefaultIcons(); + // register default icons + $this->registerDefaultIcons(); return parent::boot(); } + public function registerExtenders() + { + if (!$this['debug']) { + $this->log('In Error handler.'); + $this->error(function (\Exception $e, \Symfony\Component\HttpFoundation\Request $request, $code) { + switch ($code) { + case 405: + preg_match('/\(Allow\:(.+)\)/', $e->getMessage(), $matches); + if (isset($matches[1])) { + $matches = trim($matches[1]); + } elseif (isset($matches[0])) { + $matches = trim($matches[0]); + } else { + $matches = 'Available methods are unknown.'; + } + $message = [ + 'status' => 'error', + 'message' => 'Method not allowed', + 'allowedMethods' => $matches, + 'requestedMethod' => $request->getMethod(), + 'code' => $code, + ]; + if ($request->isXmlHttpRequest()) { + $message = json_encode($message); + } else { + $message = $this->renderView('error.405.html.twig', $message); + } + $this->log($e->getMessage(), ['code' => $code], \Symfony\Bridge\Monolog\Logger::WARNING); + break; + case 500: + $message = ['status' => 'error', 'message' => 'Critical Error', 'code' => $code]; + if ($request->isXmlHttpRequest()) { + $message = json_decode($message); + } else { + $message = $this->renderView('error.html.twig', $message); + } + + $this->log($e->getMessage(), ['code' => $code], \Symfony\Bridge\Monolog\Logger::CRITICAL); + break; + default: + $message = ['status' => 'error', 'message' => $e->getMessage(), 'code' => $code, 'requestUri' => $request->getRequestUri()]; + if ($request->isXmlHttpRequest()) { + $message = json_decode($message); + } else { + $message = $this->renderView('error.html.twig', $message); + } + + $this->log($e->getMessage(), ['code' => $code], \Symfony\Bridge\Monolog\Logger::ERROR); + break; + } + + return new \Symfony\Component\HttpFoundation\Response($message, $code); + }); + } + $this->extend('twig', function (\Twig_Environment $twig) { + if ($this['debug']) { + $twig->enableDebug(); + } + $twig->addExtension(new Twig_Extensions_Extension_Date()); + $twig->addExtension(new Sikofitt\Twig\Date()); + $twig->addExtension(new Sikofitt\Twig\RenderProfile()); + $twig->addGlobal('config', $this->config('all')); + return $twig; + }); + } + public function registerDefaultIcons() { $this->registerIcon(new \Sikofitt\Image\Profile\TwitterProfileIcon()); @@ -190,4 +189,14 @@ class App extends Application $this->registerIcon(new \Sikofitt\Image\Profile\GitlabProfileIcon()); $this->registerIcon(new \Sikofitt\Image\Profile\LinkedinProfileIcon()); } + + /** + * Registers media icons + * + * @param \Sikofitt\Image\Profile\ProfileIconInterface $icon + */ + public function registerIcon(\Sikofitt\Image\Profile\ProfileIconInterface $icon) + { + $this->config(sprintf('app.icons.%s', $icon->getName()), ['icon' => $icon->getIcon(), 'url' => $icon->getDefaultUrl()]); + } } diff --git a/app/providers.php b/app/providers.php index 9b800a4..4ebdf2d 100644 --- a/app/providers.php +++ b/app/providers.php @@ -29,9 +29,9 @@ use Silex\Provider\{ VarDumperServiceProvider, WebProfilerServiceProvider }; +use Symfony\Bridge\Monolog\Logger; use Symfony\Component\Debug\ErrorHandler; use Symfony\Component\Debug\ExceptionHandler; -use Symfony\Bridge\Monolog\Logger; use WhoopsPimple\WhoopsServiceProvider; $app->register(new ConfigServiceProvider(), [ @@ -57,8 +57,9 @@ $app ->register(new LocaleServiceProvider()) ->register(new TranslationServiceProvider()) ->register(new ValidatorServiceProvider()) - ->register(new CsrfServiceProvider()) - ->register(new MonologServiceProvider(), [ + ->register(new CsrfServiceProvider()); + +$app->register(new MonologServiceProvider(), [ //'monolog.logfile' => sprintf('%s/%s.log', $app->getLogDirectory(), $app['env']), 'monolog.name' => 'Resume.PHP', 'monolog.logfile' => 'php://stderr', @@ -72,25 +73,25 @@ $app 'console.project_directory' => $app->getAppDirectory(), ]); $app['swiftmailer.use_spool'] = false; - if(false === getenv('SPARKPOST_API_KEY') && null !== $app->config('app.smtp_host')) { - $app['swiftmailer.options'] = [ + if (false === getenv('SPARKPOST_API_KEY') && null !== $app->config('app.smtp_host')) { + $app['swiftmailer.options'] = [ 'host' => $app->config('app.smtp_host'), 'port' => $app->config('app.smtp_post'), 'username' => $app->config('app.smtp_user'), 'password' => $app->config('app.smtp_password'), ]; - $app->log('Setting up local email.'); + $app->log('Setting up local email.'); } elseif (false !== getenv('SPARKPOST_API_KEY')) { - $app['swiftmailer.options'] = [ + $app['swiftmailer.options'] = [ 'host' => getenv('SPARKPOST_SMTP_HOST'), 'port' => getenv('SPARKPOST_SMTP_PORT'), 'username' => getenv('SPARKPOST_SMTP_USERNAME'), 'password' => getenv('SPARKPOST_SMTP_PASSWORD'), 'encryption' => 'tls', ]; - $app->log('Setting up sparkpost email.'); + $app->log('Setting up sparkpost email.'); } else { - $app['swiftmailer.transport'] = new Swift_SendmailTransport(); + $app['swiftmailer.transport'] = new Swift_SendmailTransport(); } $app->register(new RoutingServiceProvider()) diff --git a/app/themes/default/base.html.twig b/app/themes/default/base.html.twig index 9d6cc17..a3f99c4 100644 --- a/app/themes/default/base.html.twig +++ b/app/themes/default/base.html.twig @@ -31,12 +31,14 @@ {% block body %} {% endblock %} +{% if renderPdf == false %} +{% endif %} {% block javascripts_foot %}{% endblock %} {% block inline_js_foot %}{% endblock %} diff --git a/app/themes/default/cover.html.twig b/app/themes/default/cover.html.twig new file mode 100644 index 0000000..7131abd --- /dev/null +++ b/app/themes/default/cover.html.twig @@ -0,0 +1,66 @@ +{% extends 'base.html.twig' %} + +{% block body %} +
+

{{ basics.name|title }}

+

{{ basics.label }}

+ + + {% if basics.summary is defined and basics.summary is not empty %} +

{{ basics.summary|raw }}

+ {% endif %} + + +

Contact

+
+
+

+
+
+

+
+
+ +
+
+

Email

+
+
+

{{ basics.email }}

+
+
+ +
+
+

Phone

+
+
+

{{ basics.phone }}

+
+
+ +
+
+

Web

+
+
+

+ + + + Me + + {{ basics.website }} + +
+ {% for profile in basics.profiles %} + {{ render_profile(profile, true)|raw }}
+ {% endfor %} +

+
+
+
+ + +{% endblock %} + diff --git a/app/themes/default/index.html.twig b/app/themes/default/index.html.twig index 4cd6d17..82caa8b 100644 --- a/app/themes/default/index.html.twig +++ b/app/themes/default/index.html.twig @@ -5,34 +5,28 @@ {% endblock %} {% block body %} -
-
-

- {% if basics.name is not empty %} - {{ basics.name|title }} - {% else %} - {{ app.config.app.title|default('Resume') }} - {% endif %} - {% if basics.label is not empty %} - {{ basics.label }} - {% endif %} -

-
- {% if basics.summary is not empty %} -

{{ basics.summary|raw }}

- {% endif %} + {% if renderPdf == false %} +
-
+

+ {% if basics.name is not empty %} + {{ basics.name|title }} + {% else %} + {{ app.config.app.title|default('Resume') }} + {% endif %} + {% if basics.label is not empty %} + {{ basics.label }} + {% endif %} +

+
+ {% if basics.summary is not empty %} +

{{ basics.summary|raw }}

+ {% endif %} + +
+ + {% endif %}
@@ -41,69 +35,81 @@ {% include 'work.html.twig' %} {% include 'references.html.twig' %}
-
- - + {% endif %}
{% include 'contact.html.twig' %} diff --git a/app/themes/default/references.html.twig b/app/themes/default/references.html.twig index 3e81bd0..97ef1b9 100644 --- a/app/themes/default/references.html.twig +++ b/app/themes/default/references.html.twig @@ -7,6 +7,5 @@ {{ reference.name }} {% endfor %} - + {% endif %} - \ No newline at end of file diff --git a/app/themes/default/skills.html.twig b/app/themes/default/skills.html.twig index 2d26276..fbcc548 100644 --- a/app/themes/default/skills.html.twig +++ b/app/themes/default/skills.html.twig @@ -1,5 +1,18 @@ {% if skills is defined and skills is not empty %} - + {% if renderPdf == true %} +
+
{{ basics.phone }}
+
{{ basics.email }}
+
{{ basics.website }}
+
+
+
{{ basics.phone }}
+
{{ basics.email }}
+
{{ basics.website }}
+
+ {% endif %}

Skills

{% for skill in skills %} diff --git a/data/resume.json b/data/resume.json index f4077ca..6bf7772 100644 --- a/data/resume.json +++ b/data/resume.json @@ -45,7 +45,7 @@ "startDate": "2007-05-27", "summary": "I have done many different things during my employment at Stanford University.
A few of things things have been : Desktop Support, Computer cluster administration for the NSF and NNIN using Scyld Clusterware, General programing/debugging and frontend as well as backend web development using a number of different technologies.", "highlights": [ - "Repaired and Maintained a 64 node research computing cluster for the NNIN funded by the NSF.", + "Repaired and Maintained a 64 node research computing cluster for the NNIN funded by the NSF.", "Converted Electrical Engineering's static web site to Drupal 7.", "Created many Drupal 7 modules for the EE website including a custom events, lecture and committe minutes content type, a CDN module for uploading and recieving images/videos/documents, an Orglist module for displaying Faculty, Students, and Staff from a custom API.", "Created a custom Drupal 7 site for the SystemX organization with integrated login for affiliates as well as Stanford staff." @@ -156,14 +156,5 @@ ] } ], - "references": [ - { - "name": "Craig Stadler", - "reference": "It is my pleasure to recommend Richard, his performance working as a consultant for Main St. Company proved that he will be a valuable addition to any company." - }, - { - "name": "John Doe", - "reference": "Richard is awesome and cool." - } - ] + "references": [] } \ No newline at end of file diff --git a/src/Sikofitt/Command/CreatePdfCommand.php b/src/Sikofitt/Command/CreatePdfCommand.php index 72f9be3..d05ca8f 100644 --- a/src/Sikofitt/Command/CreatePdfCommand.php +++ b/src/Sikofitt/Command/CreatePdfCommand.php @@ -1,4 +1,14 @@ + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + /** * Created by PhpStorm. * User: eric @@ -8,13 +18,14 @@ namespace Sikofitt\Command; - use Knp\Command\Command; +use Knp\Snappy\Pdf; +use Sikofitt\Resume\ResumeParser; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style; -use Knp\Snappy\Pdf; - +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Process\Process; /** * Class CreatePdfCommand @@ -26,9 +37,9 @@ class CreatePdfCommand extends Command /** * */ - public function configure () + public function configure() { - $this->setName('resume:pdf:create') + $this->setName('resume:pdf:create') ->setDescription('Create a pdf of your website.') ->setProcessTitle('PDF Creation'); } @@ -37,20 +48,26 @@ class CreatePdfCommand extends Command * @param InputInterface $input * @param OutputInterface $output */ - public function interact (InputInterface $input, OutputInterface $output) + public function interact(InputInterface $input, OutputInterface $output) { - $index = $this->getSilexApplication()->renderView('index.html.twig', ['renderPdf' => true]); - $pdf = new Pdf($this->getSilexApplication()->getRootDirectory() . '/vendor/bin/wkhtmltopdf-amd64'); - $pdf->generateFromHtml($index, $this->getSilexApplication()->getRootDirectory() . '/resume.pdf', ['keep-relative-links' => false, 'resolve-relative-links' => false, 'load-error-handling' => false, 'disable-javascript' => true, 'disable-external-links' => true, 'disable-internal-links' => true]); + $process = new Process('hostname'); + $process->run(); + $hostname = $process->getOutput(); + $resume = new ResumeParser($this->getSilexApplication()); + + $request = Request::createFromGlobals(); + $pdf = new \Sikofitt\Resume\Pdf($this->getSilexApplication(), $request); + $pdf->setHost(sprintf('http://%s', $hostname)); + $data = $pdf->render(); + + file_put_contents($this->getSilexApplication()->getRootDirectory() . '/resume1.pdf', $data); } /** * @param InputInterface $input * @param OutputInterface $output */ - public function execute (InputInterface $input, OutputInterface $output) + public function execute(InputInterface $input, OutputInterface $output) { - } - -} \ No newline at end of file +} diff --git a/src/Sikofitt/Config/ConfigServiceProvider.php b/src/Sikofitt/Config/ConfigServiceProvider.php index 6947307..776d98c 100644 --- a/src/Sikofitt/Config/ConfigServiceProvider.php +++ b/src/Sikofitt/Config/ConfigServiceProvider.php @@ -30,27 +30,30 @@ use Symfony\Component\Validator\Constraints\{ * * @package Sikofitt\Config */ -class ConfigServiceProvider implements ServiceProviderInterface, BootableProviderInterface { +class ConfigServiceProvider implements ServiceProviderInterface, BootableProviderInterface +{ - /** + /** * @param Container $app */ - public function register(Container $app) { - $app['config'] = function ($app) { + public function register(Container $app) + { + $app['config'] = function ($app) { $config = Config::load($app['config.path']); return $config; }; } - public function boot(Application $app) { - $configItems = [ + public function boot(Application $app) + { + $configItems = [ 'email' => $app->config('app.email'), 'phone' => $app->config('app.phone'), ]; - $constraints = [ + $constraints = [ 'email' => [ new NotNull(['message' => 'Email value in app config is not present.']), new NotBlank(['message' => 'Email should cannot be blank in config.']), @@ -66,30 +69,29 @@ class ConfigServiceProvider implements ServiceProviderInterface, BootableProvide ], ]; - $captcha = $app->config('app.captcha'); - if (isset($captcha) && $captcha) { - $configItems['captcha_sitekey'] = $app->config('app.captcha_sitekey'); - $configItems['captcha_secret'] = $app->config('app.captcha_secret'); + $captcha = $app->config('app.captcha'); + if (isset($captcha) && $captcha) { + $configItems['captcha_sitekey'] = $app->config('app.captcha_sitekey'); + $configItems['captcha_secret'] = $app->config('app.captcha_secret'); - $constraints['captcha_sitekey'] = [ + $constraints['captcha_sitekey'] = [ new NotNull(['message' => 'ReCaptcha sitekey is a required value to use the captcha, this check can be disabled by removing or setting the captcha config item to false.']), new NotBlank(), new Length(['min' => 40]), ]; - $constraints['captcha_secret'] = [ + $constraints['captcha_secret'] = [ new NotNull(), new NotBlank(), new Length(['min' => 40]), ]; + } + $errors = $app['validator']->validate($configItems, new Collection($constraints)); + $validationErrors = []; + foreach ($errors->getIterator() as $error) { + $validationErrors[] = $error->getMessage(); + } + if (count($validationErrors) > 0) { + throw new MissingConfigurationItemException($validationErrors[0]); + } } - $errors = $app['validator']->validate($configItems, new Collection($constraints)); - $validationErrors = []; - foreach ($errors->getIterator() as $error) { - $validationErrors[] = $error->getMessage(); - } - if (count($validationErrors) > 0) { - throw new MissingConfigurationItemException($validationErrors[0]); - } - - } } diff --git a/src/Sikofitt/Controller/ApiControllerProvider.php b/src/Sikofitt/Controller/ApiControllerProvider.php index 9e27c34..7614250 100644 --- a/src/Sikofitt/Controller/ApiControllerProvider.php +++ b/src/Sikofitt/Controller/ApiControllerProvider.php @@ -24,7 +24,6 @@ namespace Sikofitt\Controller; - use ReCaptcha\ReCaptcha; use Sikofitt\Form\Type\ContactType; use Silex\Api\ControllerProviderInterface; @@ -44,19 +43,21 @@ use Symfony\Component\Validator\Constraints\Type; * * @package Sikofitt\Controller */ -class ApiControllerProvider implements ControllerProviderInterface { +class ApiControllerProvider implements ControllerProviderInterface +{ - /** + /** * {@inheritdoc} * * @param Application $app * * @return mixed */ - public function connect(Application $app) { - $controllers = $app['controllers_factory']; + public function connect(Application $app) + { + $controllers = $app['controllers_factory']; - $controllers->get('/v1/schema', function () use ($app) { + $controllers->get('/v1/schema', function () use ($app) { $response = new Response(file_get_contents($app->getDataDirectory() . '/schema/schema.v1.json'), Response::HTTP_OK); $response->headers->set('Content-Type', 'application/schema+json'); @@ -64,7 +65,7 @@ class ApiControllerProvider implements ControllerProviderInterface { return $response; }); - $controllers->match('/v1/message', function (Request $request) use ($app) { + $controllers->match('/v1/message', function (Request $request) use ($app) { static $code = 255; $returnData = [ @@ -121,7 +122,7 @@ class ApiControllerProvider implements ControllerProviderInterface { $valid = $app['validator']->validate($contactFormData, new Collection($constraints)); if (count($valid) > 0) { - $sanitizeProperty = function () use ($valid) { + $sanitizeProperty = function () use ($valid) { return str_replace(['][', '[', ']'], [ '_', '', @@ -129,59 +130,55 @@ class ApiControllerProvider implements ControllerProviderInterface { ], $valid[0]->getPropertyPath()); }; - return new JsonResponse([ + return new JsonResponse([ 'status' => 'error', 'message' => $valid[0]->getMessage(), 'id' => $sanitizeProperty(), 'const' => $valid[0]->getCode(), 'code' => 256, ], 256); - - } - else { - - $contactFormName = $contactFormData['contact']['name']; - $contactFormEmail = $contactFormData['contact']['email']; - $contactFormMessage = sprintf("%s\n\nEmail : %s <%s>", $contactFormData['contact']['message'], $contactFormName, $contactFormEmail); - $failures = ''; - $message = \Swift_Message::newInstance() + } else { + $contactFormName = $contactFormData['contact']['name']; + $contactFormEmail = $contactFormData['contact']['email']; + $contactFormMessage = sprintf("%s\n\nEmail : %s <%s>", $contactFormData['contact']['message'], $contactFormName, $contactFormEmail); + $failures = ''; + $message = \Swift_Message::newInstance() ->setSubject('[Resume] Message') ->setFrom([$app->config('app.from_email') => $contactFormName]) ->setTo([$app->config('app.email') => 'No-Reply']) ->setReplyTo([$contactFormEmail => $contactFormName]) ->setBody($contactFormMessage); - $sent = $app['mailer']->send($message, $failures); - if ($sent > 0) { - $request->getSession()->remove('_csrf/contact'); + $sent = $app['mailer']->send($message, $failures); + if ($sent > 0) { + $request->getSession()->remove('_csrf/contact'); - return new JsonResponse([ + return new JsonResponse([ 'status' => 'success', 'message' => 'Message successfully sent.', 'code' => 201, 'data' => $contactFormData, 'sent' => $sent, ], 201); - } - else { - return new JsonResponse([ + } else { + return new JsonResponse([ 'status' => 'error', 'message' => 'There was an error sending the message.', 'code' => 255, 'failed' => $failures, 'sent' => $sent, ], 255); - } + } } })->method('POST')->bind('api_message'); - $controllers->post('/v1/update', function(Request $request) use ($app) { + $controllers->post('/v1/update', function (Request $request) use ($app) { return new JsonResponse($app->decodeFile($app->getResumeSchema())); })->bind('api_update'); - $controllers->post('/v1/captcha', function (Request $request) use ($app) { + $controllers->post('/v1/captcha', function (Request $request) use ($app) { $captcha = new ReCaptcha('6LcvmSQTAAAAAITkvYJjgLar1LqGGLz-ic0ZMiXo'); $valid = $captcha->verify( @@ -189,31 +186,30 @@ class ApiControllerProvider implements ControllerProviderInterface { $request->server->get('REMOTE_ADDR') ); if ($valid->isSuccess()) { - $return = [ + $return = [ 'valid' => true, 'message' => [ 'email' => null !== $app->config('app.email') ? $app->config('app.email') : 'No email has been set in the configuration. Please let the owner know.', 'phone' => null !== $app->config('app.phone') ? $app->config('app.phone') : 'No phone has been set in the configuration. Please let the owner know.', ], ]; - } - else { - $errorCodes = [ + } else { + $errorCodes = [ 'missing-input-secret' => 'The secret parameter is missing.', 'invalid-input-secret' => 'The secret parameter is invalid or malformed.', 'missing-input-response' => 'The response parameter is missing.', 'invalid-input-response' => 'The response parameter is invalid or malformed.', ]; - foreach ($valid->getErrorCodes() as $code) { - if (array_key_exists($code, $errorCodes)) { - $errors[] = $errorCodes[$code]; + foreach ($valid->getErrorCodes() as $code) { + if (array_key_exists($code, $errorCodes)) { + $errors[] = $errorCodes[$code]; + } } - } - if (!isset($errors)) { - $errors[] = 'An unknown error occurred.'; - } - $return = [ + if (!isset($errors)) { + $errors[] = 'An unknown error occurred.'; + } + $return = [ 'valid' => false, 'message' => $errors, ]; @@ -222,6 +218,6 @@ class ApiControllerProvider implements ControllerProviderInterface { return new JsonResponse(json_encode($return)); })->bind('api_captcha'); - return $controllers; + return $controllers; } } diff --git a/src/Sikofitt/Controller/ResumeControllerProvider.php b/src/Sikofitt/Controller/ResumeControllerProvider.php index f6f68a4..5fc57ae 100644 --- a/src/Sikofitt/Controller/ResumeControllerProvider.php +++ b/src/Sikofitt/Controller/ResumeControllerProvider.php @@ -11,186 +11,92 @@ namespace Sikofitt\Controller; -use ReCaptcha\ReCaptcha; -use Sikofitt\Form\Type\ContactType; -use Silex\Api\ControllerProviderInterface; -use Silex\Application; -use Symfony\Component\Form\Form; -use Symfony\Component\Form\FormFactory; -use Symfony\Component\HttpFoundation\JsonResponse; -use Symfony\Component\HttpFoundation\Request; -use Knp\Snappy\Pdf; -use Symfony\Component\HttpFoundation\Response; +use Sikofitt\{ + Resume\Pdf, + Form\Type\ContactType, + Resume\ResumeParser +}; +use Silex\{ + Api\ControllerProviderInterface, + Application, + ControllerCollection +}; +use Symfony\Component\HttpFoundation\ { + Request, + Response, + ResponseHeaderBag, + StreamedResponse +}; /** * Class ResumeControllerProvider + * * @package Sikofitt\Controller */ class ResumeControllerProvider implements ControllerProviderInterface { /** - * @var object - */ - private $resumeData; + * @var ResumeParser + */ + private $resume; - /** - * @param Application $app - * @return mixed - */ - public function connect(Application $app) - { - $this->resumeData = $app->decodeFile( - $app->getDataDirectory() . '/resume.json', - $app->getDataDirectory() . '/schema/schema.v1.json' - ); + /** + * @param Application $app + * + * @return ControllerCollection + */ + public function connect(Application $app) + { + $this->resume = new ResumeParser($app); - $contactForm = $app['form.factory']->create(ContactType::class); - $controllers = $app['controllers_factory']; - $controllers->get('/pdf', function (Request $request) use ($app) { - $pdf = new Pdf($app->getRootDirectory() . '/vendor/bin/wkhtmltopdf-amd64'); - $content = $app->render('index.html.twig', [ - 'renderPdf' => true, - 'baseUrl' => $request->getSchemeAndHttpHost(), - 'fullData' => $this->resumeData, - 'basics' => $this->getResumeBasics(), - 'work' => $this->getResumeWork(), - 'volunteer' => $this->getResumeVolunteer(), - 'education' => $this->getResumeEducation(), - 'awards' => $this->getResumeAwards(), - 'publications' => $this->getResumePublications(), - 'skills' => $this->getResumeSkills(), - 'languages' => $this->getResumeLanguages(), - 'interests' => $this->getResumeInterests(), - 'references' => $this->getResumeReferences(), - ]); + $controllers = $app['controllers_factory']; - $renderedPdf = $pdf->getOutputFromHtml($content->getContent()); + $controllers->get('/', function (Request $request) use ($app) { - $response = new Response(); - $response->setContent($renderedPdf); - $response->setStatusCode(200); - $response->headers->set('Content-Type', 'application/pdf'); - $response->headers->set('Content-Disposition', 'attachment; filename="resume.pdf'); - return $response; - }); - $controllers->get('/', function (Request $request) use ($app, $contactForm) { - + $contactForm = $app['form.factory']->create(ContactType::class); - return $app['twig']->render('index.html.twig', [ - 'renderPdf' => false, - 'baseUrl' => $request->getSchemeAndHttpHost(), - 'fullData' => $this->resumeData, - 'basics' => $this->getResumeBasics(), - 'work' => $this->getResumeWork(), - 'volunteer' => $this->getResumeVolunteer(), - 'education' => $this->getResumeEducation(), - 'awards' => $this->getResumeAwards(), - 'publications' => $this->getResumePublications(), - 'skills' => $this->getResumeSkills(), - 'languages' => $this->getResumeLanguages(), - 'interests' => $this->getResumeInterests(), - 'references' => $this->getResumeReferences(), - 'contact_form' => $contactForm->createView(), - ]); - }); + return $app['twig']->render('index.html.twig', [ + 'renderPdf' => false, + 'baseUrl' => $request->getSchemeAndHttpHost(), + 'basics' => $this->resume->getBasics(), + 'work' => $this->resume->getWork(), + 'volunteer' => $this->resume->getVolunteer(), + 'education' => $this->resume->getEducation(), + 'awards' => $this->resume->getAwards(), + 'publications' => $this->resume->getPublications(), + 'skills' => $this->resume->getSkills(), + 'languages' => $this->resume->getLanguages(), + 'interests' => $this->resume->getInterests(), + 'references' => $this->resume->getReferences(), + 'contact_form' => $contactForm->createView(), + ]); + }); - $controllers->post('/', function(Request $request) use ($app) { - $contactFormData = $request->request->all(); - $contactFormName = $contactFormData['contact']['name']; - $contactFormEmail = $contactFormData['contact']['email']; - $contactFormMessage = $contactFormData['contact']['message']; - $sent = $app['mailer']->send(\Swift_Message::newInstance() - ->setSubject('[Resume] Message') - ->setFrom([$contactFormEmail => $contactFormName]) - ->setTo($app->config('app.email')) - ->setBody($contactFormMessage) - , $failures); - dump($failures); - dump($sent); - return new JsonResponse(['failures' => $failures, 'sent' => (bool)$sent]); - }); + $controllers->get('/pdf', function (Request $request) use ($app) { - return $controllers; - } + $pdf = new Pdf($app, $request); - /** - * @return null - */ - public function getResumeBasics() - { - return (isset($this->resumeData->basics) && count($this->resumeData->basics) > 0) ? $this->resumeData->basics : null; - } + $renderedPdf = $pdf->render(); - /** - * @return null - */ - public function getResumeWork() - { - return (isset($this->resumeData->work) && count($this->resumeData->work) > 0) ? $this->resumeData->work : null; - } + $filename = str_replace('@', '-AT-', $this->resume->getBasics()->email); - /** - * @return null - */ - public function getResumeVolunteer() - { - return (isset($this->resumeData->volunteer) && count($this->resumeData->volunteer) > 0) ? $this->resumeData->volunteer : null; - } + $response = new StreamedResponse(); + $response->setCallback(function () use ($renderedPdf) { + print $renderedPdf; + }); - /** - * @return null - */ - public function getResumeEducation() - { - return (isset($this->resumeData->education) && count($this->resumeData->education) > 0) ? $this->resumeData->education : null; - } + $response->setStatusCode(Response::HTTP_OK); + $disposition = $response->headers->makeDisposition( + ResponseHeaderBag::DISPOSITION_ATTACHMENT, + $filename . '.pdf' + ); + $response->headers->set('Content-Disposition', $disposition); + $response->send(); + }); - /** - * @return null - */ - public function getResumeAwards() - { - return (isset($this->resumeData->awards) && count($this->resumeData->awards) > 0) ? $this->resumeData->awards : null; - } - /** - * @return null - */ - public function getResumePublications() - { - return (isset($this->resumeData->publications) && count($this->resumeData->publications) > 0) ? $this->resumeData->publications : null; - } - /** - * @return null - */ - public function getResumeSkills() - { - return (isset($this->resumeData->skills) && count($this->resumeData->skills) > 0) ? $this->resumeData->skills : null; - } - - /** - * @return null - */ - public function getResumeLanguages() - { - return (isset($this->resumeData->languages) && count($this->resumeData->languages) > 0) ? $this->resumeData->languages : null; - } - - /** - * @return null - */ - public function getResumeInterests() - { - return (isset($this->resumeData->interests) && count($this->resumeData->interests) > 0) ? $this->resumeData->interests : null; - } - - /** - * @return null - */ - public function getResumeReferences() - { - return (isset($this->resumeData->references) && count($this->resumeData->references) > 0) ? $this->resumeData->references : null; - } + return $controllers; + } } diff --git a/src/Sikofitt/Form/Type/ContactType.php b/src/Sikofitt/Form/Type/ContactType.php index ca891e1..164c1d3 100644 --- a/src/Sikofitt/Form/Type/ContactType.php +++ b/src/Sikofitt/Form/Type/ContactType.php @@ -32,13 +32,12 @@ use Symfony\Component\Validator\Constraints\NotBlank; class ContactType extends AbstractType { - /** + /** * @param FormBuilderInterface $builder * @param array $options */ public function buildForm(FormBuilderInterface $builder, array $options) { - $builder ->setAction('/api/v1/message') ->add('name', TextType::class, [ diff --git a/src/Sikofitt/Resume/Pdf.php b/src/Sikofitt/Resume/Pdf.php new file mode 100644 index 0000000..fbfda76 --- /dev/null +++ b/src/Sikofitt/Resume/Pdf.php @@ -0,0 +1,147 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Created by PhpStorm. + * User: eric + * Date: 7/17/16 + * Time: 3:26 PM + */ + +namespace Sikofitt\Resume; + +use App; +use Knp\Snappy\Pdf as Snappy; +use Symfony\Component\HttpFoundation\Request; + +class Pdf +{ + /** + * @var string + */ + private $host; + + /** + * @var Snappy + */ + private $snappy; + + /** + * @var App + */ + private $app; + + /** + * @var Request + */ + private $request; + + /** + * @var ResumeParser + */ + private $resume; + + /** + * Pdf constructor. + * + * @param App $app + * @param Request $request + */ + public function __construct(App $app, Request $request) + { + $this->app = $app; + $this->request = $request; + $this->resume = new ResumeParser($this->app); + $this->snappy = new Snappy($app->getRootDirectory() . '/vendor/bin/wkhtmltopdf-amd64'); + } + + /** + * @return string + */ + public function render() + { + $content = $this->getIndex(); + $cover = $this->getCover(); + $title = $this->getTitle(); + + return $this->snappy->getOutputFromHtml( + $content, [ + 'lowquality' => false, + 'collate' => true, + 'margin-right' => 1, + 'margin-left' => 1, + 'title' => $title, + 'header-font-size' => 12, + 'cover' => $cover, + 'no-stop-slow-scripts' => true, + 'stop-slow-scripts' => false, + 'enable-javascript' => true, + 'no-outline' => true, + 'outline' => false, + 'dump-outline' => $this->app->getLogDirectory() . '/pdf-outline-' . md5(time()), + ] + ); + } + + /** + * @return string + */ + public function getIndex() + { + $host = $this->getHost() ?: $this->request->getSchemeAndHttpHost(); + + return $this->app->renderView('index.html.twig', [ + 'renderPdf' => true, + 'baseUrl' => $host, + 'basics' => $this->resume->getBasics(), + 'work' => $this->resume->getWork(), + 'volunteer' => $this->resume->getVolunteer(), + 'education' => $this->resume->getEducation(), + 'awards' => $this->resume->getAwards(), + 'publications' => $this->resume->getPublications(), + 'skills' => $this->resume->getSkills(), + 'languages' => $this->resume->getLanguages(), + 'interests' => $this->resume->getInterests(), + 'references' => $this->resume->getReferences(), + ]); + } + public function setHost($host) + { + $this->host = $host; + } + public function getHost() + { + return $this->host; + } + /** + * @return string + */ + public function getCover() + { + $host = $this->getHost() ?: $this->request->getSchemeAndHttpHost(); + return $this->app->renderView('cover.html.twig', [ + 'baseUrl' => $host, + 'renderPdf' => true, + 'basics' => $this->resume->getBasics(), + ]); + } + + /** + * @return string + */ + public function getTitle() + { + $date = new \DateTimeImmutable('now'); + $date = $date->format('c'); + $title = $this->app->config('app.title') ?: 'Resume'; + return sprintf('%s (%s)', $title, $date); + } +} diff --git a/src/Sikofitt/Resume/ResumeParser.php b/src/Sikofitt/Resume/ResumeParser.php index c13ea7c..55800b0 100644 --- a/src/Sikofitt/Resume/ResumeParser.php +++ b/src/Sikofitt/Resume/ResumeParser.php @@ -9,42 +9,283 @@ * file that was distributed with this source code. */ -/** - * Created by PhpStorm. - * User: eric - * Date: 7/2/16 - * Time: 11:22 AM - */ - namespace Sikofitt\Resume; -use Webmozart\Json\Conversion\JsonConverter; +use App; /** * Class ResumeParser + * * @package Sikofitt\Resume */ -class ResumeParser implements JsonConverter +class ResumeParser extends \ArrayObject implements \JsonSerializable, \ArrayAccess { /** - * @param mixed $jsonData - * @param array $options - * - * @return void + * @var */ - public function fromJson($jsonData, array $options = []) + private $input; + /** + * @var + */ + private $basics; + /** + * @var + */ + private $work; + /** + * @var + */ + private $volunteer; + /** + * @var + */ + private $education; + /** + * @var + */ + private $awards; + /** + * @var + */ + private $publications; + /** + * @var + */ + private $skills; + /** + * @var + */ + private $languages; + /** + * @var + */ + private $interests; + /** + * @var + */ + private $references; + + /** + * ResumeParser constructor. + * + * @param App $app + * @param int $flags + * @param string $iterator_class + */ + public function __construct(App $app, $flags = 0, $iterator_class = 'ArrayIterator') { - // TODO: Implement fromJson() method. + $this->app = $app; + $this->input = $this->app->decodeFile($app->getResumeJson(), $app->getResumeSchema()); + + parent::__construct($this->input, $flags, $iterator_class); + $this + ->setBasics($this->offsetGet('basics')) + ->setWork($this->offsetGet('work')) + ->setVolunteer($this->offsetGet('volunteer')) + ->setEducation($this->offsetGet('education')) + ->setAwards($this->offsetGet('awards')) + ->setPublications($this->offsetGet('publications')) + ->setSkills($this->offsetGet('skills')) + ->setLanguages($this->offsetGet('languages')) + ->setInterests($this->offsetGet('interests')) + ->setReferences($this->offsetGet('references')); } /** - * @param mixed $data - * @param array $options - * - * @return void + * @return mixed */ - public function toJson($data, array $options = []) + public function jsonSerialize() { - // TODO: Implement toJson() method. + return $this->app->encode($this->input, $this->app->getResumeSchema()); + } + + /** + * @return mixed + */ + public function getBasics() + { + return $this->basics; + } + + /** + * @param mixed $basics + * + * @return ResumeParser + */ + public function setBasics($basics) + { + $this->basics = $basics; + return $this; + } + + /** + * @return mixed + */ + public function getWork() + { + return $this->work; + } + + /** + * @param mixed $work + * + * @return ResumeParser + */ + public function setWork($work) + { + $this->work = $work; + return $this; + } + + /** + * @return mixed + */ + public function getVolunteer() + { + return $this->volunteer; + } + + /** + * @param mixed $volunteer + * + * @return ResumeParser + */ + public function setVolunteer($volunteer) + { + $this->volunteer = $volunteer; + return $this; + } + + /** + * @return mixed + */ + public function getEducation() + { + return $this->education; + } + + /** + * @param mixed $education + * + * @return ResumeParser + */ + public function setEducation($education) + { + $this->education = $education; + return $this; + } + + /** + * @return mixed + */ + public function getAwards() + { + return $this->awards; + } + + /** + * @param mixed $awards + * + * @return ResumeParser + */ + public function setAwards($awards) + { + $this->awards = $awards; + return $this; + } + + /** + * @return mixed + */ + public function getPublications() + { + return $this->publications; + } + + /** + * @param mixed $publications + * + * @return ResumeParser + */ + public function setPublications($publications) + { + $this->publications = $publications; + return $this; + } + + /** + * @return mixed + */ + public function getSkills() + { + return $this->skills; + } + + /** + * @param mixed $skills + * + * @return ResumeParser + */ + public function setSkills($skills) + { + $this->skills = $skills; + return $this; + } + + /** + * @return mixed + */ + public function getLanguages() + { + return $this->languages; + } + + /** + * @param mixed $languages + * + * @return ResumeParser + */ + public function setLanguages($languages) + { + $this->languages = $languages; + return $this; + } + + /** + * @return mixed + */ + public function getInterests() + { + return $this->interests; + } + + /** + * @param mixed $interests + * + * @return ResumeParser + */ + public function setInterests($interests) + { + $this->interests = $interests; + return $this; + } + + /** + * @return mixed + */ + public function getReferences() + { + return $this->references; + } + + /** + * @param mixed $references + * + * @return ResumeParser + */ + public function setReferences($references) + { + $this->references = $references; + return $this; } } diff --git a/src/Sikofitt/Twig/RenderProfile.php b/src/Sikofitt/Twig/RenderProfile.php index 4b808ee..7fc796d 100644 --- a/src/Sikofitt/Twig/RenderProfile.php +++ b/src/Sikofitt/Twig/RenderProfile.php @@ -39,7 +39,7 @@ class RenderProfile extends \Twig_Extension ]; } - public function renderProfile($context, $iconData) + public function renderProfile($context, $iconData, $withText = false) { $imageData = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAIAAABLixI0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4AcHEQYsjAFXqQAAAEVpVFh0Q29tbWVudAAAAAAAQ1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcgSlBFRyB2ODApLCBxdWFsaXR5ID0gOTAKqozFDgAAAiVJREFUOMutlT/oOWEcx+8OX5fLDVwx2ZQFSQYzmZgMzCaDzaCUMpmQkowM2G+gFFnUGXRKSQwGIYtyisif+w2n+973ucef4fde7u75fD6v5/l8Ps/zHMLDxHFcp9OJxWJOp1Ov1ysUChzHLRZLNBrt9Xrn8xkahciH6vW6VqtFXouiqOFw+JmVz+eR79Rqtd6xSqUS8rUwDGNZFs7a7/c4jku9lUplOByuVqvtdpum6Xg8jqKo1MHn88FZHo8HmPnn50co8+PxeLVwjuNA1mq1giaSTCaDwaDX653P58+Av6pUKiJLKQxNJhMUReWumUxGeGFZ1mw2yydbLpe/FRQeh8NBDhJFkmQgEEAQJJ1OAyaDwfD7ISyv2+0CdZV2YL1e8zw/Go3UajXQSkjtj8fjK1YkEnmWQ6kETEajUcp65qjRaAqFApRls9kQBBkMBrfbDTBtt9tGowHmKMhqtUJzJAhCpVJBZ9LpdKfTCbLvGYaRe2ez2d1uV6vV5CahLAzDwM9jIpEAAjabDXRnCSqXy5fLBc46HA4kSUq9c7kcz/PQdQElAlnX69XhcHx5vP1+P6SP0i0DnPD3rD+xgPnxeJhMpm9AKIra7faXCYtaLBYURUHjRWEYJnbw5R0t6HK5hEIhKYggiGazOZ1O+/1+u90ej8ef73up3G63yEqlUu+dP7Du97vL5RL+F/wnIR89ZrMZhmHFYvE/sHiep2lavFff6B8xFGrMmf/uPQAAAABJRU5ErkJggg=='; //network": "Twitter" +"username": "sikofitt" +"url": "" @@ -57,7 +57,11 @@ class RenderProfile extends \Twig_Extension } $imageUrl = sprintf('%s', $imageData['icon'], $iconData->network); if (isset($iconData->url) && !empty($iconData->url)) { - return sprintf('%s', $iconData->url, $iconData->network, $imageUrl); + if ($withText) { + return sprintf('%s %s', $iconData->url, $iconData->network, $imageUrl, $iconData->url); + } else { + return sprintf('%s', $iconData->url, $iconData->network, $imageUrl); + } } else { return $imageUrl; } diff --git a/src/Sikofitt/js/resume.js b/src/Sikofitt/js/resume.js index 8476c7b..cca9fe9 100644 --- a/src/Sikofitt/js/resume.js +++ b/src/Sikofitt/js/resume.js @@ -77,4 +77,6 @@ jq(document).ready(function (jq) { jq(this).removeClass('uk-form-danger'); } }); + + jq('a[rel=ext]').attr('target', '_blank'); }); \ No newline at end of file diff --git a/src/Sikofitt/less/resume.less b/src/Sikofitt/less/resume.less index 1fead3e..8dcd706 100644 --- a/src/Sikofitt/less/resume.less +++ b/src/Sikofitt/less/resume.less @@ -28,6 +28,10 @@ a.profile-link { &:hover { filter: grayscale(0%); } + > * img { + width:25px; + padding:0px; + } } .uk-panel-image { @@ -41,6 +45,7 @@ a.profile-link { } + .uk-text-lead { font-size: 24px; line-height: 38px; @@ -62,4 +67,13 @@ a.profile-link { line-height: 40px; font-size: 34px; border-radius: 2px; +} + +.uk-sticky-active-sidebar { + margin-top: 35px; +} + +div#sidebar.uk-active { + margin-top: 35px; + transition: margin-top 2s; } \ No newline at end of file diff --git a/web/.htaccess b/web/.htaccess new file mode 100644 index 0000000..d1b713a --- /dev/null +++ b/web/.htaccess @@ -0,0 +1,9 @@ + + Options -MultiViews + + RewriteEngine On + #RewriteBase /path/to/app + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [QSA,L] + \ No newline at end of file diff --git a/web/index.php b/web/index.php index 5e66ef7..615775a 100644 --- a/web/index.php +++ b/web/index.php @@ -9,7 +9,6 @@ * file that was distributed with this source code. */ - require_once __DIR__ . '/../vendor/autoload.php'; diff --git a/web/robots.txt b/web/robots.txt new file mode 100644 index 0000000..4665fca --- /dev/null +++ b/web/robots.txt @@ -0,0 +1,5 @@ +# www.robotstxt.org/ +# www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449 + +User-agent: * +Disallow: