Merge pull request #374 from bibich/sitemap

Sitemap
This commit is contained in:
Manuel Raynaud
2014-05-04 22:54:34 +02:00
7 changed files with 246 additions and 159 deletions

View File

@@ -1,40 +1,36 @@
{ {
"name" : "thelia/thelia", "name": "thelia/thelia",
"description" : "Thelia is an ecommerce CMS.", "description": "Thelia is an ecommerce CMS.",
"license" : "LGPL-3.0+", "license": "LGPL-3.0+",
"homepage" : "http://thelia.net/", "homepage": "http://thelia.net/",
"support" : { "support": {
"forum" : "http://thelia.net/forum", "forum": "http://thelia.net/forum",
"wiki" : "http://doc.thelia.net" "wiki": "http://doc.thelia.net"
}, },
"require":{ "require": {
"php": ">=5.4", "php": ">=5.4",
"ircmaxell/password-compat": "1.0.*", "ircmaxell/password-compat": "1.0.*",
"propel/propel": "dev-master", "propel/propel": "dev-master",
"psr/log" : "1.0", "psr/log": "1.0",
"symfony/class-loader": "2.2.*", "symfony/class-loader": "2.2.*",
"symfony/config" : "2.2.*", "symfony/config": "2.2.*",
"symfony/console" : "2.2.*", "symfony/console": "2.2.*",
"symfony/dependency-injection" : "2.2.*", "symfony/dependency-injection": "2.2.*",
"symfony/event-dispatcher" : "2.2.*", "symfony/event-dispatcher": "2.2.*",
"symfony/http-kernel" : "2.2.*", "symfony/http-kernel": "2.2.*",
"symfony/routing" : "2.2.*", "symfony/routing": "2.2.*",
"symfony/filesystem" : "2.2.*", "symfony/filesystem": "2.2.*",
"symfony/yaml" : "2.2.*", "symfony/yaml": "2.2.*",
"symfony/translation" : "2.2.*", "symfony/translation": "2.2.*",
"symfony-cmf/routing": "1.0.0", "symfony-cmf/routing": "1.0.0",
"symfony/form": "2.2.*", "symfony/form": "2.2.*",
"symfony/validator": "2.3.*", "symfony/validator": "2.3.*",
"smarty/smarty": "v3.1.14", "smarty/smarty": "v3.1.14",
"kriswallsmith/assetic": "1.2.*@dev", "kriswallsmith/assetic": "1.2.*@dev",
"leafo/lessphp": "0.4.*", "leafo/lessphp": "0.4.*",
"ptachoire/cssembed": "1.0.*", "ptachoire/cssembed": "1.0.*",
"doctrine/cache": "v1.3.0",
"simplepie/simplepie": "dev-master", "simplepie/simplepie": "dev-master",
"imagine/imagine": "0.*", "imagine/imagine": "0.*",
"symfony/icu": "1.0", "symfony/icu": "1.0",
"swiftmailer/swiftmailer": "5.0.*", "swiftmailer/swiftmailer": "5.0.*",
@@ -42,20 +38,20 @@
"ensepar/html2pdf": "1.0.1", "ensepar/html2pdf": "1.0.1",
"symfony/finder": "~2.2" "symfony/finder": "~2.2"
}, },
"require-dev" : { "require-dev": {
"phpunit/phpunit": "3.7.*", "phpunit/phpunit": "3.7.*",
"fzaninotto/faker": "dev-master", "fzaninotto/faker": "dev-master",
"maximebf/debugbar": "dev-master" "maximebf/debugbar": "dev-master"
}, },
"minimum-stability": "stable", "minimum-stability": "stable",
"config" : { "config": {
"vendor-dir" : "core/vendor", "vendor-dir": "core/vendor",
"bin-dir" : "bin" "bin-dir": "bin"
}, },
"autoload": { "autoload": {
"psr-0": { "psr-0": {
"": "local/modules/", "": "local/modules/",
"Thelia" : "core/lib/" "Thelia": "core/lib/"
} }
}, },
"extra": { "extra": {
@@ -79,7 +75,9 @@
"reference": "tags/Smarty_3_1_14/distribution/" "reference": "tags/Smarty_3_1_14/distribution/"
}, },
"autoload": { "autoload": {
"classmap": ["libs/"] "classmap": [
"libs/"
]
} }
} }
} }

76
composer.lock generated
View File

@@ -3,8 +3,82 @@
"This file locks the dependencies of your project to a known state", "This file locks the dependencies of your project to a known state",
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
], ],
"hash": "5091d03a67414e4ed7aef32a8c737503", "hash": "d1e1c31ed8e38f2282ab431898cf8b08",
"packages": [ "packages": [
{
"name": "doctrine/cache",
"version": "v1.3.0",
"source": {
"type": "git",
"url": "https://github.com/doctrine/cache.git",
"reference": "e16d7adf45664a50fa86f515b6d5e7f670130449"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/cache/zipball/e16d7adf45664a50fa86f515b6d5e7f670130449",
"reference": "e16d7adf45664a50fa86f515b6d5e7f670130449",
"shasum": ""
},
"require": {
"php": ">=5.3.2"
},
"conflict": {
"doctrine/common": ">2.2,<2.4"
},
"require-dev": {
"phpunit/phpunit": ">=3.7",
"satooshi/php-coveralls": "~0.6"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-0": {
"Doctrine\\Common\\Cache\\": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jonathan H. Wage",
"email": "jonwage@gmail.com",
"homepage": "http://www.jwage.com/",
"role": "Creator"
},
{
"name": "Guilherme Blanco",
"email": "guilhermeblanco@gmail.com",
"homepage": "http://www.instaclick.com"
},
{
"name": "Roman Borschel",
"email": "roman@code-factory.org"
},
{
"name": "Benjamin Eberlei",
"email": "kontakt@beberlei.de"
},
{
"name": "Johannes Schmitt",
"email": "schmittjoh@gmail.com",
"homepage": "http://jmsyst.com",
"role": "Developer of wrapped JMSSerializerBundle"
}
],
"description": "Caching library offering an object-oriented API for many cache backends",
"homepage": "http://www.doctrine-project.org",
"keywords": [
"cache",
"caching"
],
"time": "2013-10-25 19:04:14"
},
{ {
"name": "ensepar/html2pdf", "name": "ensepar/html2pdf",
"version": "1.0.1", "version": "1.0.1",

View File

@@ -223,9 +223,6 @@ class Content extends BaseI18nLoop implements PropelSearchLoopInterface
->set("VISIBLE" , $content->getVisible()) ->set("VISIBLE" , $content->getVisible())
; ;
$loopResult->addRow($this->findNextPrev($loopResultRow, $content, $defaultFolderId)); $loopResult->addRow($this->findNextPrev($loopResultRow, $content, $defaultFolderId));
} }
@@ -234,8 +231,8 @@ class Content extends BaseI18nLoop implements PropelSearchLoopInterface
} }
/** /**
* @param LoopResultRow $loopResultRow * @param LoopResultRow $loopResultRow
* @param \Thelia\Model\Content $content * @param \Thelia\Model\Content $content
* @param $defaultFolderId * @param $defaultFolderId
* @return LoopResultRow * @return LoopResultRow
*/ */

View File

@@ -12,7 +12,6 @@
namespace Thelia\Form; namespace Thelia\Form;
use Symfony\Component\Validator\Constraints\NotBlank;
use Thelia\Core\Translation\Translator; use Thelia\Core\Translation\Translator;
/** /**

View File

@@ -13,17 +13,26 @@
namespace Front\Controller; namespace Front\Controller;
use Doctrine\Common\Cache\FilesystemCache;
use Thelia\Controller\Front\BaseFrontController; use Thelia\Controller\Front\BaseFrontController;
use Thelia\Core\HttpFoundation\Request; use Thelia\Core\HttpFoundation\Request;
use Thelia\Core\HttpFoundation\Response; use Thelia\Core\HttpFoundation\Response;
use Thelia\Log\Tlog; use Thelia\Log\Tlog;
use Thelia\Model\ConfigQuery; use Thelia\Model\ConfigQuery;
use Thelia\Model\LangQuery;
/** /**
* Controller uses to generate sitemap.xml * Controller uses to generate sitemap.xml
* *
* A default cache of 2 hours is used to avoid attack. You can flush cache if you have `ADMIN` role and pass flush=1 in * A default cache of 2 hours is used to avoid attack. You can flush cache if you have `ADMIN` role and pass flush=1 in
* query parameter. * query string parameter.
*
* You can generate sitemap according to specific language and/or specific context (catalog/content). You have to
* use ```lang``` and ```context``` query string parameters to do so. If a language is not used in website or if the
* context is not supported the page not found is displayed.
*
* {url}/sitemap?lang=fr&context=catalog will generate a sitemap for catalog (categories and products)
* for french language.
* *
* @package Front\Controller * @package Front\Controller
* @author Julien Chanséaume <jchanseaume@openstudio.fr> * @author Julien Chanséaume <jchanseaume@openstudio.fr>
@@ -34,12 +43,12 @@ class SitemapController extends BaseFrontController {
/** /**
* Folder name for sitemap cache * Folder name for sitemap cache
*/ */
const SITEMAP_DIR = "sitemap"; const SITEMAP_CACHE_DIR = "sitemap";
/** /**
* Folder name for sitemap cache * Key prefix for sitemap cache
*/ */
const SITEMAP_FILE = "sitemap"; const SITEMAP_CACHE_KEY = "sitemap";
/** /**
* @return Response * @return Response
@@ -49,36 +58,48 @@ class SitemapController extends BaseFrontController {
/** @var Request $request */ /** @var Request $request */
$request = $this->getRequest(); $request = $this->getRequest();
$flush = $request->query->get("flush", "");
$expire = ConfigQuery::read("sitemap_ttl", '7200');
// check if sitemap already in cache // the locale : fr, en,
$cacheDir = $this->getCacheDir(); $lang = $request->query->get("lang", "");
$cacheFileURL = $cacheDir . self::SITEMAP_FILE . '.xml'; if ("" !== $lang) {
$expire = intval($expire) ?: 7200; if (! $this->checkLang($lang)){
$cacheContent = null; $this->pageNotFound();
if (!($this->checkAdmin() && "" !== $flush)){
try {
$cacheContent = $this->getCache($cacheFileURL, $expire);
} catch (\RuntimeException $ex) {
// Problem loading cache, permission errors ?
Tlog::getInstance()->addAlert($ex->getMessage());
} }
} }
// specific content : product, category, cms
$context = $request->query->get("context", "");
if (! in_array($context, array("", "catalog", "content")) ){
$this->pageNotFound();
}
if (null === $cacheContent){ $flush = $request->query->get("flush", "");
// check if sitemap already in cache
$cacheContent = false;
$cacheDir = $this->getCacheDir();
$cacheKey = self::SITEMAP_CACHE_KEY . $lang . $context;
$cacheExpire = intval(ConfigQuery::read("sitemap_ttl", '7200')) ?: 7200;
$cacheDriver = new FilesystemCache($cacheDir);
if (!($this->checkAdmin() && "" !== $flush)){
$cacheContent = $cacheDriver->fetch($cacheKey);
} else {
$cacheDriver->delete($cacheKey);
}
// if not in cache
if (false === $cacheContent){
// render the view // render the view
$cacheContent = $this->renderRaw("sitemap"); $cacheContent = $this->renderRaw(
"sitemap",
array(
"_lang_" => $lang,
"_context_" => $context
)
);
// save cache // save cache
try { $cacheDriver->save($cacheKey, $cacheContent, $cacheExpire);
$this->setCache($cacheFileURL, $cacheContent);
} catch (\RuntimeException $ex) {
// Problem loading cache, permission errors ?
Tlog::getInstance()->addAlert($ex->getMessage());
}
} }
$response = new Response(); $response = new Response();
@@ -88,70 +109,44 @@ class SitemapController extends BaseFrontController {
return $response; return $response;
} }
/**
* get the cache directory for sitemap
*
* @return mixed|string
*/
protected function getCacheDir()
{
$cacheDir = $this->container->getParameter("kernel.cache_dir");
$cacheDir = rtrim($cacheDir, '/');
$cacheDir .= '/' . self::SITEMAP_CACHE_DIR . '/';
return $cacheDir;
}
/** /**
* Check if current user has ADMIN role * Check if current user has ADMIN role
* *
* @return bool * @return bool
*/ */
protected function checkAdmin(){ protected function checkAdmin(){
return $this->getSecurityContext()->isGranted(array("ADMIN"), array(), array(), array()); return $this->getSecurityContext()->hasAdminUser();
} }
/**
* Get the content of the file if it exists and not expired?
*
* @param $fileURL path to the file
* @param $expire TTL for the file
* @return null|string The content of the file if it exists and not expired
* @throws \RuntimeException
*/
protected function getCache($fileURL, $expire)
{
$content = null;
if (is_file($fileURL)){
$mtime = filemtime($fileURL);
if ($mtime + $expire < time()){
if (! @unlink($fileURL)){
throw new \RuntimeException(sprintf("Failed to remove %s file in cache directory", $fileURL));
}
} else {
$content = file_get_contents($fileURL);
}
}
return $content;
}
/** /**
* Save content in the file specified by `$fileURL` * Check if a lang is used
* *
* @param $fileURL the path to the file * @param $lang The lang code. e.g.: fr
* @param $content the content of the file * @return bool true if the language is used, otherwise false
* @throws \RuntimeException
*/ */
protected function setCache($fileURL, $content) private function checkLang($lang)
{ {
if (! @file_put_contents($fileURL, $content)){ // load locals
throw new \RuntimeException(sprintf("Failed to save %s file in cache directory", $fileURL)); $lang = LangQuery::create()
} ->findOneByCode($lang);
}
/** return (null !== $lang);
* Retrieve the cache dir used for sitemaps
*
* @return string the path to the cache dir
* @throws \RuntimeException
*/
protected function getCacheDir()
{
$cacheDir = $this->container->getParameter("kernel.cache_dir");
$cacheDir = rtrim($cacheDir, '/');
$cacheDir .= '/' . self::SITEMAP_DIR . '/';
if (! is_dir($cacheDir)){
if (! @mkdir($cacheDir, 0777, true)) {
throw new \RuntimeException(sprintf("Failed to create %s dir in cache directory", self::SITEMAP_DIR));
}
}
return $cacheDir;
} }
} }

View File

@@ -1,47 +1,71 @@
<?xml version="1.0"?> <?xml version="1.0"?>
<!--
generated on : {$smarty.now|date_format:'%Y-%m-%d %H:%M:%S'}
{if $_lang_ != "" }lang : {$_lang_}{/if}
{if $_context_ != "" }context : {$_context_}{/if}
-->
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url> <url>
<loc>{url path="/"}</loc> <loc>{url path="/"}</loc>
</url> {*
<!-- categories --> You can also set priority and changefreq
{loop type="lang" name="category_lang"} <priority>0.8</priority>
{loop type="category" name="category" lang="$ID"} <changefreq>weekly</changefreq>
<url> *}
<loc>{$URL}</loc> </url>
<lastmod>{format_date date=$UPDATE_DATE format="c"}</lastmod>
{* {if $_context_ == "" || $_context_ == "catalog" }
You can also set priority and changefreq
<priority>0.8</priority> <!-- categories -->
<changefreq>weekly</changefreq> {loop type="lang" name="category_lang"}
*} {if $_lang_ == "" || $_lang_ == $CODE }
</url> {loop type="category" name="category" lang="$ID"}
{/loop} <url>
{/loop} <loc>{$URL}</loc>
<!-- products --> <lastmod>{format_date date=$UPDATE_DATE format="c"}</lastmod>
{loop type="lang" name="product_lang"} </url>
{loop type="product" name="product" lang="$ID"} {/loop}
<url> {/if}
<loc>{$URL}</loc> {/loop}
<lastmod>{format_date date=$UPDATE_DATE format="c"}</lastmod>
</url> <!-- products -->
{/loop} {loop type="lang" name="product_lang"}
{/loop} {if $_lang_ == "" || $_lang_ == $CODE }
<!-- folders --> {loop type="product" name="product" lang="$ID"}
{loop type="lang" name="folder_lang"} <url>
{loop type="folder" name="folder" lang="$ID"} <loc>{$URL}</loc>
<url> <lastmod>{format_date date=$UPDATE_DATE format="c"}</lastmod>
<loc>{$URL}</loc> </url>
<lastmod>{format_date date=$UPDATE_DATE format="c"}</lastmod> {/loop}
</url> {/if}
{/loop} {/loop}
{/loop}
<!-- contents --> {/if}
{loop type="lang" name="content_lang"}
{loop type="content" name="content" lang="$ID"} {if $_context_ == "" || $_context_ == "content" }
<url> <!-- folders -->
<loc>{$URL}</loc> {loop type="lang" name="folder_lang"}
<lastmod>{format_date date=$UPDATE_DATE format="c"}</lastmod> {if $_lang_ == "" || $_lang_ == $CODE }
</url> {loop type="folder" name="folder" lang="$ID"}
{/loop} <url>
{/loop} <loc>{$URL}</loc>
<lastmod>{format_date date=$UPDATE_DATE format="c"}</lastmod>
</url>
{/loop}
{/if}
{/loop}
<!-- contents -->
{loop type="lang" name="content_lang"}
{if $_lang_ == "" || $_lang_ == $CODE }
{loop type="content" name="content" lang="$ID"}
<url>
<loc>{$URL}</loc>
<lastmod>{format_date date=$UPDATE_DATE format="c"}</lastmod>
</url>
{/loop}
{/if}
{/loop}
{/if}
</urlset> </urlset>

View File

@@ -6,4 +6,4 @@ Disallow: /admin
Disallow: /cart Disallow: /cart
Disallow: /404 Disallow: /404
#Sitemap: http://www.yourdomain.com/sitemap.xml #Sitemap: http://www.yourdomain.com/sitemap