* keywords im content mit Links ersetzen

* Links mit Klasse "show-modal" bei Klick in einen JS-Modal laden, statt dem Link zu folgen

git-svn-id: http://78.47.251.156/svn/dev/sterntours-3@3296 f459cee4-fb09-11de-96c3-f9c5f16c3c76
This commit is contained in:
uli 2017-02-17 13:16:15 +00:00
parent 20beca7c4d
commit c924b4af15
15 changed files with 333 additions and 75 deletions

View file

@ -0,0 +1 @@
{% block body %}{% endblock %}

View file

@ -103,6 +103,9 @@
</div><!-- end wrapper -->
<!-- default modal -->
{% embed 'default/components/embed/modal.html.twig' with {id: 'default'} %}{% endembed %}
{% block javascripts %}
<script src="http://maps.google.com/maps/api/js?sensor=false"></script>
{% javascripts

View file

@ -3,7 +3,7 @@
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title">{{ title }}</h4>
<h4 class="modal-title">{{ title ?? '' }}</h4>
</div>
<div class="modal-body">
{% block body %}{% endblock %}

View file

@ -1,7 +1,7 @@
{% extends 'base.html.twig' %}
{% extends get_base_template() %}
{% block body %}
<section class="clearfix">
{{ page.content|raw }}
{{ page.content|raw|keywords }}
</section>
{% endblock %}

View file

@ -1,4 +1,4 @@
{% extends 'base.html.twig' %}
{% extends get_base_template() %}
{#
{% block nav_sidebar_widget %}
@ -21,6 +21,6 @@
</section>
<section class="clearfix">
{{ page.content|raw }}
{{ page.content|raw|keywords }}
</section>
{% endblock %}

View file

@ -1,4 +1,4 @@
{% extends 'base.html.twig' %}
{% extends get_base_template() %}
{% block body %}
<section class="clearfix">

View file

@ -1,5 +1,5 @@
{# @var travel_program \AppBundle\Entity\TravelProgram #}
{% extends 'base.html.twig' %}
{% extends get_base_template() %}
{% block javascripts %}
{{ parent() }}
@ -116,7 +116,7 @@
<div role="tabpanel" class="tab-pane active" id="travel-description-content-tab">
{{ travel_program.htmlDescription|raw }}
{{ travel_program.htmlDescription|raw|keywords }}
{% if travel_program.advices is not empty %}
<h3>Hinweise</h3>

View file

@ -1,4 +1,4 @@
{% extends 'base.html.twig' %}
{% extends get_base_template() %}
{% block body %}
<section class="clearfix">
@ -32,6 +32,6 @@
</section>
<section class="clearfix">
{{ page.content|raw }}
{{ page.content|keywords|raw }}
</section>
{% endblock %}

View file

@ -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 }
app.keyword_service:
class: AppBundle\Service\KeywordService
arguments:
- '@doctrine.orm.entity_manager'

View file

@ -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);
$('<iframe />')
.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);
});

View file

@ -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);
$('<iframe />')
.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');
}
});
});

View file

@ -0,0 +1,202 @@
<?php
/**
* @author Ulrich Hecht <ulrich.hecht@hecht-software.de>
* @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('<?xml encoding="utf-8" ?><html><body>'. $html .'</body></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 '<a href="'. $url .'" '. $attr .' class="'. $additional_classes . $classes .'">'. $keywordValue .'</a>';
}
}

View file

@ -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' );

View file

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

View file

@ -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();