Finished auto pdf generation

This commit is contained in:
R. Eric Wheeler 2016-07-17 17:02:58 -07:00
parent 4a3223307c
commit d2659172f7
21 changed files with 912 additions and 484 deletions

View File

@ -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,6 +32,15 @@ class App extends Application
use Application\UrlGeneratorTrait;
private $debug;
/**
* @return string
*/
public function getConfDirectory()
{
return $this->getAppDirectory() . '/config';
}
/**
* Returns the application directory.
*
@ -45,6 +53,22 @@ class App extends Application
return dirname($r->getFileName());
}
/**
* @return string
*/
public function getResumeJson()
{
return $this->getDataDirectory() . '/resume.json';
}
/**
* @return string
*/
public function getDataDirectory()
{
return $this->getRootDirectory() . '/data';
}
/**
* Returns the root directory of the application.
*
@ -56,30 +80,6 @@ class App extends Application
return dirname($this->getAppDirectory());
}
/**
* @return string
*/
public function getConfDirectory()
{
return $this->getAppDirectory() . '/config';
}
/**
* @return string
*/
public function getDataDirectory()
{
return $this->getRootDirectory() . '/data';
}
/**
* @return string
*/
public function getResumeJson()
{
return $this->getDataDirectory() . '/resume.json';
}
/**
* @return string
*/
@ -93,15 +93,11 @@ class App extends Application
return $this->getDataDirectory() . '/logs';
}
/**
* Registers media icons
*
* @param \Sikofitt\Image\Profile\ProfileIconInterface $icon
*/
public function registerIcon(\Sikofitt\Image\Profile\ProfileIconInterface $icon)
public function getDebug()
{
$this->config(sprintf('app.icons.%s', $icon->getName()), ['icon' => $icon->getIcon(), 'url' => $icon->getDefaultUrl()]);
return $this['debug'];
}
public function setDebug()
{
$this['debug'] = (null !== $this->config('app.debug') ? $this->config('app.debug') : true);
@ -111,10 +107,14 @@ class App extends Application
}
}
public function getDebug()
public function boot()
{
return $this['debug'];
$this->registerExtenders();
// register default icons
$this->registerDefaultIcons();
return parent::boot();
}
public function registerExtenders()
{
if (!$this['debug']) {
@ -137,7 +137,7 @@ class App extends Application
'requestedMethod' => $request->getMethod(),
'code' => $code,
];
if($request->isXmlHttpRequest()) {
if ($request->isXmlHttpRequest()) {
$message = json_encode($message);
} else {
$message = $this->renderView('error.405.html.twig', $message);
@ -145,12 +145,18 @@ class App extends Application
$this->log($e->getMessage(), ['code' => $code], \Symfony\Bridge\Monolog\Logger::WARNING);
break;
case 500:
$message = json_encode(['status' => 'error', 'message' => 'Critical Error', 'code' => $code]);
$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()) {
if ($request->isXmlHttpRequest()) {
$message = json_decode($message);
} else {
$message = $this->renderView('error.html.twig', $message);
@ -174,13 +180,6 @@ class App extends Application
return $twig;
});
}
public function boot()
{
$this->registerExtenders();
// register default icons
$this->registerDefaultIcons();
return parent::boot();
}
public function registerDefaultIcons()
{
@ -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()]);
}
}

View File

@ -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,7 +73,7 @@ $app
'console.project_directory' => $app->getAppDirectory(),
]);
$app['swiftmailer.use_spool'] = false;
if(false === getenv('SPARKPOST_API_KEY') && null !== $app->config('app.smtp_host')) {
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'),

View File

@ -31,12 +31,14 @@
{% block body %}
{% endblock %}
</div>
{% if renderPdf == false %}
<footer id="footer" style="
margin-top: 100px;
height: 150px;
background-color: #000;
">
</footer>
{% endif %}
<script src="{{ baseUrl ~ asset('js/resume.min.js') }}"></script>
{% block javascripts_foot %}{% endblock %}
{% block inline_js_foot %}{% endblock %}

View File

@ -0,0 +1,66 @@
{% extends 'base.html.twig' %}
{% block body %}
<div class="uk-container uk-container-center">
<h1 class="uk-text-center uk-heading-large">{{ basics.name|title }}</h1>
<h2 class="uk-text-center">{{ basics.label }}</h2>
{% if basics.summary is defined and basics.summary is not empty %}
<p class="uk-text-lead uk-text-center">{{ basics.summary|raw }}</p>
{% endif %}
<h2 class="uk-text-center">Contact</h2>
<div class="uk-grid uk-grid-match" data-uk-grid-margin="">
<div class="uk-width-1-2 uk-row-first">
<h3 class="uk-text-center"></h3>
</div>
<div class="uk-width-1-2">
<p class="uk-text-left uk-align-left uk-text-top"></p>
</div>
</div> <!-- /uk-grid-match -->
<div class="uk-grid uk-grid-match" data-uk-grid-margin="">
<div class="uk-width-1-2 uk-row-first">
<h3 class="uk-text-center">Email</h3>
</div>
<div class="uk-width-1-2">
<p class="uk-text-left uk-align-left uk-text-top">{{ basics.email }}</p>
</div>
</div> <!-- /uk-grid-match -->
<div class="uk-grid uk-grid-match" data-uk-grid-margin="">
<div class="uk-width-1-2 uk-row-first">
<h3 class="uk-text-center">Phone</h3>
</div>
<div class="uk-width-1-2">
<p class="uk-text-left">{{ basics.phone }}</p>
</div>
</div><!-- /uk-grid-match -->
<div class="uk-grid uk-grid-match" data-uk-grid-margin="">
<div class="uk-width-1-2 uk-row-first">
<h3 class="uk-text-center">Web</h3>
</div>
<div class="uk-width-1-2">
<p class="uk-text-left">
<span class="uk-display-inline-block uk-margin-small-top uk-margin-small-bottom">
<a class="profile-link" href="{{ basics.website }}" title="Home page">
<span class="uk-margin-small-right">
<img class="uk-thumbnail uk-thumbnail-mini uk-border-circle" src="{{ basics.picture }}" alt="Me" />
</span>
{{ basics.website }}
</a>
</span><br />
{% for profile in basics.profiles %}
<span class="uk-display-inline-block uk-margin-small-top uk-margin-small-bottom">{{ render_profile(profile, true)|raw }}</span><br />
{% endfor %}
</p>
</div>
</div><!-- /uk-grid-match -->
</div>
</div><!-- /.uk-container.uk-container-center -->
{% endblock %}

View File

@ -5,18 +5,10 @@
{% endblock %}
{% block body %}
<style type="text/css">
.uk-sticky-active-sidebar {
margin-top: 35px;
}
div#sidebar.uk-active {
margin-top: 35px;
transition: margin-top 2s;
}
</style>
<div class="uk-grid" data-uk-grid-margin xmlns="http://www.w3.org/1999/html">
{% if renderPdf == false %}
<div class="uk-width-1-1">
<h1 class="uk-heading-large">
{% if basics.name is not empty %}
{{ basics.name|title }}
@ -33,6 +25,8 @@
{% endif %}
</div>
{% endif %}
</div>
<div class="uk-grid" data-uk-grid-margin>
@ -41,6 +35,7 @@
{% include 'work.html.twig' %}
{% include 'references.html.twig' %}
</div>
{% if renderPdf == false %}
<div class="uk-width-medium-1-4">
<div id="sidebar" class="uk-panel uk-panel-header uk-panel-box"
data-uk-sticky="{top:35, animation: 'uk-animation-slide-top', getWidthFrom:'#sidebar'}">
@ -54,13 +49,22 @@
<ul class="uk-list uk-list-line">
{% if app.config.app.phone is not empty %}
{% if renderPdf == true %}
<li class="uk-list-space"><a href="tel:{{ app.config.app.phone }}"
title="Telephone">{{ app.config.app.phone }}</a></li>
{% else %}
<li class="uk-list-space"><a href="#phone-modal" class="hidden-phone" data-uk-modal>Phone</a></li>
{% endif %}
{% endif %}
<li class="uk-list-space">
{% if basics.email is not empty %}
{% if renderPdf == true %}
<a href="mailto:{{ basics.email }}" title="Send email">{{ basics.email }}</a>
{% else %}
<a href="#contact-form-wrapper" data-uk-modal>{{ basics.email }}</a>
{% endif %}
{% endif %}
</li>
@ -69,6 +73,7 @@
<a href="{{ basics.website }}" target="_blank" title="Home page">{{ basics.website }}</a>
</li>
{% endif %}
<li><a href="{{ baseUrl ~ asset('pdf') }}" title="Download Resume in PDF format">Download PDF</a></li>
{% if basics.location|length > 0 and basics.location is not empty %}
<li class="uk-list-space">
<address>
@ -104,6 +109,7 @@
</div>
{% endif %}
</div>
{% include 'contact.html.twig' %}

View File

@ -7,6 +7,5 @@
<small>{{ reference.name }}</small>
</blockquote>
{% endfor %}
{% endif %}
</div>
{% endif %}

View File

@ -1,5 +1,18 @@
{% if skills is defined and skills is not empty %}
{% if renderPdf == true %}
<div style="position:relative; top:0; left:0;"
class="uk-grid uk-width-1-1 uk-grid-match uk-grid-width-1-3 uk-margin-large-top">
<div class="uk-text-left">{{ basics.phone }}</div>
<div class="uk-text-center">{{ basics.email }}</div>
<div class="uk-text-right">{{ basics.website }}</div>
</div>
<div style="position:absolute; bottom:0; left:0; border-top:1px solid #ddd; padding-top:6px;"
class="uk-container uk-container-center uk-grid uk-width-1-1 uk-grid-match uk-grid-width-1-3 uk-margin-large-top">
<div class="uk-text-left">{{ basics.phone }}</div>
<div class="uk-text-center">{{ basics.email }}</div>
<div class="uk-text-right">{{ basics.website }}</div>
</div>
{% endif %}
<h1 id="skills">Skills</h1>
<dl class="uk-description-list-line">
{% for skill in skills %}

View File

@ -45,7 +45,7 @@
"startDate": "2007-05-27",
"summary": "I have done many different things during my employment at Stanford University.<br />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 <a href=\\\"//nnin.org\\\" title=\\\"National Nanotechnology Infrastructure Network\\\">NNIN</a> funded by the <a href=\\\"//nsf.gov\\\" title=\\\"National Science Foundation\\\">NSF</a>.",
"Repaired and Maintained a 64 node research computing cluster for the <a href=\"http://nnin.org\" rel=\"ext\" title=\"National Nanotechnology Infrastructure Network\">NNIN</a> funded by the <a href=\"https://nsf.gov\" rel=\"ext\" title=\"National Science Foundation\">NSF</a>.",
"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": []
}

View File

@ -1,4 +1,14 @@
<?php
/*
* This file is part of Resume.PHP.
*
* (copyleft) R. Eric Wheeler <sikofitt@gmail.com>
*
* 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,7 +37,7 @@ class CreatePdfCommand extends Command
/**
*
*/
public function configure ()
public function configure()
{
$this->setName('resume:pdf:create')
->setDescription('Create a pdf of your website.')
@ -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)
{
}
}

View File

@ -30,12 +30,14 @@ 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) {
public function register(Container $app)
{
$app['config'] = function ($app) {
$config = Config::load($app['config.path']);
@ -43,7 +45,8 @@ class ConfigServiceProvider implements ServiceProviderInterface, BootableProvide
};
}
public function boot(Application $app) {
public function boot(Application $app)
{
$configItems = [
'email' => $app->config('app.email'),
'phone' => $app->config('app.phone'),
@ -90,6 +93,5 @@ class ConfigServiceProvider implements ServiceProviderInterface, BootableProvide
if (count($validationErrors) > 0) {
throw new MissingConfigurationItemException($validationErrors[0]);
}
}
}

View File

@ -24,7 +24,6 @@
namespace Sikofitt\Controller;
use ReCaptcha\ReCaptcha;
use Sikofitt\Form\Type\ContactType;
use Silex\Api\ControllerProviderInterface;
@ -44,7 +43,8 @@ use Symfony\Component\Validator\Constraints\Type;
*
* @package Sikofitt\Controller
*/
class ApiControllerProvider implements ControllerProviderInterface {
class ApiControllerProvider implements ControllerProviderInterface
{
/**
* {@inheritdoc}
@ -53,7 +53,8 @@ class ApiControllerProvider implements ControllerProviderInterface {
*
* @return mixed
*/
public function connect(Application $app) {
public function connect(Application $app)
{
$controllers = $app['controllers_factory'];
$controllers->get('/v1/schema', function () use ($app) {
@ -136,10 +137,7 @@ class ApiControllerProvider implements ControllerProviderInterface {
'const' => $valid[0]->getCode(),
'code' => 256,
], 256);
}
else {
} else {
$contactFormName = $contactFormData['contact']['name'];
$contactFormEmail = $contactFormData['contact']['email'];
$contactFormMessage = sprintf("%s\n\nEmail : %s <%s>", $contactFormData['contact']['message'], $contactFormName, $contactFormEmail);
@ -162,8 +160,7 @@ class ApiControllerProvider implements ControllerProviderInterface {
'data' => $contactFormData,
'sent' => $sent,
], 201);
}
else {
} else {
return new JsonResponse([
'status' => 'error',
'message' => 'There was an error sending the message.',
@ -176,7 +173,7 @@ class ApiControllerProvider implements ControllerProviderInterface {
})->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');
@ -196,8 +193,7 @@ class ApiControllerProvider implements ControllerProviderInterface {
'phone' => null !== $app->config('app.phone') ? $app->config('app.phone') : 'No phone has been set in the configuration. Please let the owner know.',
],
];
}
else {
} else {
$errorCodes = [
'missing-input-secret' => 'The secret parameter is missing.',
'invalid-input-secret' => 'The secret parameter is invalid or malformed.',

View File

@ -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
* @var ResumeParser
*/
private $resumeData;
private $resume;
/**
* @param Application $app
* @return mixed
*
* @return ControllerCollection
*/
public function connect(Application $app)
{
$this->resumeData = $app->decodeFile(
$app->getDataDirectory() . '/resume.json',
$app->getDataDirectory() . '/schema/schema.v1.json'
);
$this->resume = new ResumeParser($app);
$controllers = $app['controllers_factory'];
$controllers->get('/', function (Request $request) use ($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(),
]);
$renderedPdf = $pdf->getOutputFromHtml($content->getContent());
$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) {
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(),
'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) {
$pdf = new Pdf($app, $request);
$renderedPdf = $pdf->render();
$filename = str_replace('@', '-AT-', $this->resume->getBasics()->email);
$response = new StreamedResponse();
$response->setCallback(function () use ($renderedPdf) {
print $renderedPdf;
});
$response->setStatusCode(Response::HTTP_OK);
$disposition = $response->headers->makeDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
$filename . '.pdf'
);
$response->headers->set('Content-Disposition', $disposition);
$response->send();
});
return $controllers;
}
/**
* @return null
*/
public function getResumeBasics()
{
return (isset($this->resumeData->basics) && count($this->resumeData->basics) > 0) ? $this->resumeData->basics : null;
}
/**
* @return null
*/
public function getResumeWork()
{
return (isset($this->resumeData->work) && count($this->resumeData->work) > 0) ? $this->resumeData->work : null;
}
/**
* @return null
*/
public function getResumeVolunteer()
{
return (isset($this->resumeData->volunteer) && count($this->resumeData->volunteer) > 0) ? $this->resumeData->volunteer : null;
}
/**
* @return null
*/
public function getResumeEducation()
{
return (isset($this->resumeData->education) && count($this->resumeData->education) > 0) ? $this->resumeData->education : null;
}
/**
* @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;
}
}

View File

@ -38,7 +38,6 @@ class ContactType extends AbstractType
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->setAction('/api/v1/message')
->add('name', TextType::class, [

147
src/Sikofitt/Resume/Pdf.php Normal file
View File

@ -0,0 +1,147 @@
<?php
/*
* This file is part of Resume.PHP.
*
* (copyleft) R. Eric Wheeler <sikofitt@gmail.com>
*
* 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);
}
}

View File

@ -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;
}
}

View File

@ -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('<img src="%s" alt="%s" />', $imageData['icon'], $iconData->network);
if (isset($iconData->url) && !empty($iconData->url)) {
if ($withText) {
return sprintf('<a class="profile-link" href="%s" title="%s" target="_blank"><span class="uk-margin-small-right">%s</span> %s</a>', $iconData->url, $iconData->network, $imageUrl, $iconData->url);
} else {
return sprintf('<a class="profile-link" href="%s" title="%s" target="_blank">%s</a>', $iconData->url, $iconData->network, $imageUrl);
}
} else {
return $imageUrl;
}

View File

@ -77,4 +77,6 @@ jq(document).ready(function (jq) {
jq(this).removeClass('uk-form-danger');
}
});
jq('a[rel=ext]').attr('target', '_blank');
});

View File

@ -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;
@ -63,3 +68,12 @@ a.profile-link {
font-size: 34px;
border-radius: 2px;
}
.uk-sticky-active-sidebar {
margin-top: 35px;
}
div#sidebar.uk-active {
margin-top: 35px;
transition: margin-top 2s;
}

9
web/.htaccess Normal file
View File

@ -0,0 +1,9 @@
<IfModule mod_rewrite.c>
Options -MultiViews
RewriteEngine On
#RewriteBase /path/to/app
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
</IfModule>

View File

@ -9,7 +9,6 @@
* file that was distributed with this source code.
*/
require_once __DIR__ . '/../vendor/autoload.php';

5
web/robots.txt Normal file
View File

@ -0,0 +1,5 @@
# www.robotstxt.org/
# www.google.com/support/webmasters/bin/answer.py?hl=en&answer=156449
User-agent: *
Disallow: