Commit 151a17aa authored by Felix Herrmann's avatar Felix Herrmann
Browse files

Merge branch 'feature/seo-url-gen' into 'master'

Add SEO-URL generation using the SEO index module

See merge request shopware/plugins/RicoDynamicFAQ!27
parents ae5819b3 596925e0
<?php
/**
* MIT License
*
* Copyright (c) 2020 PSVneo
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
declare(strict_types=1);
namespace RicoDynamicFaq\Components\Manager;
use Exception;
use RicoDynamicFaq\Models\Question;
use RicoDynamicFaq\Repository\QuestionRepository;
use Shopware\Components\Model\ModelManager;
use Shopware\Components\Plugin\CachedConfigReader;
use Shopware\Models\Shop\Shop;
use Shopware_Components_Modules;
use sRewriteTable;
class SeoUrlManager implements SeoUrlManagerInterface
{
protected const ORIGINAL_PATH = 'sViewport=RicoFaq';
/**
* @var Shopware_Components_Modules
*/
protected $modules;
/**
* @var ModelManager
*/
protected $modelManager;
/**
* @var CachedConfigReader
*/
protected $configReader;
/**
* @var string
*/
protected $pluginName = '';
/**
* @var sRewriteTable
*/
protected $rewriteTable;
/**
* @var QuestionRepository
*/
protected $questionRepository;
public function __construct(
Shopware_Components_Modules $modules,
ModelManager $modelManager,
CachedConfigReader $configReader,
string $pluginName
) {
$this->modules = $modules;
$this->modelManager = $modelManager;
$this->configReader = $configReader;
$this->pluginName = $pluginName;
$this->rewriteTable = $this->modules->RewriteTable();
$this->questionRepository = $this->modelManager->getRepository(Question::class);
}
public function refreshSeoUrls(Shop $shop): void
{
$this->flushExistingUrls($shop->getId());
$this->buildUrlsForShop($shop);
}
protected function flushExistingUrls(int $shopId): void
{
$queryBuilder = $this->modelManager->getConnection()->createQueryBuilder();
$queryBuilder->delete('s_core_rewrite_urls')
->where(
$queryBuilder->expr()->andX(
$queryBuilder->expr()->eq('subshopID', ':shopId'),
$queryBuilder->expr()->like('org_path', ':path')
)
)
->setParameter('shopId', $shopId, \PDO::PARAM_INT)
->setParameter('path', self::ORIGINAL_PATH . '%', \PDO::PARAM_STR)
->execute();
}
protected function buildUrlsForShop(Shop $shop): void
{
$config = $this->configReader->getByPluginName($this->pluginName, $shop);
if (!array_key_exists('basePath', $config)) {
throw new Exception('Could not find required option "basePath" of plugin ' . $this->pluginName . '.');
}
$this->insertUrl($shop, self::ORIGINAL_PATH, $config['basePath']);
$questions = $this->questionRepository->findAll();
if ((bool) $config['disableDetailView']) {
return;
}
/** @var Question $question */
foreach ($questions as $question) {
$this->buildQuestionUrl($shop, $question, (string) $config['basePath']);
}
}
protected function insertUrl(Shop $shop, string $originalPath, string $seoUrl): void
{
$shop->registerResources();
$this->rewriteTable->sInsertUrl($originalPath, strtolower($this->rewriteTable->sCleanupPath($seoUrl)));
}
protected function buildQuestionUrl(Shop $shop, Question $question, string $basePath): void
{
$this->insertUrl(
$shop,
self::ORIGINAL_PATH . '&action=detail&id=' . $question->getId(),
$basePath . '/' . $this->escapeQuestionTitle($question->getQuestionText())
);
}
protected function escapeQuestionTitle(string $tile): string
{
return trim(str_replace(['.', '/'], '-', strtolower(strip_tags($tile))));
}
}
<?php
/**
* MIT License
*
* Copyright (c) 2020 PSVneo
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
declare(strict_types=1);
namespace RicoDynamicFaq\Components\Manager;
use Shopware\Models\Shop\Shop;
interface SeoUrlManagerInterface
{
public function refreshSeoUrls(Shop $shop): void;
}
......@@ -67,6 +67,25 @@ class Shopware_Controllers_Backend_Faq extends Shopware_Controllers_Backend_Appl
$this->View()->assign(['success' => true, 'data' => null]);
}
public function seoFaqAction(): void
{
@set_time_limit(1200);
$shopId = (int) $this->Request()->getParam('shopId');
/** @var Shopware_Components_SeoIndex $seoIndex */
$seoIndex = $this->container->get('SeoIndex');
$shop = $seoIndex->registerShop($shopId);
/** @var Shopware_Components_Modules $modules */
$modules = $this->container->get('modules');
$rewriteTable = $modules->RewriteTable();
$rewriteTable->baseSetup();
/** @var \RicoDynamicFAQ\Components\Manager\SeoUrlManagerInterface $seoUrlManager */
$seoUrlManager = $this->container->get('rico_dynamic_faq.service.components.manager.seo_url_manager');
$seoUrlManager->refreshSeoUrls($shop);
$this->View()->assign([
'success' => true,
]);
}
/**
* {@inheritdoc}
*/
......
......@@ -57,6 +57,11 @@ class Shopware_Controllers_Frontend_RicoFaq extends \RicoDynamicFaq\Controllers\
public function detailAction(): void
{
if ((bool) $this->config->get('disableDetailView', $this->getShop())) {
$this->forward('pageNotFoundError', 'error', 'frontend');
return;
}
$question = $this->questionRepository->findById((int) $this->Request()->getParam('id'));
if ($this->getShop()->getId() > 1) {
$question = $this->translateQuestions([$question])[0] ?? null;
......
......@@ -4,8 +4,8 @@
<elements>
<element scope="shop">
<name>basePath</name>
<label lang="de">Plugin URL</label>
<label>Plugin URL</label>
<label lang="de">Plugin sprechender URL-Pfad</label>
<label>Plugin speaking URL path</label>
<value>faq</value>
<description lang="de">
Beinhaltet die Plugin URL. Zum Beispiel "faq".
......@@ -22,5 +22,22 @@
<description>Defines the maximum output of questions per page.</description>
<description lang="de">Definiert die maximale Anzahl an Fragen pro Seite.</description>
</element>
<element type="boolean">
<name>disableDetailView</name>
<label>Disables the detail view of questions</label>
<label lang="de">Einzelansicht von Fragen deaktivieren</label>
<description>
<![CDATA[
Disables the detail view of questions and redirects to 404 page instead<br>
If this option is active no detail view URLs getting generated
]]>
</description>
<description lang="de">
<![CDATA[
Deaktiviert Einzelansichten von Fragen und leitet stattdessen auf die 404-Seite um.<br>
Wenn diese Option aktiv ist, werden keine URLs für Einzelansichten generiert.
]]>
</description>
</element>
</elements>
</config>
......@@ -9,13 +9,11 @@
<argument>%rico_dynamic_faq.plugin_dir%</argument>
<tag name="shopware.event_subscriber"/>
</service>
<service class="RicoDynamicFaq\Subscribers\UrlSubscriber"
id="rico_dynamic_faq.subscribers.url_subscriber">
<argument type="service" id="modules"/>
<service class="RicoDynamicFaq\Subscribers\BackendPerformanceSubscriber"
id="rico_dynamic_faq.subscribers.backend_performance_subscriber">
<argument>%rico_dynamic_faq.plugin_dir%</argument>
<argument type="service" id="models"/>
<argument type="service" id="shopware.plugin_manager"/>
<argument type="service" id="shopware.plugin.cached_config_reader"/>
<argument>%rico_dynamic_faq.plugin_name%</argument>
<argument type="service" id="rico_dynamic_faq.config"/>
<tag name="shopware.event_subscriber"/>
</service>
<service class="RicoDynamicFaq\Subscribers\NavigationSubscriber"
......@@ -54,5 +52,12 @@
<argument type="service"
id="rico_dynamic_faq.service.components.translation.translation_overlay_component"/>
</service>
<service class="RicoDynamicFaq\Components\Manager\SeoUrlManager"
id="rico_dynamic_faq.service.components.manager.seo_url_manager">
<argument type="service" id="modules"/>
<argument type="service" id="models"/>
<argument type="service" id="shopware.plugin.cached_config_reader"/>
<argument>%rico_dynamic_faq.plugin_name%</argument>
</service>
</services>
</container>
// {block name="backend/performance/view/main/multi_request_tasks" append}
Ext.define('Shopware.apps.Performance.view.main.RicoDynamicFAQ', {
override: 'Shopware.apps.Performance.view.main.MultiRequestTasks',
initComponent: function() {
this.addProgressBar(
{
initialText: 'RicoDynamicFAQ URLs',
progressText: '[0] of [1] faq URLs',
requestUrl: '{url controller=Faq action=seoFaq}'
},
'faq',
'seo'
);
this.callParent(arguments);
}
});
// {/block}
......@@ -29,115 +29,70 @@ declare(strict_types=1);
namespace RicoDynamicFaq\Subscribers;
use Enlight\Event\SubscriberInterface;
use Shopware\Bundle\PluginInstallerBundle\Service\InstallerService;
use Enlight_Controller_ActionEventArgs;
use Enlight_Event_EventArgs;
use RicoDynamicFaq\Models\Question;
use RicoDynamicFaq\Services\ConfigurationService;
use Shopware\Components\Model\ModelManager;
use Shopware\Components\Plugin\CachedConfigReader;
use Shopware\Models\Shop\Repository;
use Shopware\Models\Shop\Shop;
/**
* Class UrlSubscriber
*/
class UrlSubscriber implements SubscriberInterface
class BackendPerformanceSubscriber implements SubscriberInterface
{
/**
* @var InstallerService
*/
private $pluginManager;
/**
* @var \sRewriteTable
*/
private $rewriteTable;
/**
* @var Repository
*/
private $shopRepository;
/**
* @var \Doctrine\DBAL\Connection
*/
private $connection;
/**
* @var CachedConfigReader
* @var string
*/
private $cachedConfigReader;
protected $pluginDirectory = '';
/**
* @var string
* @var ModelManager
*/
private $pluginName;
protected $modelManager;
/**
* UrlSubscriber constructor.
*
* @param InstallerService $pluginManager
* @param \Shopware_Components_Modules $modules
* @param ModelManager $models
* @param CachedConfigReader $cachedConfigReader
* @param string $pluginName
* @var ConfigurationService
*/
public function __construct($modules, $models, $pluginManager, $cachedConfigReader, $pluginName)
{
$this->pluginManager = $pluginManager;
$this->pluginName = $pluginName;
$this->rewriteTable = $modules->RewriteTable();
$this->shopRepository = $models->getRepository('Shopware\Models\Shop\Shop');
$this->connection = $models->getConnection();
$this->cachedConfigReader = $cachedConfigReader;
protected $configurationService;
public function __construct(
string $pluginDirectory,
ModelManager $modelManager,
ConfigurationService $configurationService
) {
$this->pluginDirectory = $pluginDirectory;
$this->modelManager = $modelManager;
$this->configurationService = $configurationService;
}
/**
* @return array
*/
public static function getSubscribedEvents()
public static function getSubscribedEvents(): array
{
return [
'Shopware_Controllers_Backend_Config_After_Save_Config_Element' => 'afterSave',
'Enlight_Controller_Action_PostDispatch_Backend_Performance' => 'onDispatchPerformance',
'Shopware_Controllers_Seo_filterCounts' => 'addFaqCount',
];
}
/**
* @param \Enlight_Event_EventArgs $eventArgs
* @param mixed $args
*
* @throws \Exception
*/
public function afterSave($args)
public function onDispatchPerformance(Enlight_Controller_ActionEventArgs $args): void
{
if ($this->pluginManager->getPluginByName($this->pluginName)->getId() !== $args['element']->getForm()->getPluginId()) {
$subject = $args->getSubject();
$request = $subject->Request();
if ($request->getActionName() !== 'load') {
return;
}
$this->connection->query(
'DELETE FROM s_core_rewrite_urls WHERE org_path = "sViewport=RicoFaq"'
)->execute();
foreach ($this->shopRepository->findBy(['active' => true]) as $shop) {
$config = $this->cachedConfigReader->getByPluginName($this->pluginName, $shop);
if (array_key_exists('basePath', $config)) {
$this->insertUrl($shop, $config['basePath']);
}
}
$subject->View()->addTemplateDir($this->pluginDirectory . '/Resources/views/');
$subject->View()->extendsTemplate('backend/performance/view/main/faq.js');
}
/**
* @param $elementValue
*
* @throws \Exception
*/
private function insertUrl(Shop $shop, $elementValue)
public function addFaqCount(Enlight_Event_EventArgs $args): array
{
if (!($shop instanceof Shop)) {
return false;
$counts = $args->getReturn();
if ((bool) $this->configurationService->get('disableDetailView', Shopware()->Shop())) {
$counts['faq'] = 1;
return $counts;
}
$shop->registerResources();
$this->rewriteTable->sInsertUrl(
'sViewport=RicoFaq',
$elementValue
);
$questionRepository = $this->modelManager->getRepository(Question::class);
$questions = $questionRepository->findAll();
$counts['faq'] = (1 + count($questions));
return true;
return $counts;
}
}
......@@ -3,12 +3,20 @@
xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/shopware/shopware/5.3/engine/Shopware/Components/Plugin/schema/plugin.xsd">
<label lang="de">Dynamic FAQ</label>
<label>Dynamic FAQ</label>
<version>2.0.0</version>
<version>2.1.0</version>
<copyright>(c) PSVneo</copyright>
<license>MIT</license>
<link>https://git.riconnect.de/riconet-public/shopware/plugins/RicoDynamicFAQ</link>
<author>Wolf Utz, Alexander Wink</author>
<compatibility minVersion="5.3.0" maxVersion="5.6.99"/>
<changelog version="2.1.0">
<changes>
<![CDATA[
Add SEO-URL generation using the performance module<br>
Add option to disable detail view
]]>
</changes>
</changelog>
<changelog version="2.0.0">
<changes>Add multilingual frontend</changes>
</changelog>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment