Sitemap Deluxe
A nice and easy way for automatically generate your sitemaps. Main features: generate html & xml sitemap, send your sitemap to search engines (Google, Yahoo, Ask, Bing), auto generated robot.txt, support for dynamics and statics sections...
First sorry for my bad English...
Here’s my first contribution to CakePHP community: "Sitemap Deluxe 1.0 Beta"
With this you can create Google sitemaps for your Cake application, send your sitemap to common search engines like Google, MSN (Bing), Yahoo, ASK and optionally auto generate your robot.txt file.
Now create the next files and paste code on it:
app/config/routes.php
app/controllers/sitemaps_controller.php
app/views/layouts/xml/default.ctp
app/views/sitemaps/xml/index.ctp
app/views/sitemaps/index.ctp
app/views/sitemaps/send_sitemap.ctp
Download latest Jquery (Minified) version from here:
http://docs.jquery.com/Downloading_jQuery and copy it in your "app/webroot/js" folder.
Now you need to change the sitemaps controller file for adapt it to your needs, add as many statics and dynamics sections as you want.
Next step is check that xml and html sitemaps are working, point your browser to:
http//yoursite.com/sitemap
and
http//yoursite.com/sitemap.xml
If all is ok you can submit your sitemaps to search engines, point your browser to:
http//yoursite.com/send_sitemap
And click in "Send" button on each site for send your sitemap.
All done, now you must comment and tell how worked for you!! :P
Feel free to correct my English and my code.
Here’s my first contribution to CakePHP community: "Sitemap Deluxe 1.0 Beta"
With this you can create Google sitemaps for your Cake application, send your sitemap to common search engines like Google, MSN (Bing), Yahoo, ASK and optionally auto generate your robot.txt file.
Now create the next files and paste code on it:
app/config/routes.php
Router::connect('/sitemap', array('controller' => 'sitemaps', 'action' => 'index'));
Router::connect('/sitemap/:action/*', array('controller' => 'sitemaps'));
// Optional
Router::connect('/robots/:action/*', array('controller' => 'sitemaps', 'action' => 'robot'));
Router::parseExtensions();
app/controllers/sitemaps_controller.php
Controller Class:
<?php
/**
* Sitemap Deluxe v1.0 Beta
*
* by Cristian Deluxe http://www.cristiandeluxe.com // http://blog.cristiandeluxe.com
*
* Licenced by a Creative Commons GNU LGPL license
* http://creativecommons.org/license/cc-lgpl
*
* @copyright Copyright 2008-2009, Cristian Deluxe (http://www.cristiandeluxe.com)
* @link http://bakery.cakephp.org/articles/view/sitemap-deluxe
*/
class SitemapsController extends AppController{
var $name = 'Sitemaps';
var $helpers = array('Time', 'Xml', 'Javascript');
var $components = array('RequestHandler');
var $uses = array();
var $array_dynamic = array();
var $array_static = array();
var $sitemap_url = '/sitemap.xml';
var $yahoo_key = 'insert your yahoo api key here';
/*
* Our sitemap
*/
function index(){
Configure::write('debug', 0);
$this->__get_data();
$this->set('dynamics', $this->array_dynamic);
$this->set('statics', $this->array_static);
if ($this->RequestHandler->accepts('html')) {
$this->RequestHandler->respondAs('html');
} elseif ($this->RequestHandler->accepts('xml')) {
$this->RequestHandler->respondAs('xml');
}
}
/*
* Action for send sitemaps to search engines
*/
function send_sitemap() {
// This action must be only for admins
}
/*
* This make a simple robot.txt file use it if you don't have your own
*/
function robot() {
Configure::write('debug', 0);
$expire = 25920000;
header('Date: ' . date("D, j M Y G:i:s ", time()) . ' GMT');
header('Expires: ' . gmdate("D, d M Y H:i:s", time() + $expire) . ' GMT');
header('Content-Type: text/plain');
header('Cache-Control: max-age='.$expire.', s-maxage='.$expire.', must-revalidate, proxy-revalidate');
header('Pragma: nocache');
echo 'User-Agent: *'."\n".'Allow: /'."\n".'Sitemap: ' . FULL_BASE_URL . $this->sitemap_url;
exit();
}
/*
* Here must be all our public controllers and actions
*/
function __get_data() {
ClassRegistry::init('Post')->recursive = false;
$this->__add_dynamic_section(
'Post',
ClassRegistry::init('Post')->find('all', array('fields' => array('slug', 'modified', 'title'))),
array(
'controllertitle' => 'My Posts',
'fields' => array('id' => 'slug'),
'changefreq' => 'daily',
'pr' => '1.0',
'url' => array('controller' => 'posts', 'action' => 'view')
)
);
$this->__add_static_section(
'Contact Form',
array('controller' => 'contact', 'action' => 'index'),
array(
'changefreq' => 'yearly',
'pr' => '0.4'
)
);
ClassRegistry::init('Gallery')->recursive = false;
$this->__add_dynamic_section(
'Gallery',
ClassRegistry::init('Gallery')->find('all', array('fields' => array('id', 'name'))),
array(
'controllertitle' => 'My supersite gallery',
'fields' => array('title' => 'name', 'date' => false),
'pr' => '0.7',
'changefreq' => 'weekly',
'url' => array('controller' => 'gallery', 'action'=>'show')
)
);
}
/*
* Add a "static" section
*/
function __add_static_section($title = null, $url = null, $options = null) {
if(is_null($title) || empty($title) || is_null($url) || empty($url) ) {
return false;
}
$defaultoptions = array(
'pr' => '0.5', // Valid values range from 0.0 to 1.0
'changefreq' => 'monthly', // Possible values: always, hourly, daily, weekly, monthly, yearly, never
);
$options = array_merge($defaultoptions, $options);
$this->array_static[] = array(
'title' => $title,
'url' => $url,
'options' => $options
);
}
/*
* Add a section based on data from our database
*/
function __add_dynamic_section($model = null, $data = null, $options = null){
if(is_null($model) || empty($model) || is_null($data) || empty($data) ) {
return false;
}
$defaultoptions = array(
'fields' => array(
'id' => 'id',
'date' => 'modified',
'title' => 'title'
),
'controllertitle' => 'not set',
'pr' => '0.5', // Valid values range from 0.0 to 1.0
'changefreq' => 'monthly', // Possible values: always, hourly, daily, weekly, monthly, yearly, never
'url' => array(
'controller' => false,
'action' => false,
'index' => 'index'
)
);
$options = array_merge($defaultoptions, $options);
$options['fields'] = array_merge($defaultoptions['fields'], $options['fields']);
$options['url'] = array_merge($defaultoptions['url'], $options['url']);
if($options['fields']['date'] == false) {
$options['fields']['date'] = time();
}
$this->array_dynamic[] = array(
'model' => $model,
'options' => $options,
'data' => $data
);
}
/*
* This make a GET petition to search engine url
*/
function __ping_site($url = null, $params = null) {
if(is_null($url) || empty($url) || is_null($params) || empty($params) ) {
return false;
}
App::import('Core', 'HttpSocket');
$HttpSocket = new HttpSocket();
$html = $HttpSocket->get($url, $params);
return $HttpSocket->response;
}
/*
* Show response for ajax based on a boolean result
*/
function __ajaxresponse($result = false){
if(!$result) {
return 'fail';
}
return 'success';
}
/*
* Function for ping Google
*/
function ping_google() {
Configure::write('debug', 0);
$url = 'http://www.google.com/webmasters/tools/ping';
$params = 'sitemap=' . urlencode(FULL_BASE_URL . $this->sitemap_url);
echo $this->__ajaxresponse($this->__check_ok_google( $this->__ping_site($url, $params) ));
exit();
}
/*
* Function for check Google's response
*/
function __check_ok_google($response = null){
if( is_null($response) || !is_array($response) || empty($response) ) {
return false;
}
if(
isset($response['status']['code']) && $response['status']['code'] == '200' &&
isset($response['status']['reason-phrase']) && $response['status']['reason-phrase'] == 'OK' &&
isset($response['body']) && !empty($response['body']) &&
strpos(strtolower($response['body']), "successfully added") != false) {
return true;
}
return false;
}
/*
* Function for ping Ask.com
*/
function ping_ask() { // fail if we are in local environment
Configure::write('debug', 0);
$url = 'http://submissions.ask.com/ping';
$params = 'sitemap=' . urlencode(FULL_BASE_URL . $this->sitemap_url);
echo $this->__ajaxresponse($this->__check_ok_ask( $this->__ping_site($url, $params) ));
exit();
}
/*
* Function for check Ask's response
*/
function __check_ok_ask($response = null){
if( is_null($response) || !is_array($response) || empty($response) ) {
return false;
}
if(
isset($response['status']['code']) && $response['status']['code'] == '200' &&
isset($response['status']['reason-phrase']) && $response['status']['reason-phrase'] == 'OK' &&
isset($response['body']) && !empty($response['body']) &&
strpos(strtolower($response['body']), "has been successfully received and added") != false) {
return true;
}
return false;
}
/*
* Function for ping Yahoo
*/
function ping_yahoo() {
Configure::write('debug', 0);
$url = 'http://search.yahooapis.com/SiteExplorerService/V1/updateNotification';
$params = 'appid='.$this->yahoo_key.'&url=' . urlencode(FULL_BASE_URL . $this->sitemap_url);
echo $this->__ajaxresponse($this->__check_ok_yahoo( $this->__ping_site($url, $params) ));
exit();
}
/*
* Function for check Yahoo's response
*/
function __check_ok_yahoo($response = null){
if( is_null($response) || !is_array($response) || empty($response) ) {
return false;
}
if(
isset($response['status']['code']) && $response['status']['code'] == '200' &&
isset($response['status']['reason-phrase']) && $response['status']['reason-phrase'] == 'OK' &&
isset($response['body']) && !empty($response['body']) &&
strpos(strtolower($response['body']), "successfully submitted") != false) {
return true;
}
return false;
}
/*
* Function for ping Bing
*/
function ping_bing() {
Configure::write('debug', 0);
$url = 'http://www.bing.com/webmaster/ping.aspx';
$params = '&siteMap=' . urlencode(FULL_BASE_URL . $this->sitemap_url);
echo $this->__ajaxresponse($this->__check_ok_bing( $this->__ping_site($url, $params) ));
exit();
}
/*
* Function for check Bing's response
*/
function __check_ok_bing($response = null){
if( is_null($response) || !is_array($response) || empty($response) ) {
return false;
}
if(
isset($response['status']['code']) && $response['status']['code'] == '200' &&
isset($response['status']['reason-phrase']) && $response['status']['reason-phrase'] == 'OK' &&
isset($response['body']) && !empty($response['body']) &&
strpos(strtolower($response['body']), "thanks for submitting your sitemap") != false) {
return true;
}
return false;
}
}
?>
app/views/layouts/xml/default.ctp
View Template:
<?php header('Content-Type: text/xml');
echo $content_for_layout;?>
app/views/sitemaps/xml/index.ctp
View Template:
<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc><?php echo Router::url('/', true); ?></loc>
<lastmod><?php echo trim($time->toAtom(time())); ?></lastmod>
<changefreq>weekly</changefreq>
<priority>1.0</priority>
</url>
<?php
if( isset($statics) && !empty($statics) ):
foreach ($statics as $static):?>
<url>
<loc><?php echo Router::url($static['url'], true); ?></loc>
<lastmod><?php echo trim($time->toAtom(time())); ?></lastmod>
<priority><?php echo $static['options']['pr'] ?></priority>
<changefreq><?php echo $static['options']['changefreq'] ?></changefreq>
</url>
<?php
endforeach;
endif;
if( isset($dynamics) && !empty($dynamics) ):
foreach ($dynamics as $dynamic):?>
<url>
<loc><?php echo Router::url(array(
'controller' => $dynamic['options']['url']['controller'],
'action' => $dynamic['options']['url']['index']
), true); ?></loc>
<lastmod><?php echo trim($time->toAtom(time())); ?></lastmod>
<priority><?php echo $dynamic['options']['pr'] ?></priority>
<changefreq><?php echo $dynamic['options']['changefreq'] ?></changefreq>
</url>
<?php foreach ($dynamic['data'] as $section):?>
<url>
<loc><?php echo Router::url(array(
'controller' => $dynamic['options']['url']['controller'],
'action' => $dynamic['options']['url']['action'],
$section[$dynamic['model']][$dynamic['options']['fields']['id']]
), true); ?></loc>
<lastmod><?php echo trim($time->toAtom($section[$dynamic['model']][$dynamic['options']['fields']['date']]))?></lastmod>
<priority><?php echo $dynamic['options']['pr'] ?></priority>
<changefreq><?php echo $dynamic['options']['changefreq'] ?></changefreq>
</url>
<?php endforeach;
endforeach;
endif; ?>
</urlset>
app/views/sitemaps/index.ctp
View Template:
<?php
$this->pageTitle = 'Sitemap';
?>
<h1>
Sitemap
</h1>
<table cellpadding="0" cellspacing="0">
<?php
if( isset($dynamics) && !empty($dynamics) ):
foreach ($dynamics as $dynamic): ?>
<tr>
<th>
<?php echo $html->link(
$dynamic['options']['controllertitle'],
array(
'controller' => $dynamic['options']['url']['controller'],
'action' => $dynamic['options']['url']['index']
)); ?>
</th>
</tr>
<?php foreach ($dynamic['data'] as $section):?>
<tr>
<td>
> <?php echo $html->link(
$section[$dynamic['model']][$dynamic['options']['fields']['title']],
array(
'controller' => $dynamic['options']['url']['controller'],
'action' => $dynamic['options']['url']['action'],
$section[$dynamic['model']][$dynamic['options']['fields']['id']]
) ); ?>
</td>
</tr>
<?php endforeach;?>
<tr>
<td class="clear"> </td>
</tr>
<?php
endforeach;
endif;
if(isset($statics) && !empty($statics) ):?>
<tr>
<td class="title">
Misc
</td>
</tr>
<?php foreach ($statics as $static): ?>
<tr>
<td>
<?php echo $html->link(
$static['title'],
$static['url']); ?>
</td>
</tr>
<?php endforeach;?>
<tr>
<td class="clear"> </td>
</tr>
<?php endif; ?>
</table>
app/views/sitemaps/send_sitemap.ctp
View Template:
<?php
$this->pageTitle = 'Send SiteMap';
$javascript->link('jquery-1.3.2.min.js', false);
?>
<h1>
Send SiteMap
</h1>
<table cellpadding="0" cellspacing="0">
<tr>
<th>Site</th>
<th>Status</th>
<th>Action</th>
</tr>
<tr>
<td>
Google
</td>
<td>
<div id="results_google">Not Send</div>
</td>
<td>
<?php echo $form->create('Sitemap', array('action' => 'ping_google', 'type' => 'get', 'id' => 'ping_google') );?>
<?php echo $form->end('Send');?>
</td>
</tr>
<tr>
<td>
Ask
</td>
<td>
<div id="results_ask">Not Send</div>
</td>
<td>
<?php echo $form->create('Sitemap', array('action' => 'ping_ask', 'type' => 'get', 'id' => 'ping_ask') );?>
<?php echo $form->end('Send');?>
</td>
</tr>
<tr>
<td>
Yahoo
</td>
<td>
<div id="results_yahoo">Not Send</div>
</td>
<td>
<?php echo $form->create('Sitemap', array('action' => 'ping_yahoo', 'type' => 'get', 'id' => 'ping_yahoo') );?>
<?php echo $form->end('Send');?>
</td>
</tr>
<tr>
<td>
Bing
</td>
<td>
<div id="results_bing">Not Send</div>
</td>
<td>
<?php echo $form->create('Sitemap', array('action' => 'ping_bing', 'type' => 'get', 'id' => 'ping_bing') );?>
<?php echo $form->end('Send');?>
</td>
</tr>
</table>
<script language="javascript" type="text/javascript">
//<![CDATA[
var GoogleForm = 'ping_google';
var GoogleResult = 'results_google';
var AskForm = 'ping_ask';
var AskResult = 'results_ask';
var YahooForm = 'ping_yahoo';
var YahooResult = 'results_yahoo';
var BingForm = 'ping_bing';
var BingResult = 'results_bing';
var msgProgress = 'Sending SiteMap...';
var msgOK = 'Sended and received OK';
var msgFail = 'Error, sitemap not sended';
$(document).ready(function(){
$('#' + GoogleForm).submit(processGoogle);
$('#' + AskForm).submit(processAsk);
$('#' + YahooForm).submit(processYahoo);
$('#' + BingForm).submit(processBing);
});
function showresults(divid, data){
$("#"+divid).html(data);
$("#"+divid).css({width: "0%"}).animate({width: "100%"}, 'slow');
}
function parseresults(data) {
var bgcolor = '900';
var textcolor = 'FFF';
var message = msgFail;
if($.trim(data) == "success") {
var bgcolor = '090';
var textcolor = 'FFF';
var message = msgOK;
}
return '<div style="background:#'+bgcolor+'; color:#'+textcolor+'; padding: 10px;">'+message+'<\/div>';
}
function processGoogle(event){
event.preventDefault();
$("#" + GoogleResult).html(msgProgress);
$.get("<?php echo Router::url(array('action' => 'ping_google'), true); ?>", null, function(data) {
showresults(GoogleResult, parseresults(data));
});
}
function processAsk(event){
event.preventDefault();
$("#" + AskResult).html(msgProgress);
$.get("<?php echo Router::url(array('action' => 'ping_ask'), true); ?>", null, function(data) {
showresults(AskResult, parseresults(data));
});
}
function processYahoo(event){
event.preventDefault();
$("#" + YahooResult).html(msgProgress);
$.get("<?php echo Router::url(array('action' => 'ping_yahoo'), true); ?>", null, function(data) {
showresults(YahooResult, parseresults(data));
});
}
function processBing(event){
event.preventDefault();
$("#" + BingResult).html(msgProgress);
$.get("<?php echo Router::url(array('action' => 'ping_bing'), true); ?>", null, function(data) {
showresults(BingResult, parseresults(data));
});
}
//]]>
</script>
Download latest Jquery (Minified) version from here:
http://docs.jquery.com/Downloading_jQuery and copy it in your "app/webroot/js" folder.
Now you need to change the sitemaps controller file for adapt it to your needs, add as many statics and dynamics sections as you want.
Next step is check that xml and html sitemaps are working, point your browser to:
http//yoursite.com/sitemap
and
http//yoursite.com/sitemap.xml
If all is ok you can submit your sitemaps to search engines, point your browser to:
http//yoursite.com/send_sitemap
And click in "Send" button on each site for send your sitemap.
All done, now you must comment and tell how worked for you!! :P
Feel free to correct my English and my code.








EXCELENT work! it not only saved me some heardaches... but i learned some serious php coding too!
I had two problems
1- } elseif ($this->RequestHandler->accepts('xml')) {
$this->RequestHandler->respondAs('xml');
does not seem to work. it gets the "html" template.
2- robots.txt... router does not seem to work. I get the you should create a controller message with all the urls robots/ robots.txt robots/index...
EXCELENT work! it not only saved me some heardaches... but i learned some serious php coding too!
I had two problems
1- } elseif ($this->RequestHandler->accepts('xml')) {
$this->RequestHandler->respondAs('xml');
does not seem to work. it gets the "html" template.
2- robots.txt... router does not seem to work. I get the you should create a controller message with all the urls robots/ robots.txt robots/index...
i already follow the steps
but
the requested address '/sitemap' was not found on this server.
http://dsi.vozibrale.com/downloads/view/fusion-charts-plugin-for-cakephp it works but the plug-in doesn't work, any solutions?
this can not be for every kind of web site...
it doesn't take care of security holes, bandwidth, etc....
even the code is duplicated many times (for every SE)!
I am thirsty for the alpha version 1.0
</urlset> <script type="text/javascript">
--------------^
i have no idea, where it gets this code from...?
can u help?
thx 4 the nice Sitemap. Just trying to implement it.
I have a question:
What is the pr Param for ?
'changefreq' => 'yearly',
'pr' => '0.4'
=> ok i found out due to working sitemap.xml now, it stands 4 "Priority"!
So i have to give my contents different Prioritys
Thx
ixopo
For other users, here are some of the stumbling points I had.
1: When I first started, it would not recognize the /sitemap url. I thought this was because I needed to make a sitemap model with the 'var $useTable = false;' declaration, but it was because I didn't have the same controllers used in the tutorial which were 'posts' and 'gallery'. Pay attention when the author says, "You need to change to sitemap controller to fit your needs." You'll need to change the __get_data() function (seen here: http://bit.ly/YhCXo ).
2: Also note that in the index() function, the author put in a debug statement, in part to prevent entity problems with the XML file. (Seen here: http://bit.ly/1eFLu ) Make sure you set the debug up from 0 if you want to see errors when visiting the page. I kept going to mysite.com/sitemap and seeing nothing to help me. (Yeah, a bit of a noob).
3: If you do see problems with the XML file having entity issues with 'nbsp', then make sure the debug in the index() function is set back to 0;
4: Finally, to solve my original quest, if you want to know how many pages are on your site, go to the /views/sitemaps/index.ctp and put a '$count' variable inside the loop that spits out the pages (seen here: http://bit.ly/UOq4b ). Then you can echo out the number of pages on the site ( http://bit.ly/2g1YmF ). The final result: http://bit.ly/Q5u6
5: Oh, one more thing to the author who invited English correction. First of all, thanks very much for your contribution. I admire anybody who speaks two languages. However, on the /view/sitemaps/send_sitemap.ctp page, it should be "Not Sent" instead of "Not Send" and "Sent and received OK" instead of "Sended and received OK" (seen here: http://bit.ly/wmbVH )
1. This tool won't work when site has more than 50000 urls. :-(
2. Usage of find('all') without limit section may cause an error of memory limit exceeding on fetching too many results.
Sam, if you made a cached version of this, post it!! i want to see it!! (even implement it :P)
I want to add more search engines for next version.
Comments and code's enhances are always welcome...
Thanks again
I have adapted it a bit last night for my own circumstances; this may be the cause of the bit not being cached but apart from taht everything of looking real smart in my initial trial of this
Comments are closed for articles over a year old