diff --git a/local/modules/Front/Config/front.xml b/local/modules/Front/Config/front.xml
index 319d9b4c2..4f32372a3 100644
--- a/local/modules/Front/Config/front.xml
+++ b/local/modules/Front/Config/front.xml
@@ -194,6 +194,12 @@
+
+
+ Front\Controller\SitemapController::generateAction
+
+
+
Thelia\Controller\Front\DefaultController::noAction
diff --git a/local/modules/Front/Controller/SitemapController.php b/local/modules/Front/Controller/SitemapController.php
new file mode 100644
index 000000000..422d4247f
--- /dev/null
+++ b/local/modules/Front/Controller/SitemapController.php
@@ -0,0 +1,157 @@
+
+ */
+class SitemapController extends BaseFrontController {
+
+
+ /**
+ * Folder name for sitemap cache
+ */
+ const SITEMAP_DIR = "sitemap";
+
+ /**
+ * Folder name for sitemap cache
+ */
+ const SITEMAP_FILE = "sitemap";
+
+ /**
+ * @return Response
+ */
+ public function generateAction()
+ {
+
+ /** @var Request $request */
+ $request = $this->getRequest();
+ $flush = $request->query->get("flush", "");
+ $expire = ConfigQuery::read("sitemap_ttl", '7200');
+
+ // check if sitemap already in cache
+ $cacheDir = $this->getCacheDir();
+ $cacheFileURL = $cacheDir . self::SITEMAP_FILE . '.xml';
+ $expire = intval($expire) ?: 7200;
+ $cacheContent = null;
+
+ if (!($this->checkAdmin() && "" !== $flush)){
+ try {
+ $cacheContent = $this->getCache($cacheFileURL, $expire);
+ } catch (\RuntimeException $ex) {
+ // Problem loading cache, permission errors ?
+ Tlog::getInstance()->addAlert($ex->getMessage());
+ }
+ }
+
+ if (null === $cacheContent){
+ // render the view
+ $cacheContent = $this->renderRaw("sitemap");
+
+ // save cache
+ try {
+ $this->setCache($cacheFileURL, $cacheContent);
+ } catch (\RuntimeException $ex) {
+ // Problem loading cache, permission errors ?
+ Tlog::getInstance()->addAlert($ex->getMessage());
+ }
+
+ }
+
+ $response = new Response();
+ $response->setContent($cacheContent);
+ $response->headers->set('Content-Type', 'application/xml');
+
+ return $response;
+ }
+
+ /**
+ * Check if current user has ADMIN role
+ *
+ * @return bool
+ */
+ protected function checkAdmin(){
+ return $this->getSecurityContext()->isGranted(array("ADMIN"), array(), array(), array());
+ }
+
+ /**
+ * 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`
+ *
+ * @param $fileURL the path to the file
+ * @param $content the content of the file
+ * @throws \RuntimeException
+ */
+ protected function setCache($fileURL, $content)
+ {
+ if (! @file_put_contents($fileURL, $content)){
+ throw new \RuntimeException(sprintf("Failed to save %s file in cache directory", $fileURL));
+ }
+ }
+
+ /**
+ * 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;
+ }
+
+}
\ No newline at end of file
diff --git a/setup/insert.sql b/setup/insert.sql
index a8a1bb212..e751f8805 100644
--- a/setup/insert.sql
+++ b/setup/insert.sql
@@ -45,7 +45,8 @@ INSERT INTO `config` (`name`, `value`, `secured`, `hidden`, `created_at`, `updat
('thelia_release_version','1', 1, 1, NOW(), NOW()),
('thelia_extra_version','', 1, 1, NOW(), NOW()),
('front_cart_country_cookie_name','fcccn', 1, 1, NOW(), NOW()),
-('front_cart_country_cookie_expires','2592000', 1, 1, NOW(), NOW());
+('front_cart_country_cookie_expires','2592000', 1, 1, NOW(), NOW()),
+('sitemap_ttl','7200', 1, 1, NOW(), NOW());
INSERT INTO `config_i18n` (`id`, `locale`, `title`, `description`, `chapo`, `postscriptum`) VALUES
diff --git a/setup/update/2.0.1.sql b/setup/update/2.0.1.sql
index cc368353a..7b91f3232 100644
--- a/setup/update/2.0.1.sql
+++ b/setup/update/2.0.1.sql
@@ -10,6 +10,8 @@ INSERT INTO `config` (`name`, `value`, `secured`, `hidden`, `created_at`, `updat
('front_cart_country_cookie_name','fcccn', 1, 1, NOW(), NOW());
INSERT INTO `config` (`name`, `value`, `secured`, `hidden`, `created_at`, `updated_at`) VALUES
('front_cart_country_cookie_expires','2592000', 1, 1, NOW(), NOW());
+INSERT INTO `config` (`name`, `value`, `secured`, `hidden`, `created_at`, `updated_at`) VALUES
+('sitemap_ttl','7200', 1, 1, NOW(), NOW());
ALTER TABLE `module` ADD INDEX `idx_module_activate` (`activate`);
diff --git a/templates/frontOffice/default/sitemap.html b/templates/frontOffice/default/sitemap.html
new file mode 100644
index 000000000..75d43920f
--- /dev/null
+++ b/templates/frontOffice/default/sitemap.html
@@ -0,0 +1,47 @@
+
+
+
+ {url path="/"}
+
+
+{loop type="lang" name="category_lang"}
+{loop type="category" name="category" lang="$ID"}
+
+ {$URL}
+ {format_date date=$UPDATE_DATE format="c"}
+ {*
+ You can also set priority and changefreq
+ 0.8
+ weekly
+ *}
+
+{/loop}
+{/loop}
+
+{loop type="lang" name="product_lang"}
+{loop type="product" name="product" lang="$ID"}
+
+ {$URL}
+ {format_date date=$UPDATE_DATE format="c"}
+
+{/loop}
+{/loop}
+
+{loop type="lang" name="folder_lang"}
+{loop type="folder" name="folder" lang="$ID"}
+
+ {$URL}
+ {format_date date=$UPDATE_DATE format="c"}
+
+{/loop}
+{/loop}
+
+{loop type="lang" name="content_lang"}
+{loop type="content" name="content" lang="$ID"}
+
+ {$URL}
+ {format_date date=$UPDATE_DATE format="c"}
+
+{/loop}
+{/loop}
+
\ No newline at end of file