- {{ travel_program.htmlDescription|raw }}
+ {{ travel_program.htmlDescription|raw|keywords }}
{% if travel_program.advices is not empty %}
Hinweise
diff --git a/trunk/app/Resources/views/default/pages/travelProgramOverview.html.twig b/trunk/app/Resources/views/default/pages/travelProgramOverview.html.twig
index c322e560..5bca7d16 100644
--- a/trunk/app/Resources/views/default/pages/travelProgramOverview.html.twig
+++ b/trunk/app/Resources/views/default/pages/travelProgramOverview.html.twig
@@ -1,4 +1,4 @@
-{% extends 'base.html.twig' %}
+{% extends get_base_template() %}
{% block body %}
- {{ page.content|raw }}
+ {{ page.content|keywords|raw }}
{% endblock %}
\ No newline at end of file
diff --git a/trunk/app/config/services.yml b/trunk/app/config/services.yml
index 25117a51..b32a4abd 100644
--- a/trunk/app/config/services.yml
+++ b/trunk/app/config/services.yml
@@ -11,8 +11,8 @@ services:
app.controller_listener:
class: AppBundle\Listener\KernelControllerListener
arguments:
- - "@doctrine.orm.entity_manager"
- - "@controller_resolver"
+ - '@doctrine.orm.entity_manager'
+ - '@controller_resolver'
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }
@@ -21,6 +21,8 @@ services:
#public: false
arguments:
- '@twig'
+ - '@app.keyword_service'
+ - '@request_stack'
tags:
- { name: twig.extension }
@@ -29,7 +31,7 @@ services:
arguments:
- '@monolog.logger'
- app.booking_request_validator:
- class: AppBundle\Validator\BookingRequestValidator
- tags:
- - {name: validator.constraint_validator }
\ No newline at end of file
+ app.keyword_service:
+ class: AppBundle\Service\KeywordService
+ arguments:
+ - '@doctrine.orm.entity_manager'
\ No newline at end of file
diff --git a/trunk/src/AppBundle/Resources/public/js/custom.js b/trunk/src/AppBundle/Resources/public/js/custom.js
index 75c7c055..432b15bf 100644
--- a/trunk/src/AppBundle/Resources/public/js/custom.js
+++ b/trunk/src/AppBundle/Resources/public/js/custom.js
@@ -1,6 +1,82 @@
-(function($) {
+jQuery(document).ready(function($) {
"use strict";
+ /* ==============================================
+ VIDEOS
+ =============================================== */
+
+ var videos$ = $('a[id^="video_"]');
+
+ function videoInitHandler()
+ {
+ var el$ = $(this);
+
+ var text = el$.text();
+ var length = text.length - 11;
+ var caption = text.substring(0, length);
+ var expl = this.id.substring(6, this.id.length);
+
+ $('
')
+ .attr({
+ width: 680,
+ height: 466,
+ src: '//www.youtube.com/embed/'+ expl,
+ frameborder: 0,
+ allowfullscreen: true,
+ 'data-st-video': this.id
+ })
+ .addClass('st-collapsed')
+ .hide()
+ .insertAfter(this)
+ ;
+ el$
+ .css('background-image', 'url(/images/st2/icons/arrowup.gif)')
+ .text(caption + ' einblenden')
+ .attr('href', 'javascript:void(0);')
+ ;
+ }
+
+ videos$.each(videoInitHandler);
+
+ videos$.click(function() {
+
+ var el$ = $(this);
+ var video$ = $('[data-st-video='+ this.id +']');
+ var text = el$.text();
+ var length = text.length - 11;
+ var caption = text.substring(0, length);
+
+ if (el$.hasClass('st-collapsed'))
+ {
+ video$.slideDown('slow');
+ el$.text(caption + ' ausblenden');
+ el$.removeClass('st-collapsed');
+ }
+ else
+ {
+ video$.slideUp(400);
+ el$.text(caption + ' einblenden');
+ el$.addClass('st-collapsed');
+ }
+ });
+
+ /* ==============================================
+ KEYWORDS
+ =============================================== */
+
+ var modal$ = $('#st-default-modal');
+
+ $('a.show-layer').click(function() {
+
+ $.get($(this).attr('href')).then(function(r) {
+
+ modal$.find('.modal-body').html(r);
+ modal$.find('a[id^="video_"]').each(videoInitHandler);
+ modal$.modal('show');
+ });
+ return false;
+ });
+
/* ==============================================
HEADER STICKY -->
=============================================== */
@@ -339,4 +415,4 @@
firstDay: 1
});
- })(jQuery);
\ No newline at end of file
+ });
\ No newline at end of file
diff --git a/trunk/src/AppBundle/Resources/public/js/travelProgram.js b/trunk/src/AppBundle/Resources/public/js/travelProgram.js
index e8ec5ee8..db5c1dca 100644
--- a/trunk/src/AppBundle/Resources/public/js/travelProgram.js
+++ b/trunk/src/AppBundle/Resources/public/js/travelProgram.js
@@ -24,59 +24,4 @@ $(document).ready(function() {
activateTravelDatesTab();
}
-
-
- var videos$ = $('a[id^="video_"]');
-
- videos$.each(function() {
-
- var el$ = $(this);
-
- var text = el$.text();
- var length = text.length - 11;
- var caption = text.substring(0, length);
- var expl = this.id.substring(6, this.id.length);
-
- $('
')
- .attr({
- width: 680,
- height: 466,
- src: '//www.youtube.com/embed/'+ expl,
- frameborder: 0,
- allowfullscreen: true,
- 'data-st-video': this.id
- })
- .addClass('st-collapsed')
- .hide()
- .insertAfter(this)
- ;
- el$
- .css('background-image', 'url(/images/st2/icons/arrowup.gif)')
- .text(caption + ' einblenden')
- .attr('href', 'javascript:void(0);')
- ;
- });
-
- videos$.click(function() {
-
- var el$ = $(this);
- var video$ = $('[data-st-video='+ this.id +']');
- var text = el$.text();
- var length = text.length - 11;
- var caption = text.substring(0, length);
-
- if (el$.hasClass('st-collapsed'))
- {
- video$.slideDown('slow');
- el$.text(caption + ' ausblenden');
- el$.removeClass('st-collapsed');
- }
- else
- {
- video$.slideUp(400);
- el$.text(caption + ' einblenden');
- el$.addClass('st-collapsed');
- }
- });
-
});
\ No newline at end of file
diff --git a/trunk/src/AppBundle/Service/KeywordService.php b/trunk/src/AppBundle/Service/KeywordService.php
new file mode 100644
index 00000000..df995c2e
--- /dev/null
+++ b/trunk/src/AppBundle/Service/KeywordService.php
@@ -0,0 +1,202 @@
+
+ * @date 02/17/2017
+ */
+
+namespace AppBundle\Service;
+
+
+use AppBundle\Entity\Keyword;
+use AppBundle\Entity\Page;
+use Doctrine\ORM\EntityManager;
+
+class KeywordService
+{
+ protected static $DO_SKIP_KEYWORD_CHECK_BY_DOM_ELEMENT_NAME = ['h1' => true, 'h2' => true, 'h3' => true,
+ 'h4' => true, 'h5' => true, 'h6' => true, 'a' => true, 'audio' => true, 'video' => true, 'object' => true,
+ 'nav' => true, 'form' => true
+ ];
+
+ private $entityManager;
+ private $dictByContext = null;
+ private $globalDict = null;
+
+ /**
+ * KeywordService constructor.
+ *
+ * @param EntityManager $entityManager
+ */
+ public function __construct(EntityManager $entityManager)
+ {
+ $this->entityManager = $entityManager;
+ }
+
+ /**
+ * Source: http://78.47.251.156/svn/stern-consulting/sunstarPlugin/branches/1.4/lib/sun.class.php
+ *
+ * @param $html
+ * @param string $classes Space separated values for the "class"-attribute
+ * @return string
+ */
+ public function addKeywordLinksToHtml($html, $classes = '')
+ {
+ $dict = $this->getKeywordsToLinksDict($classes);
+ $doc = new \DOMDocument('1.0', 'utf-8');
+ $doc->loadHTML(''. $html .'');
+ $xp = new \DOMXPath($doc);
+ $rootNodes = $xp->query('//body')->item(0)->childNodes;
+ $this->addKeywordLinksToDomNodes($rootNodes, $dict, $doc);
+ $ret = '';
+ foreach($rootNodes as $node)
+ {
+ $ret .= $doc->saveHTML($node);
+ }
+ return $ret;
+ }
+
+ /**
+ * Quelle: http://78.47.251.156/svn/stern-consulting/sunstarPlugin/branches/1.4/lib/sun.class.php
+ *
+ * @param \DOMNodeList|\DOMNode[] &$nodes
+ * @param array &$dict
+ * @param \DOMDocument &$doc
+ */
+ private function addKeywordLinksToDomNodes(&$nodes, &$dict, &$doc)
+ {
+ // As $nodes is a direct reference to $parentNode->childNodes it will be changed when
+ // replacing text-nodes due to adding keywords. Therefore we loop through this
+ // auxilary array '$originalNodes' which simply contains all nodes to process.
+ $originalNodes = [];
+ foreach ($nodes as $node)
+ {
+ $originalNodes[] = $node;
+ }
+
+ /** @var \DOMNode $node */
+ foreach($originalNodes as $node)
+ {
+ if($node->nodeType == XML_TEXT_NODE)
+ {
+ $nodeValue = $node->nodeValue;
+ $foundKeywords = false;
+ foreach($dict as $keyword => $link)
+ {
+ $nodeValue = preg_replace('/(^|\W)'. preg_quote($keyword, '/') .'($|\W)/u',
+ '$1'. $link .'$2', $nodeValue, 1, $count);
+ if($count > 0)
+ {
+ $foundKeywords = true;
+ unset($dict[$keyword]);
+ }
+ }
+ if($foundKeywords)
+ {
+ $newNode = $doc->createDocumentFragment();
+ $newNode->appendXML($nodeValue);
+ $node->parentNode->replaceChild($newNode, $node);
+ }
+ }
+ elseif($node->nodeType == XML_ELEMENT_NODE &&
+ !isset(self::$DO_SKIP_KEYWORD_CHECK_BY_DOM_ELEMENT_NAME[strtolower($node->nodeName)]))
+ {
+ $classAttr = $node->attributes->getNamedItem("class");
+ if (!(isset($classAttr) && $classAttr->nodeValue &&
+ preg_match('/(^|\s)skip-keywords($|\s)/', $classAttr->nodeValue) == 1))
+ {
+ $this->addKeywordLinksToDomNodes($node->childNodes, $dict, $doc);
+ }
+ }
+ }
+ }
+
+ /**
+ * Source: http://78.47.251.156/svn/stern-consulting/sunstarPlugin/branches/1.4/lib/sun.class.php
+ *
+ * @param string $classes Space separated values for the "class"-attribute
+ * @return array
+ */
+ private function getKeywordsToLinksDict($classes = '', $context = null)
+ {
+ if ($context)
+ {
+ if (!isset($this->dictByContext[$context]))
+ {
+ $this->dictByContext[$context] = null;
+ }
+ $dict = &$this->dictByContext[$context];
+ }
+ else
+ {
+ $dict = &$this->globalDict;
+ }
+
+ if($dict === null)
+ {
+ $dict = [];
+ $keywords = $this->entityManager->getRepository('AppBundle:Keyword')->findAll();
+ /** @var Keyword $keyword */
+ foreach($keywords as $keyword)
+ {
+ if(isset($dict[$keyword->getValue()]))
+ {
+ continue;
+ }
+ $dictEntry = $this->createKeywordDictEntry($keyword->getUrl(), $keyword->getValue(), $classes);
+ if ($dictEntry !== null)
+ {
+ $dict[$keyword->getValue()] = $dictEntry;
+ }
+ }
+
+ $pages = $this->entityManager->createQueryBuilder()
+ ->from('AppBundle:Page', 'p')
+ ->select('p')
+ ->where('p.keyword IS NOT NULL')
+ ->getQuery()
+ ->execute()
+ ;
+ /** @var Page $page */
+ foreach($pages as $page)
+ {
+ if(isset($dict[$page->getKeyword()]))
+ {
+ continue;
+ }
+ $dictEntry = $this->createKeywordDictEntry($page->getUrlPath(), $page->getKeyword(), $classes);
+ if ($dictEntry !== null)
+ {
+ $dict[$page->getKeyword()] = $dictEntry;
+ }
+ }
+ }
+ return $dict;
+ }
+
+ private function createKeywordDictEntry($url, $keywordValue, $classes)
+ {
+ $port = parse_url($url, PHP_URL_PORT);
+ if($url[0] == '/' || strtolower($_SERVER['HTTP_HOST']) ==
+ strtolower(parse_url($url, PHP_URL_HOST) .($port ? ':'. $port : '')))
+ {
+ // Same host => check if same page
+ $urlPath = parse_url($url, PHP_URL_PATH);
+ if($_SERVER['REQUEST_URI'] == $urlPath ||
+ ($_SERVER['REQUEST_URI'] == '/' && $urlPath == ''))
+ {
+ // Yes => we don't want to link to the current URL
+ return null;
+ }
+ // No => setup a JS popup layer
+ $attr = '';
+ $additional_classes = 'intern-link show-layer ';
+ }
+ else
+ {
+ $additional_classes = '';
+ $attr = 'target="_blank"';
+ }
+ return '
'. $keywordValue .'';
+ }
+
+}
\ No newline at end of file
diff --git a/trunk/src/AppBundle/Twig/AppExtension.php b/trunk/src/AppBundle/Twig/AppExtension.php
index a85f699e..ab753eac 100644
--- a/trunk/src/AppBundle/Twig/AppExtension.php
+++ b/trunk/src/AppBundle/Twig/AppExtension.php
@@ -7,14 +7,21 @@
namespace AppBundle\Twig;
+use AppBundle\Service\KeywordService;
+use Symfony\Component\HttpFoundation\RequestStack;
+
class AppExtension extends \Twig_Extension
{
- protected $environment;
+ private $environment;
+ private $keywordService;
+ private $requestStack;
private $template;
- public function __construct(\Twig_Environment $env)
+ public function __construct(\Twig_Environment $env, KeywordService $keywordService, RequestStack $requestStack)
{
$this->environment = $env;
+ $this->keywordService = $keywordService;
+ $this->requestStack = $requestStack;
}
public function getFilters()
@@ -23,6 +30,9 @@ class AppExtension extends \Twig_Extension
'stripslashes' => new \Twig_SimpleFilter('stripslashes', [$this, 'stripslashesFilter'], [
'is_safe' => ['html']
]),
+ 'keywords' => new \Twig_SimpleFilter('keywords', [$this, 'keywordsFilter'], [
+ 'is_safe' => ['html']
+ ]),
];
}
@@ -35,6 +45,7 @@ class AppExtension extends \Twig_Extension
'form_field_pho' => new \Twig_SimpleFunction('form_field_pho', [$this, 'formFieldPho'], [
'is_safe' => ['html']
]),
+ 'get_base_template' => new \Twig_SimpleFunction('get_base_template', [$this, 'getBaseTemplate']),
];
}
@@ -43,6 +54,16 @@ class AppExtension extends \Twig_Extension
return stripslashes($v);
}
+ public function keywordsFilter($v)
+ {
+ return $this->keywordService->addKeywordLinksToHtml($v, 'keyword-link');
+ }
+
+ public function getBaseTemplate()
+ {
+ return ($this->requestStack->getCurrentRequest()->isXmlHttpRequest() ? 'ajax' : 'base') .'.html.twig';
+ }
+
public function formField($form, $label = null, $opt = null)
{
$this->template = $this->environment->loadTemplate( '::default/form/helpers.html.twig' );
diff --git a/trunk/web/app.php b/trunk/web/app.php
index 6bd0ea01..60262fe0 100644
--- a/trunk/web/app.php
+++ b/trunk/web/app.php
@@ -2,6 +2,8 @@
use Symfony\Component\HttpFoundation\Request;
+libxml_use_internal_errors(true);
+
/** @var \Composer\Autoload\ClassLoader $loader */
$loader = require __DIR__.'/../app/autoload.php';
include_once __DIR__.'/../var/bootstrap.php.cache';
diff --git a/trunk/web/app_dev.php b/trunk/web/app_dev.php
index 94f18864..42a5b22c 100644
--- a/trunk/web/app_dev.php
+++ b/trunk/web/app_dev.php
@@ -3,6 +3,8 @@
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Debug\Debug;
+libxml_use_internal_errors(true);
+
// If you don't want to setup permissions the proper way, just uncomment the following PHP line
// read http://symfony.com/doc/current/book/installation.html#checking-symfony-application-configuration-and-setup
// for more information
@@ -10,6 +12,7 @@ use Symfony\Component\Debug\Debug;
// This check prevents access to debug front controllers that are deployed by accident to production servers.
// Feel free to remove this, extend it, or make something more sophisticated.
+/*
if (isset($_SERVER['HTTP_CLIENT_IP'])
|| isset($_SERVER['HTTP_X_FORWARDED_FOR'])
|| !(in_array(@$_SERVER['REMOTE_ADDR'], ['127.0.0.1', '::1']) || php_sapi_name() === 'cli-server')
@@ -17,14 +20,17 @@ if (isset($_SERVER['HTTP_CLIENT_IP'])
header('HTTP/1.0 403 Forbidden');
exit('You are not allowed to access this file. Check '.basename(__FILE__).' for more information.');
}
+//*/
/** @var \Composer\Autoload\ClassLoader $loader */
$loader = require __DIR__.'/../app/autoload.php';
// Redis loader
+/*
$redisLoader = new \AppBundle\RedisClassLoader('de.sterntours.v3', $loader);
$loader->unregister();
$redisLoader->register(true);
+//*/
Debug::enable();