<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xml:base="https://www.stress-free.co.nz"  xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
 <title>stressfree - cms</title>
 <link>https://www.stress-free.co.nz/tech/cms</link>
 <description></description>
 <language>en</language>
<item>
 <title>Synchronise two SilverStripe CMS instances</title>
 <link>https://www.stress-free.co.nz/synchronise_two_silverstripe_cms_instances</link>
 <description>
  &lt;div class=&quot;field-body&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;img src=&quot;https://www.stress-free.co.nz/sites/default/files/u63/silverstripe-logo.png&quot; alt=&quot;&quot; width=&quot;100&quot; height=&quot;97&quot; /&gt;&lt;/div&gt;&lt;p&gt;This script allows you to operate two distinct, fully functional SilverStripe instances and have the content of one synchronised with the other. When running a content management system in a corporate environment it is useful to have an internal &#039;development&#039; site and a public &#039;production&#039; site. SilverStripe has a couple of caching options, namely &lt;a href=&quot;http://doc.silverstripe.org/doku.php?id=staticpublisher&quot;&gt;StaticPublisher&lt;/a&gt; and &lt;a href=&quot;http://doc.silverstripe.org/doku.php?id=staticexporter&quot;&gt;StaticExporter&lt;/a&gt;, but these generate static HTML files that cannot be easily modified by content editors.&lt;/p&gt;&lt;p&gt;This approach allows the development and production SilverStripe servers to be easily synchronised, but in between times, content editors are free to make different changes at each end. This is useful when internally the content of the website is undergoing significant change, but during this time the production website content must be &#039;maintained&#039;.&lt;br /&gt;i.e. You are not forced to &#039;freeze&#039; your production website, or push internal changes out before they have been properly vetted.&lt;/p&gt;&lt;p&gt;The script copies the local SilverStripe MySQL database (sans page revisions) to the production site and synchronises the assets/Uploads directory.&lt;br /&gt;&lt;strong&gt;Note:&lt;/strong&gt; Page revisions are not sent to the production site because this takes a significant amount of time and bandwidth. Considering these revisions are stored on the internal development server, storing them in both locations is not necessary.&lt;/p&gt;&lt;!--break--&gt;&lt;p&gt;A flow diagram of the actions that take place during this synchronisation process is provided below.&lt;/p&gt;&lt;div class=&quot;centeredimage&quot;&gt;&lt;a href=&quot;https://www.stress-free.co.nz/sites/default/files/u63/sssync-flow-lg.png&quot;&gt;&lt;img src=&quot;https://www.stress-free.co.nz/sites/default/files/u63/sssync-flow-sm.png&quot; alt=&quot;&quot; width=&quot;400&quot; height=&quot;560&quot; /&gt;&lt;br /&gt;Click to enlarge&lt;/a&gt;&lt;/div&gt; &lt;p&gt;This script assumes that SilverStripe&#039;s StaticPublisher caching mechanism &lt;strong&gt;is enabled on both local and remote sites&lt;/strong&gt;, otherwise the sync process will fail.&lt;/p&gt;&lt;h3&gt;Requirements&lt;/h3&gt; &lt;ul&gt;&lt;li&gt;The script requires SSH, MySQL and RSync, awk, and grep on both servers to function correctly.&lt;/li&gt;&lt;li&gt;For the email notifications to work, either sendmail or postfix will need to be running locally so that the mail command can deliver notifications.&lt;/li&gt;&lt;li&gt;For the script run without SSH prompting for passwords, key-based authentication between the two servers will need to be configured. (The key should not have a password.)&lt;/li&gt;&lt;li&gt;The local MySQL user needs to be able to access two databases:&lt;ul&gt;&lt;li&gt;Read-only access to the local SilverStripe database.&lt;/li&gt;&lt;li&gt;All permissions to an empty database where the script can make a copy of the SilverStripe database and strip the page revisions from.&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h3&gt;Configuration&lt;/h3&gt;&lt;p&gt;The sssync.sh script pulls configuration information from a supplied config file. Below is an example configuration file that lists the various options that should be tuned to your environment.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;config&lt;/strong&gt;&lt;/p&gt;&lt;p class=&quot;codesnippet&quot;&gt;# The local directory where the SilverStripe website is installed&lt;br /&gt;Local_SilverStripe_Directory /var/www&lt;br /&gt;&lt;br /&gt;# The local temp directory which the script has write access to&lt;br /&gt;Local_Temp_Directory /tmp&lt;br /&gt;&lt;br /&gt;# The local MySQL user (must have write permissions to temp database)&lt;br /&gt;Local_MySQL_User localuser&lt;br /&gt;&lt;br /&gt;# The local MySQL password&lt;br /&gt;Local_MySQL_Password localpassword&lt;br /&gt;&lt;br /&gt;# The local MySQL hostname&lt;br /&gt;Local_MySQL_Host localhost&lt;br /&gt;&lt;br /&gt;# The local MySQL port&lt;br /&gt;Local_MySQL_Port 3306&lt;br /&gt;&lt;br /&gt;# The local (primary) MySQL database&lt;br /&gt;Local_MySQL_Database silverstripe&lt;br /&gt;&lt;br /&gt;# The local temporary database used to store a revisionless version of the site&lt;br /&gt;Local_MySQL_TempDatabase silverstripe_tmp&lt;br /&gt;&lt;br /&gt;# The local user who owns the cache files&lt;br /&gt;Local_User www&lt;br /&gt;&lt;br /&gt;# The local group who owns the cache files&lt;br /&gt;Local_Group www&lt;br /&gt;&lt;br /&gt;# The remote SSH username&lt;br /&gt;Remote_SSH_User remoteuser&lt;br /&gt;&lt;br /&gt;# The remote SSH hostname&lt;br /&gt;Remote_SSH_Host remote.host.name&lt;br /&gt;&lt;br /&gt;# The remote SSH port&lt;br /&gt;Remote_SSH_Port 22&lt;br /&gt;&lt;br /&gt;# The remote directory where the SilverStripe website is installed&lt;br /&gt;Remote_SilverStripe_Directory /var/www&lt;br /&gt;&lt;br /&gt;# The remote directory where backups of the website and database are stored&lt;br /&gt;Remote_Backup_Directory /var/backup/silverstripe&lt;br /&gt;&lt;br /&gt;# The remote MySQL username&lt;br /&gt;Remote_MySQL_User remoteuser&lt;br /&gt;&lt;br /&gt;# The remote MySQL password&lt;br /&gt;Remote_MySQL_Password remotepassword&lt;br /&gt;&lt;br /&gt;# The remote MySQL hostname&lt;br /&gt;Remote_MySQL_Host localhost&lt;br /&gt;&lt;br /&gt;# The remote MySQL port&lt;br /&gt;Remote_MySQL_Port 3306&lt;br /&gt;&lt;br /&gt;# The remote MySQL database&lt;br /&gt;Remote_MySQL_Database silverstripe&lt;br /&gt;&lt;br /&gt;# The remote user who owns the cache files&lt;br /&gt;Remote_User www&lt;br /&gt;&lt;br /&gt;# The remote group who owns the cache files&lt;br /&gt;Remote_Group www&lt;br /&gt;&lt;br /&gt;# The email address(es) of recipients for sssync email&lt;br /&gt;Recipient_Email_Address notify@user&lt;br /&gt;&lt;br /&gt;# The sssync from email address&lt;br /&gt;From_Email_Address sssync@domain.com&lt;br /&gt;&lt;br /&gt;# The SMTP server (assumes the Heirloom Mailx utility is used)&lt;br /&gt;SMTP_Server smtp.server.com&lt;/p&gt;&lt;h3&gt;The sssync.sh script&lt;/h3&gt; &lt;p&gt;The sssync.sh script performs all the described synchronisation functions. Copy and paste the following into a file on your local server named sssync.sh. Make sure you mark it as executable (chmod 777).&lt;/p&gt;&lt;p&gt;&lt;strong&gt;sssync.sh&lt;/strong&gt; (this file can be downloaded &lt;a href=&quot;/sites/default/files/u63/sssync.sh&quot;&gt;from here&lt;/a&gt;)&lt;strong&gt;&lt;br /&gt;&lt;/strong&gt;&lt;/p&gt;&lt;p class=&quot;codesnippet&quot;&gt;#!/bin/sh&lt;br /&gt;#&lt;br /&gt;#   This program is free software: you can redistribute it and/or modify&lt;br /&gt;#   it under the terms of the GNU General Public License as published by&lt;br /&gt;#   the Free Software Foundation, either version 3 of the License, or&lt;br /&gt;#   (at your option) any later version.&lt;br /&gt;#&lt;br /&gt;#   This program is distributed in the hope that it will be useful,&lt;br /&gt;#   but WITHOUT ANY WARRANTY; without even the implied warranty of&lt;br /&gt;#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the&lt;br /&gt;#   GNU General Public License for more details.&lt;br /&gt;#&lt;br /&gt;#   A copy of the GNU General Public License is available at&lt;br /&gt;#   &amp;lt;http://www.gnu.org/licenses/&amp;gt;.&lt;br /&gt;#&lt;br /&gt;#&lt;br /&gt;###########################################################&lt;br /&gt;#           sssync - SilverStripe Site Sync               #&lt;br /&gt;###########################################################&lt;br /&gt;#&lt;br /&gt;# Author: David Harrison&lt;br /&gt;# Date: 9 December 2009&lt;br /&gt;#&lt;br /&gt;# This script synchronises a remote SilverStripe installation&lt;br /&gt;# with a local copy. It is assumed that SilverStripe&#039;s caching&lt;br /&gt;# mechanism is enabled.&lt;br /&gt;# For SSH authentication to occur without a password prompt,&lt;br /&gt;# SSH keys should be generated to allow password-less login.&lt;br /&gt;#&lt;br /&gt;# ---------------------------------------------------------&lt;br /&gt;&lt;br /&gt;#############################################&lt;br /&gt;# Variables pulled from the supplied config #&lt;br /&gt;#############################################&lt;br /&gt;&lt;br /&gt;# Local website directory&lt;br /&gt;localSSDir=`awk &#039;/^Local_SilverStripe_Directory/{print $2}&#039; $1`&lt;br /&gt;# Local temp directory&lt;br /&gt;tempDir=`awk &#039;/^Local_Temp_Directory/{print $2}&#039; $1`&lt;br /&gt;# Local MySQL configuration&lt;br /&gt;localMySQLUser=`awk &#039;/^Local_MySQL_User/{print $2}&#039; $1`&lt;br /&gt;localMySQLPassword=`awk &#039;/^Local_MySQL_Password/{print $2}&#039; $1`&lt;br /&gt;localMySQLHost=`awk &#039;/^Local_MySQL_Host/{print $2}&#039; $1`&lt;br /&gt;localMySQLPort=`awk &#039;/^Local_MySQL_Port/{print $2}&#039; $1`&lt;br /&gt;localMySQLDatabase=`awk &#039;/^Local_MySQL_Database/{print $2}&#039; $1`&lt;br /&gt;localMySQLTempDatabase=`awk &#039;/^Local_MySQL_TempDatabase/{print $2}&#039; $1`&lt;br /&gt;&lt;br /&gt;# Remote SSH configuration&lt;br /&gt;remoteSSHUser=`awk &#039;/^Remote_SSH_User/{print $2}&#039; $1` &lt;br /&gt;remoteSSHHost=`awk &#039;/^Remote_SSH_Host/{print $2}&#039; $1` &lt;br /&gt;remoteSSHPort=`awk &#039;/^Remote_SSH_Port/{print $2}&#039; $1`&lt;br /&gt;# Remote directories&lt;br /&gt;remoteSSDir=`awk &#039;/^Remote_SilverStripe_Directory/{print $2}&#039; $1`&lt;br /&gt;remoteBackupDir=`awk &#039;/^Remote_Backup_Directory/{print $2}&#039; $1`&lt;br /&gt;# Remote MySQL configuration&lt;br /&gt;remoteMySQLUser=`awk &#039;/^Remote_MySQL_User/{print $2}&#039; $1`&lt;br /&gt;remoteMySQLPassword=`awk &#039;/^Remote_MySQL_Password/{print $2}&#039; $1`&lt;br /&gt;remoteMySQLHost=`awk &#039;/^Remote_MySQL_Host/{print $2}&#039; $1`&lt;br /&gt;remoteMySQLPort=`awk &#039;/^Remote_MySQL_Port/{print $2}&#039; $1`&lt;br /&gt;remoteMySQLDatabase=`awk &#039;/^Remote_MySQL_Database/{print $2}&#039; $1`&lt;br /&gt;# The email options - email requires sendmail or postfix running locally&lt;br /&gt;emailRecipient=`awk &#039;/^Recipient_Email_Address/{print $2}&#039; $1`&lt;br /&gt;fromAddress=`awk &#039;/^From_Email_Address/{print $2}&#039; $1`&lt;br /&gt;smtpServer=`awk &#039;/^SMTP_Server/{print $2}&#039; $1`&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;# The sendEmail function delivers an email notification.&lt;br /&gt;# This function assumes the Heerloom mailx utility is installed on the system.&lt;br /&gt;# It takes the following parameters:&lt;br /&gt;#  1- Subject&lt;br /&gt;#  2- Message&lt;br /&gt;sendEmail() {&lt;br /&gt;   echo &quot;Sending email to $emailRecipient:&quot;&lt;br /&gt;   echo &quot;  Subject - ${1}&quot;&lt;br /&gt;   echo &quot;  Message - ${2}&quot;&lt;br /&gt;   echo ${2} | mail -s &quot;${1}&quot; -S &quot;smtp=$smtpServer&quot; -r $fromAddress $emailRecipient&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;# The buildStripVersionsSQL function constructs a temporary SQL file that contains&lt;br /&gt;# commands for removing the revisions from the temp database.&lt;br /&gt;#&lt;br /&gt;# Note: If you have custom page types include the relevant SQL statements below&lt;br /&gt;buildStripVersionsSQL() {&lt;br /&gt;   echo &quot;DELETE FROM ErrorPage_versions;&quot; &amp;gt; ${tempDir}/sssync.sql&lt;br /&gt;   echo &quot;DELETE FROM GhostPage_versions;&quot; &amp;gt;&amp;gt; ${tempDir}/sssync.sql&lt;br /&gt;   echo &quot;DELETE FROM RedirectorPage_versions;&quot; &amp;gt;&amp;gt; ${tempDir}/sssync.sql&lt;br /&gt;   echo &quot;DELETE FROM SiteTree_versions;&quot; &amp;gt;&amp;gt; ${tempDir}/sssync.sql&lt;br /&gt;   echo &quot;DELETE FROM VirtualPage_versions;&quot; &amp;gt;&amp;gt; ${tempDir}/sssync.sql&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;# The cleanTemp function removes the temporary error file.&lt;br /&gt;cleanTemp() {&lt;br /&gt;   rm ${tempDir}/sssync.err &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;# The rollBackChanges function restores the file and database backup of the remote website.&lt;br /&gt;rollBackChanges() {&lt;br /&gt;   echo &quot;Rolling back the remote file changes&quot;&lt;br /&gt;   ssh ${remoteSSHUser}@${remoteSSHHost} -p ${remoteSSHPort} \&lt;br /&gt;       &quot;tar -xzf ${remoteBackupDir}/html.tgz -C ${remoteSSDir}&quot;&lt;br /&gt;   echo &quot;Rolling back the remote database changes&quot;&lt;br /&gt;   ssh ${remoteSSHUser}@${remoteSSHHost} -p ${remoteSSHPort} \&lt;br /&gt;       &quot;mysql -u ${remoteMySQLUser} -h ${remoteMySQLHost} -p${remoteMySQLPassword} \&lt;br /&gt;       -P ${remoteMySQLPort} \&lt;br /&gt;       ${remoteMySQLDatabase} &amp;lt; ${remoteBackupDir}/backup.sql&quot;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;echo&lt;br /&gt;echo &quot;--------------------------------------------&quot;&lt;br /&gt;echo &quot;|  SilverStripe Sync process initiated   |&quot;&lt;br /&gt;echo &quot;--------------------------------------------&quot;&lt;br /&gt;echo&lt;br /&gt;echo &quot;Local SilverStripe directory: ${localSSDir}&quot;&lt;br /&gt;echo &quot;Temporary directory: ${tempDir}&quot;&lt;br /&gt;echo &quot;--------------------------------------------&quot;&lt;br /&gt;echo&lt;br /&gt;&lt;br /&gt;logger &quot;Initiating sssync script...&quot;&lt;br /&gt;&lt;br /&gt;echo &quot;Rebuilding the local SilverStripe cache&quot;&lt;br /&gt;cd ${localSSDir}&lt;br /&gt;sapphire/sake dev/buildcache flush=1 &amp;gt; ${tempDir}/sssync.err 2&amp;gt;&amp;amp;1&lt;br /&gt;chown -R $localUser:$localGroup cache&lt;br /&gt;localCacheRebuilt=`tail ${tempDir}/sssync.err | grep &quot;== Done! ==&quot;`&lt;br /&gt;cleanTemp&lt;br /&gt;&lt;br /&gt;if [ &quot;${localCacheRebuilt}&quot; != &quot;== Done! ==&quot; ]&lt;br /&gt;then&lt;br /&gt;   echo&lt;br /&gt;   echo &quot;** Error rebuilding the SilverStripe cache - Exiting **&quot;&lt;br /&gt;   echo&lt;br /&gt;   &lt;br /&gt;   sendEmail &quot;Error rebuilding local SilverStripe cache&quot;\&lt;br /&gt;             &quot;There was an error rebuilding the SilverStripe cache. \&lt;br /&gt;             The sync process was not undertaken.&quot;&lt;br /&gt;   exit&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#############################################&lt;br /&gt;# Create a local, revisionless SS database  #&lt;br /&gt;#############################################&lt;br /&gt;&lt;br /&gt;echo &quot;Creating a temporary, revisionless database&quot;&lt;br /&gt;&lt;br /&gt;mysqldump -C -u ${localMySQLUser} -p${localMySQLPassword} -h ${localMySQLHost} -P ${localMySQLPort} ${localMySQLDatabase} | \&lt;br /&gt;    mysql -u ${localMySQLUser} -p${localMySQLPassword} -h ${localMySQLHost} -P ${localMySQLPort} ${localMySQLTempDatabase} &amp;gt; ${tempDir}/sssync.err 2&amp;gt;&amp;amp;1&lt;br /&gt;&lt;br /&gt;# Create the SQL file to pass to the temp database&lt;br /&gt;buildStripVersionsSQL&lt;br /&gt;&lt;br /&gt;# Stip the versions from the temp database&lt;br /&gt;mysql -u ${localMySQLUser} -p${localMySQLPassword} -h ${localMySQLHost} -P ${localMySQLPort} ${localMySQLTempDatabase} \&lt;br /&gt;    &amp;lt; ${tempDir}/sssync.sql &amp;gt; ${tempDir}/sssync.err 2&amp;gt;&amp;amp;1&lt;br /&gt;&lt;br /&gt;# Remove the temporary SQL file&lt;br /&gt;rm ${tempDir}/sssync.sql &lt;br /&gt;    &lt;br /&gt;localDBCreated=$(cat ${tempDir}/sssync.err)&lt;br /&gt;cleanTemp&lt;br /&gt;&lt;br /&gt;if [ &quot;${localDBCreated}&quot; != &quot;&quot; ]&lt;br /&gt;then&lt;br /&gt;   echo&lt;br /&gt;   echo &quot;** Error creating a revisionless database - Exiting **&quot;&lt;br /&gt;   echo&lt;br /&gt;   sendEmail &quot;Error creating revisionless database&quot;\&lt;br /&gt;       &quot;There was an error creating a revisionless version of the local database. \&lt;br /&gt;      The sync process was not undertaken.&quot;&lt;br /&gt;   exit&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;#############################################&lt;br /&gt;# Before performing the sync, make a backup #&lt;br /&gt;#############################################&lt;br /&gt;&lt;br /&gt;echo &quot;Moving HTML backup ${remoteBackupDir}/html.tgz to ${remoteBackupDir}/html.tgz.old&quot;&lt;br /&gt;ssh ${remoteSSHUser}@${remoteSSHHost} -p ${remoteSSHPort} \&lt;br /&gt;    &quot;mv ${remoteBackupDir}/html.tgz ${remoteBackupDir}/html.tgz.old&quot;&lt;br /&gt;echo &quot;Moving SQL backup ${remoteBackupDir}/backup.sql to ${remoteBackupDir}/backup.sql.old&quot;&lt;br /&gt;ssh ${remoteSSHUser}@${remoteSSHHost} -p ${remoteSSHPort} \&lt;br /&gt;    &quot;mv ${remoteBackupDir}/backup.sql ${remoteBackupDir}/backup.sql.old&quot;&lt;br /&gt;&lt;br /&gt;echo &quot;Creating backup of the remote website&quot;&lt;br /&gt;remoteBackupMade=`ssh ${remoteSSHUser}@${remoteSSHHost} -p ${remoteSSHPort} \&lt;br /&gt;    &quot;tar -czf ${remoteBackupDir}/html.tgz -C ${remoteSSDir} .&quot;`&lt;br /&gt;&lt;br /&gt;echo &quot;Creating backup of the remote database&quot;&lt;br /&gt;remoteBackupMade=&quot;${remoteBackupMade}`ssh ${remoteSSHUser}@${remoteSSHHost} -p ${remoteSSHPort} \&lt;br /&gt;    &quot;mysqldump -u ${remoteMySQLUser} -h ${remoteMySQLHost} -P ${remoteMySQLPort} -p${remoteMySQLPassword} \&lt;br /&gt;     ${remoteMySQLDatabase} &amp;gt; ${remoteBackupDir}/backup.sql&quot;`&quot;&lt;br /&gt;&lt;br /&gt;if [ &quot;${remoteBackupMade}&quot; != &quot;&quot; ]&lt;br /&gt;then&lt;br /&gt;   echo&lt;br /&gt;   echo &quot;** Error creating remote backup - Exiting **&quot;&lt;br /&gt;   echo&lt;br /&gt;   echo &quot;Moving the old remote backups into place&quot;&lt;br /&gt;   ssh ${remoteSSHUser}@${remoteSSHHost} -p ${remoteSSHPort} \&lt;br /&gt;       &quot;mv ${remoteBackupDir}/html.tgz.old ${remoteBackupDir}/html.tgz&quot;&lt;br /&gt;   ssh ${remoteSSHUser}@${remoteSSHHost} -p ${remoteSSHPort} \&lt;br /&gt;       &quot;mv ${remoteBackupDir}/backup.sql.old ${remoteBackupDir}/backup.sql&quot;&lt;br /&gt;   sendEmail &quot;Error creating remote backup&quot;\&lt;br /&gt;       &quot;There was an error creating a backup of the remote website files or database. \&lt;br /&gt;       The sync process was not undertaken.&quot;&lt;br /&gt;   exit&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;# Variable to hold sync failure flag&lt;br /&gt;syncFailure=&quot;false&quot;&lt;br /&gt;&lt;br /&gt;##############################################&lt;br /&gt;# Perform the synchronisation of file assets #&lt;br /&gt;##############################################&lt;br /&gt;&lt;br /&gt;echo &quot;Synchronising remote website assets/Uploads directory with local copy&quot;&lt;br /&gt;remoteFileSync=`rsync -aqz --delete -e &quot;ssh -p ${remoteSSHPort}&quot; ${localSSDir}/assets/Uploads/ \&lt;br /&gt;    ${remoteSSHUser}@${remoteSSHHost}:${remoteSSDir}/assets/Uploads/`&lt;br /&gt;&lt;br /&gt;if [ &quot;${remoteFileSync}&quot; != &quot;&quot; ]&lt;br /&gt;then&lt;br /&gt;   syncFailure=&quot;true&quot;&lt;br /&gt;   echo&lt;br /&gt;   echo &quot;** Error synchronising website assets/Uploads - Rolling back changes **&quot;&lt;br /&gt;   echo&lt;br /&gt;   &lt;br /&gt;   sendEmail &quot;Error synchronising website assets/Uploads&quot;\&lt;br /&gt;       &quot;There was an error synchronising the remote website&#039;s asset directory. \&lt;br /&gt;       The sync process was rolled back.&quot;&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;##############################################&lt;br /&gt;# Synchronise the local and remote databases #&lt;br /&gt;##############################################&lt;br /&gt;&lt;br /&gt;echo &quot;Synchronising the remote database with the local (temp) database&quot;&lt;br /&gt;&lt;br /&gt;mysqldump -C -u ${localMySQLUser} -p${localMySQLPassword} -h ${localMySQLHost} \&lt;br /&gt;    -P ${localMySQLPort} ${localMySQLTempDatabase} | \&lt;br /&gt;    ssh ${remoteSSHUser}@${remoteSSHHost} -p ${remoteSSHPort} \&lt;br /&gt;    &quot;mysql -u ${remoteMySQLUser} -h ${remoteMySQLHost} -p${remoteMySQLPassword} \&lt;br /&gt;    -P ${remoteMySQLPort} ${remoteMySQLDatabase}&quot; &amp;gt; ${tempDir}/sssync.err 2&amp;gt;&amp;amp;1&lt;br /&gt;&lt;br /&gt;remoteMySQLSync=$(cat ${tempDir}/sssync.err)&lt;br /&gt;cleanTemp&lt;br /&gt;&lt;br /&gt;if [ &quot;${remoteMySQLSync}&quot; != &quot;&quot; ]&lt;br /&gt;then&lt;br /&gt;   syncFailure=&quot;true&quot;&lt;br /&gt;   echo&lt;br /&gt;   echo &quot;** Error synchronising the MySQL databases - Rolling back changes **&quot;&lt;br /&gt;   echo&lt;br /&gt;   &lt;br /&gt;   sendEmail &quot;Error synchronising the MySQL databases&quot;\&lt;br /&gt;       &quot;There was an error synchronising the two MySQL databases. \&lt;br /&gt;       The sync process was rolled back.&quot;&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;if [ &quot;${syncFailure}&quot; == &quot;true&quot; ]&lt;br /&gt;then&lt;br /&gt;   echo&lt;br /&gt;   echo &quot;** Error synchronising the SilverStripe site - Rolling back &amp;amp; exiting **&quot;&lt;br /&gt;   echo&lt;br /&gt;   &lt;br /&gt;   # Roll back the file and database changes&lt;br /&gt;   rollBackChanges&lt;br /&gt;   &lt;br /&gt;   sendEmail &quot;Error synchronising the remote website&quot;\&lt;br /&gt;       &quot;There was an error performing the synchronisation process. \&lt;br /&gt;       The sync process was rolled back.&quot;&lt;br /&gt;   exit&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;##############################################&lt;br /&gt;# Rebuild the remote SilverStripe web cache  #&lt;br /&gt;##############################################&lt;br /&gt;&lt;br /&gt;echo &quot;Rebuilding the remote SilverStripe cache&quot;&lt;br /&gt;ssh ${remoteSSHUser}@${remoteSSHHost} -p ${remoteSSHPort} \&lt;br /&gt;    &quot;cd ${remoteSSDir}; sapphire/sake dev/buildcache flush=1&quot; &amp;gt; ${tempDir}/sssync.err 2&amp;gt;&amp;amp;1&lt;br /&gt;ssh ${remoteSSHUser}@${remoteSSHHost} -p ${remoteSSHPort} \&lt;br /&gt;    &quot;chown -R $remoteUser:$remoteGroup ${remoteSSDir}/cache&quot;&lt;br /&gt;remoteCacheRebuilt=`tail ${tempDir}/sssync.err | grep &quot;== Done! ==&quot;`&lt;br /&gt;cleanTemp&lt;br /&gt;&lt;br /&gt;if [ &quot;${remoteCacheRebuilt}&quot; != &quot;== Done! ==&quot; ]&lt;br /&gt;then&lt;br /&gt;   echo&lt;br /&gt;   echo &quot;** Error rebuilding the remote SilverStripe cache - Rolling back changes **&quot;&lt;br /&gt;   echo&lt;br /&gt;   &lt;br /&gt;   # Roll back the file and database changes&lt;br /&gt;   rollBackChanges&lt;br /&gt;   &lt;br /&gt;   sendEmail &quot;Error rebuilding the remote SilverStripe cache&quot;\&lt;br /&gt;             &quot;There was an error rebuilding the remote SilverStripe cache.&quot;&lt;br /&gt;   exit&lt;br /&gt;fi&lt;br /&gt;&lt;br /&gt;sendEmail &quot;SilverStripe was successfully synchronised&quot;\&lt;br /&gt;             &quot;Congratulations, the remote website was synchronised without any issues.&quot;&lt;br /&gt;&lt;br /&gt;logger &quot;sssync script completed&quot;&lt;br /&gt;&lt;br /&gt;echo&lt;br /&gt;echo &quot;------------------------------------------------&quot;&lt;br /&gt;echo &quot;SilverStripe was successfully synchronised&quot;&lt;br /&gt;echo &quot;==============================&quot;&lt;br /&gt;echo&lt;/p&gt; &lt;h3&gt;Running the script&lt;/h3&gt;&lt;p&gt;Assuming your configuration file is in the same directory as the sssync.sh script, run the sync process with the following command:&lt;/p&gt;&lt;p&gt;&lt;strong&gt;./sssync.sh config&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;Assuming the requirements have been met and sync process takes place without error, the following output should be generated:&lt;/p&gt;&lt;p class=&quot;codesnippet&quot;&gt;--------------------------------------------&lt;br /&gt;|  SilverStripe Sync process initiated   |&lt;br /&gt;--------------------------------------------&lt;br /&gt;&lt;br /&gt;Local SilverStripe directory: /var/www&lt;br /&gt;Temporary directory: /var/backup/sssync&lt;br /&gt;------------------------------------------&lt;br /&gt;&lt;br /&gt;Rebuilding the local SilverStripe cache&lt;br /&gt;Creating a temporary, revisionless database&lt;br /&gt;Moving HTML backup /var/backup/silverstripe/html.tgz to /var/backup/silverstripe/html.tgz.old&lt;br /&gt;Moving SQL backup /var/backup/silverstripe/backup.sql to /var/backup/silverstripe/backup.sql.old&lt;br /&gt;Creating backup of the remote website&lt;br /&gt;Creating backup of the remote database&lt;br /&gt;Synchronising remote website assets/Uploads directory with local copy&lt;br /&gt;Synchronising the remote database with the local (temp) database&lt;br /&gt;Rebuilding the remote SilverStripe cache&lt;br /&gt;Sending email to recipient@user.com:&lt;br /&gt;  Subject - SilverStripe was successfully synchronised&lt;br /&gt;  Message - Congratulations, the remote website was synchronised without any issues.&lt;br /&gt;&lt;br /&gt;------------------------------------------------&lt;br /&gt;SilverStripe was successfully synchronised&lt;br /&gt;==============================&lt;/p&gt; &lt;p&gt;It is possible to have multiple configuration files and store them in a different directory to the sssync.sh script. For example:&lt;/p&gt; &lt;p&gt;&lt;strong&gt;./ssync.sh /etc/sssync/production&lt;/strong&gt;&lt;br /&gt;&lt;strong&gt;./ssync.sh /etc/sssync/testing&lt;/strong&gt;&lt;/p&gt; &lt;p&gt;The above commands will execute the sync process using the &quot;production&quot; and &quot;testing&quot; configuration files stored in the /etc/sssync directory.&lt;/p&gt; &lt;h3&gt;Handling error pages&lt;/h3&gt;&lt;p&gt;The sssync.sh script only synchronises the assets/Uploads directory as this is where file and image uploads are stored by default. SilverStripe error pages are stored in the root of the assets directory which is not synchronised. If an error page is changed, make sure it is republished using the SilverStripe admin interface.&lt;/p&gt;  &lt;/div&gt;

&lt;ul class=&quot;field-taxonomy-vocabulary-1&quot;&gt;

      &lt;li&gt;
      &lt;a href=&quot;/tech/cms&quot;&gt;cms&lt;/a&gt;    &lt;/li&gt;
      &lt;li&gt;
      &lt;a href=&quot;/tech/silverstripe&quot;&gt;silverstripe&lt;/a&gt;    &lt;/li&gt;
      &lt;li&gt;
      &lt;a href=&quot;/tech/tutorial&quot;&gt;tutorial&lt;/a&gt;    &lt;/li&gt;
  
&lt;/ul&gt;
</description>
 <pubDate>Wed, 09 Dec 2009 00:40:09 +0000</pubDate>
 <dc:creator>David</dc:creator>
 <guid isPermaLink="false">554 at https://www.stress-free.co.nz</guid>
</item>
<item>
 <title>Integrating Google Site Search into SilverStripe</title>
 <link>https://www.stress-free.co.nz/integrating_google_site_search_into_silverstripe</link>
 <description>
  &lt;div class=&quot;field-body&quot;&gt;
    &lt;div class=&quot;image&quot;&gt;&lt;img src=&quot;/sites/default/files/u63/silverstripe-logo.png&quot; alt=&quot;&quot; width=&quot;100&quot; height=&quot;97&quot; /&gt;&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;http://silverstripe.org/&quot;&gt;SilverStripe&lt;/a&gt; is an excellent, user-friendly content management system but its internal search functionality is, to put it kindly, useless. Fortunately with &lt;a href=&quot;http://www.google.com/sitesearch/&quot;&gt;Google Site Search&lt;/a&gt; you can embed a Google-powered custom search engine into your SilverStripe site. Doing so requires a paid Site Search account, pricing for which starts at  $100/year.&lt;/p&gt;
&lt;p&gt;This tutorial explains how to integrate this Google Site Search XML feed into your SilverStripe site. Doing so has a number of benefits over the &lt;a href=&quot;http://www.google.com/sitesearch/#custom&quot;&gt;standard means of integrating&lt;/a&gt; Site Search, namely:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;No Javascript is required to display results within the SilverStripe site.&lt;/li&gt;
&lt;li&gt;The user is not taken to a separate, Google operated website to view results.&lt;/li&gt;
&lt;li&gt;The look and feel is consistent with the rest of the SilverStripe site.&lt;/li&gt;
&lt;li&gt;Multiple Site Search engines can be integrated into a single SilverStripe site.&lt;/li&gt;
&lt;li&gt;Site Search results pages are integrated into SilverStripe&#039;s management console.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; To integrate Site Search into SilverStripe using the described method a Site Search plan must be purchased as this provides results in XML. The free, advertising supported, Site Search engine does not provide search results in XML and cannot be used.&lt;/p&gt;
&lt;!--break--&gt;
&lt;h2&gt;Loading XML data from an external source&lt;br /&gt;&lt;/h2&gt;
&lt;p&gt;Before the search page can be added to SilverStripe we need a reliable means of loading XML content. This is complicated by the fact many Web hosts disable PHP&#039;s built in &lt;a href=&quot;http://www.php.net/function.fopen&quot;&gt;URL fetcher (fopen)&lt;/a&gt; with the following &lt;strong&gt;php.ini&lt;/strong&gt; directive:&lt;/p&gt;
&lt;p class=&quot;codesnippet&quot;&gt;allow_url_fopen = Off&lt;/p&gt;
&lt;p&gt;Assuming it is installed, the  &lt;a href=&quot;http://curl.haxx.se/&quot;&gt;cURL&lt;/a&gt; can get around this restriction, hence the XmlLoader helper library includes both methods (cURL is used by default in search.php).&lt;/p&gt;
&lt;p&gt;Create a XmlLoader.php file in your SilverStripe&#039;s mysite/code directory with the following contents:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;mysite/code/XmlLoader.php&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;codesnippet&quot;&gt;&amp;lt;?php&lt;br /&gt;class XmlLoader {&lt;br /&gt;&lt;br /&gt; public function pullXml($url, $parameters, $useCurl) {&lt;br /&gt; $urlString = $url.&quot;?&quot;.$this-&amp;gt;buildParamString($parameters);&lt;br /&gt;&lt;br /&gt; if ($useCurl) {&lt;br /&gt; return simplexml_load_string($this-&amp;gt;loadCurlData($urlString));&lt;br /&gt; } else {&lt;br /&gt; return simplexml_load_file($urlString);&lt;br /&gt; }            &lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private function loadCurlData($urlString) {&lt;br /&gt;&lt;br /&gt; if ($urlString == -1) {&lt;br /&gt; echo &quot;No url supplied&amp;lt;br/&amp;gt;&quot;.&quot;/n&quot;;&lt;br /&gt; return(-1);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; $ch = curl_init();&lt;br /&gt; curl_setopt($ch, CURLOPT_URL, $urlString);&lt;br /&gt; curl_setopt($ch, CURLOPT_TIMEOUT, 180);&lt;br /&gt; curl_setopt($ch, CURLOPT_HEADER, 0);&lt;br /&gt; curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);&lt;br /&gt; $data = curl_exec($ch);&lt;br /&gt; curl_close($ch);&lt;br /&gt;&lt;br /&gt; return $data;        &lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private function buildParamString($parameters) {&lt;br /&gt; $urlString = &quot;&quot;;&lt;br /&gt;&lt;br /&gt; foreach ($parameters as $key =&amp;gt; $value) {&lt;br /&gt; $urlString .= urlencode($key).&quot;=&quot;.urlencode($value).&quot;&amp;amp;&quot;;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; if (trim($urlString) != &quot;&quot;) {&lt;br /&gt; $urlString = preg_replace(&quot;/&amp;amp;$/&quot;, &quot;&quot;, $urlString);&lt;br /&gt; return $urlString;    &lt;br /&gt; } else {&lt;br /&gt; return (-1);&lt;br /&gt; }&lt;br /&gt; }    &lt;br /&gt;}&lt;br /&gt;?&amp;gt;&lt;/p&gt;
&lt;p&gt;With the helper library in place to load the XML, it is now time to implement the SilverStripe &quot;search&quot; page type and logic. Create a search.php file in your SilverStripe&#039;s mysite/code directory with the following contents:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;mysite/code/search.php&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;codesnippet&quot;&gt;&amp;lt;?php&lt;br /&gt;&lt;br /&gt; require_once &#039;XmlLoader.php&#039;;&lt;br /&gt;&lt;br /&gt; class Search extends Page {&lt;br /&gt; static $db = array(&lt;br /&gt; &#039;GoogleSearchId&#039; =&amp;gt; &#039;Text&#039;,&lt;br /&gt; &#039;NoResults&#039; =&amp;gt; &#039;HTMLText&#039;,&lt;br /&gt; );&lt;br /&gt; static $has_one = array(&lt;br /&gt; );&lt;br /&gt;&lt;br /&gt; function getCMSFields() {&lt;br /&gt; $fields = parent::getCMSFields();&lt;br /&gt;&lt;br /&gt; $fields-&amp;gt;addFieldToTab(&#039;Root.Content.Main&#039;, new TextField(&lt;br /&gt; &#039;GoogleSearchId&#039;, &#039;Google Site Search ID&#039;), &#039;Content&#039;);&lt;br /&gt; $fields-&amp;gt;addFieldToTab(&#039;Root.Content.Main&#039;, new HtmlEditorField(&lt;br /&gt; &#039;NoResults&#039;, &#039;No results message&#039;), &#039;Content&#039;);&lt;br /&gt;&lt;br /&gt; # Remove the content field&lt;br /&gt; $fields-&amp;gt;removeFieldFromTab(&quot;Root.Content.Main&quot;,&quot;Content&quot;);&lt;br /&gt;&lt;br /&gt; return $fields;&lt;br /&gt; }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; class Search_Controller extends Page_Controller {&lt;br /&gt;&lt;br /&gt; function SearchForm() {&lt;br /&gt; $input = array_merge($_GET, $_POST);&lt;br /&gt; $query = $input[&#039;q&#039;];&lt;br /&gt;&lt;br /&gt; $output = &quot;&amp;lt;form class=\&quot;search\&quot; action=\&quot;/search/results\&quot;&amp;gt;&amp;lt;fieldset&amp;gt;&quot;;&lt;br /&gt; $output .= &quot;&amp;lt;input type=\&quot;text\&quot; size=\&quot;40\&quot; name=\&quot;q\&quot; value=\&quot;$query\&quot;/&amp;gt;&quot;;&lt;br /&gt; $output .= &quot;&amp;lt;input type=\&quot;hidden\&quot; name=\&quot;p\&quot; value=\&quot;1\&quot;/&amp;gt;&quot;;&lt;br /&gt; $output .= &quot;&amp;lt;input type=\&quot;submit\&quot; value=\&quot;Search\&quot;/&amp;gt;&quot;;&lt;br /&gt; $output .= &quot;&amp;lt;/fieldset&amp;gt;&amp;lt;/form&amp;gt;&quot;;&lt;br /&gt;&lt;br /&gt; return $output;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; function SearchResults() {&lt;br /&gt;&lt;br /&gt; $output = &quot;&quot;;&lt;br /&gt;&lt;br /&gt; $input = array_merge($_GET, $_POST);&lt;br /&gt; $page = isset($input[&#039;p&#039;]) ? $input[&#039;p&#039;] : &#039;1&#039;;&lt;br /&gt; $query = $input[&#039;q&#039;];&lt;br /&gt;&lt;br /&gt; $perPage = 10;&lt;br /&gt; if ($page &amp;lt; 1) { $page = 1; }&lt;br /&gt;&lt;br /&gt; $xml = $this-&amp;gt;getGoogleSearchResults($this-&amp;gt;GoogleSearchId, $perPage, $page, $query);&lt;br /&gt; $results = $this-&amp;gt;parseGoogleSearchResults($xml);&lt;br /&gt;&lt;br /&gt; $totalResults = $this-&amp;gt;getResultCount($xml);&lt;br /&gt;&lt;br /&gt; $output .= $this-&amp;gt;getFormattedResults($results);&lt;br /&gt;&lt;br /&gt; if (count($results) == 0) {&lt;br /&gt; // Show no results message&lt;br /&gt; $output .= $this-&amp;gt;NoResults;;&lt;br /&gt; } else {&lt;br /&gt; // Append paging&lt;br /&gt; $output .= $this-&amp;gt;getPagingForResults($totalResults, $query, $perPage, $page);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; return $output;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt; private function getGoogleSearchResults($googleId, $perPage, $page, $query) {&lt;br /&gt;&lt;br /&gt; $startingRecord = ($page - 1) * $perPage;&lt;br /&gt;&lt;br /&gt; $url = &quot;http://www.google.com/search&quot;;&lt;br /&gt; $parameters = array();&lt;br /&gt; $parameters[&quot;client&quot;] = &quot;google-csbe&quot;;&lt;br /&gt; $parameters[&quot;output&quot;] = &quot;xml_no_dtd&quot;;&lt;br /&gt; $parameters[&quot;num&quot;] = $perPage;&lt;br /&gt; $parameters[&quot;cx&quot;] = $googleId;&lt;br /&gt; $parameters[&quot;start&quot;] = $startingRecord;&lt;br /&gt; $parameters[&quot;q&quot;] = $query;&lt;br /&gt;&lt;br /&gt; $XmlLoader = new XmlLoader();&lt;br /&gt;&lt;br /&gt; return $XmlLoader-&amp;gt;pullXml($url, $parameters, true);&lt;br /&gt;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private function parseGoogleSearchResults($xml) {&lt;br /&gt;&lt;br /&gt; $results = array();&lt;br /&gt;&lt;br /&gt; $attr[&quot;title&quot;] = $xml-&amp;gt;xpath(&quot;/GSP/RES/R/T&quot;);&lt;br /&gt; $attr[&quot;url&quot;] = $xml-&amp;gt;xpath(&quot;/GSP/RES/R/U&quot;);&lt;br /&gt; $attr[&quot;desc&quot;] = $xml-&amp;gt;xpath(&quot;/GSP/RES/R/S&quot;);&lt;br /&gt;&lt;br /&gt; foreach($attr as $key =&amp;gt; $attribute) {&lt;br /&gt; $i = 0;&lt;br /&gt; foreach($attribute as $element) {&lt;br /&gt; $results[$i][$key] = (string)$element;&lt;br /&gt; $i++;&lt;br /&gt; }&lt;br /&gt; }&lt;br /&gt; return $results;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private function getFormattedResults($results) {&lt;br /&gt;&lt;br /&gt; $output = &quot;&quot;;&lt;br /&gt;&lt;br /&gt; if (count($results) &amp;gt; 0) {&lt;br /&gt; $output .= &quot;&amp;lt;ul class=\&quot;results\&quot;&amp;gt;&quot;;&lt;br /&gt; foreach($results as $i =&amp;gt; $result) {&lt;br /&gt; $title = &quot;&quot;;&lt;br /&gt; $url = &quot;&quot;;&lt;br /&gt; $desc = &quot;&quot;;&lt;br /&gt; foreach($result as $key =&amp;gt; $value) {&lt;br /&gt; if ($key == &quot;title&quot;) {&lt;br /&gt; $title = $value;&lt;br /&gt; }&lt;br /&gt; if ($key == &quot;url&quot;) {&lt;br /&gt; $url = $value;&lt;br /&gt; }&lt;br /&gt; if ($key == &quot;desc&quot;) {&lt;br /&gt; $desc = $value;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; $output .= &quot;&amp;lt;li&amp;gt;&amp;lt;a href=\&quot;$url\&quot;&amp;gt;$title&amp;lt;/a&amp;gt;&amp;lt;p&amp;gt;&quot;;&lt;br /&gt; $output .= str_replace(&quot;&amp;lt;br&amp;gt;&quot;, &quot;&amp;lt;br/&amp;gt;&quot;, $desc);&lt;br /&gt; $output .= &quot;&amp;lt;/p&amp;gt;&amp;lt;/li&amp;gt;\n&quot;;&lt;br /&gt; }&lt;br /&gt; $output .= &quot;&amp;lt;/ul&amp;gt;&quot;;&lt;br /&gt; }&lt;br /&gt; return $output;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private function getResultCount($xml) {&lt;br /&gt;&lt;br /&gt; $totalResults = 0;&lt;br /&gt; $count = $xml-&amp;gt;xpath(&quot;/GSP/RES/M&quot;);&lt;br /&gt; foreach($count as $value) {&lt;br /&gt; $totalResults = $value;&lt;br /&gt; }&lt;br /&gt; return $totalResults;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; private function getPagingForResults($totalResults, $query, $perPage, $page) {&lt;br /&gt;&lt;br /&gt; $maxPage = ceil($totalResults/$perPage);&lt;br /&gt;&lt;br /&gt; if ($totalResults &amp;gt; 1) {&lt;br /&gt; $output = &quot;&amp;lt;div class=\&quot;searchPaging\&quot;&amp;gt;&amp;lt;p&amp;gt;&quot;;&lt;br /&gt;&lt;br /&gt; for($pageNum = 1; $pageNum &amp;lt;= $maxPage; $pageNum++) {&lt;br /&gt; if ($pageNum == $page) {&lt;br /&gt; $output .= &quot; &amp;lt;strong&amp;gt;$pageNum&amp;lt;/strong&amp;gt; &quot;;&lt;br /&gt; } else {&lt;br /&gt; $output .= &quot; &amp;lt;a href=\&quot;&quot;.$this-&amp;gt;AbsoluteLink().&quot;results?q=$query&amp;amp;p=$pageNum\&quot;&amp;gt;$pageNum&amp;lt;/a&amp;gt; &quot;;&lt;br /&gt; }&lt;br /&gt; }&lt;br /&gt; $output .= &quot;&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&quot;;&lt;br /&gt; }&lt;br /&gt; return $output;&lt;br /&gt; }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; ?&amp;gt;&lt;/p&gt;
&lt;p&gt;This file defines a search page type with two fields, a Google Search Id and an HTML field that is displayed if no search results are found. As this page does not have any content of its own the default SilverStripe content field is also disabled to avoid confusion.&lt;/p&gt;
&lt;p&gt;With the backend logic in place it is time to implement the templates. The templates themselves will vary from site to site, but the examples given are good starting points. There are two templates, one which simply displays the search box and a second that displays the results.&lt;/p&gt;
&lt;p&gt;Create a Search.ss file in your SilverStripe&#039;s mysite/templates/Layout directory with the following contents:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;mysite/templates/Layout/Search.ss&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;codesnippet&quot;&gt;&amp;lt;% if Menu(2) %&amp;gt;&lt;br /&gt; &amp;lt;div class=&quot;pageWithMenu&quot;&amp;gt;&lt;br /&gt; &amp;lt;% end_if %&amp;gt;&lt;br /&gt; &amp;lt;div class=&quot;page&quot;&amp;gt;&lt;br /&gt; &amp;lt;% if Menu(2) %&amp;gt;&lt;br /&gt; &amp;lt;div class=&quot;content contentStandard&quot;&amp;gt;&lt;br /&gt; &amp;lt;% else %&amp;gt;&lt;br /&gt; &amp;lt;div class=&quot;content contentFull&quot;&amp;gt;&lt;br /&gt; &amp;lt;% end_if %&amp;gt;&lt;br /&gt; &amp;lt;h1&amp;gt;$Title&amp;lt;/h1&amp;gt;&lt;br /&gt; &amp;lt;div class=&quot;contentWrapper&quot;&amp;gt;&lt;br /&gt; $SearchForm&lt;br /&gt; &amp;lt;div class=&quot;clear&quot;&amp;gt;&amp;lt;!-- --&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt; &amp;lt;/div&amp;gt;&lt;br /&gt; &amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;% if Menu(2) %&amp;gt;&lt;br /&gt; &amp;lt;div id=&quot;sidepanel&quot;&amp;gt;&lt;br /&gt; &amp;lt;% include SideBar %&amp;gt;&lt;br /&gt; &amp;lt;/div&amp;gt;&lt;br /&gt; &amp;lt;div class=&quot;clear&quot;&amp;gt;&amp;lt;!-- --&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt; &amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;% end_if %&amp;gt;&lt;/p&gt;
&lt;p&gt;Now create the results page named Search_results.ss in your SilverStripe&#039;s mysite/templates/Layout directory with the following contents:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;mysite/templates/Layout/Search_results.ss&lt;/strong&gt;&lt;/p&gt;
&lt;p class=&quot;codesnippet&quot;&gt;&amp;lt;% if Menu(2) %&amp;gt;&lt;br /&gt; &amp;lt;div class=&quot;pageWithMenu&quot;&amp;gt;&lt;br /&gt; &amp;lt;% end_if %&amp;gt;&lt;br /&gt; &amp;lt;div class=&quot;page&quot;&amp;gt;&lt;br /&gt; &amp;lt;% if Menu(2) %&amp;gt;&lt;br /&gt; &amp;lt;div class=&quot;content contentStandard&quot;&amp;gt;&lt;br /&gt; &amp;lt;% else %&amp;gt;&lt;br /&gt; &amp;lt;div class=&quot;content contentFull&quot;&amp;gt;&lt;br /&gt; &amp;lt;% end_if %&amp;gt;&lt;br /&gt; &amp;lt;h1&amp;gt;$Title&amp;lt;/h1&amp;gt;&lt;br /&gt; &amp;lt;div class=&quot;contentWrapper&quot;&amp;gt;&lt;br /&gt; $SearchForm&lt;br /&gt; &amp;lt;div id=&quot;searchResults&quot;&amp;gt;&lt;br /&gt; $SearchResults&lt;br /&gt; &amp;lt;/div&amp;gt;&lt;br /&gt; &amp;lt;/div&amp;gt;&lt;br /&gt; &amp;lt;div class=&quot;clear&quot;&amp;gt;&amp;lt;!-- --&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt; &amp;lt;/div&amp;gt;&lt;br /&gt; &amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;% if Menu(2) %&amp;gt;&lt;br /&gt; &amp;lt;div id=&quot;sidepanel&quot;&amp;gt;&lt;br /&gt; &amp;lt;% include SideBar %&amp;gt;&lt;br /&gt; &amp;lt;/div&amp;gt;&lt;br /&gt; &amp;lt;div class=&quot;clear&quot;&amp;gt;&amp;lt;!-- --&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt; &amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt; &amp;lt;% end_if %&amp;gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The content of these two files will vary depending on your site. In the above example a SideBar include file is used to load the secondary menu.&lt;/p&gt;
&lt;p&gt;With the backend logic and template files in place it is time to rebuild the SilverStripe database so that the new page type can be recognised. Enter the following (modified) URL into your browser: http://yourwebsite/dev/build?flush=all&lt;/p&gt;
&lt;p&gt;All going well the rebuild command will execute correctly. If it does browse to the administration section of your site and create a &#039;search&#039; page type.&lt;/p&gt;
&lt;div class=&quot;centeredimage&quot;&gt;&lt;img src=&quot;/sites/default/files/u63/silverstripe-google-create.jpg&quot; alt=&quot;&quot; width=&quot;400&quot; height=&quot;247&quot; /&gt;&lt;br /&gt;The search page type in the create menu&lt;/div&gt;
&lt;p&gt;On the search page enter your relevant Google Search Id and Results Not Found message. For the page URL use /search as this is hard coded into the search.php file. It is possible to change this URL (or use a dynamic one) but for the purposes of this tutorial it is not necessary.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You can get your Google Search Id from the Google Search administration console, or it can be found within the embed URL used in the Javascript or external search forms.&lt;/p&gt;
&lt;div class=&quot;centeredimage&quot;&gt;&lt;a href=&quot;/sites/default/files/u63/silverstripe-google-edit_lg.jpg&quot;&gt;&lt;img src=&quot;/sites/default/files/u63/silverstripe-google-edit_sm.jpg&quot; alt=&quot;&quot; width=&quot;400&quot; height=&quot;356&quot; /&gt;&lt;br /&gt;The search page settings (click to enlarge)&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Once published open the page and try a search. Assuming your code and settings are correct the Google search results should be displayed within your SilverStripe page. Now all there is left for you to do is style the results.&lt;/p&gt;
&lt;p&gt;For an example of this technique at work, checkout the &lt;a href=&quot;http://www.pco.parliament.govt.nz/search&quot;&gt;Parliamentary Counsel Office&#039;s search interface&lt;/a&gt; which is implemented using the method just described.&lt;/p&gt;  &lt;/div&gt;

&lt;ul class=&quot;field-taxonomy-vocabulary-1&quot;&gt;

      &lt;li&gt;
      &lt;a href=&quot;/tech/search&quot;&gt;search&lt;/a&gt;    &lt;/li&gt;
      &lt;li&gt;
      &lt;a href=&quot;/tech/google&quot;&gt;google&lt;/a&gt;    &lt;/li&gt;
      &lt;li&gt;
      &lt;a href=&quot;/tech/cms&quot;&gt;cms&lt;/a&gt;    &lt;/li&gt;
      &lt;li&gt;
      &lt;a href=&quot;/tech/silverstripe&quot;&gt;silverstripe&lt;/a&gt;    &lt;/li&gt;
      &lt;li&gt;
      &lt;a href=&quot;/tech/tutorial&quot;&gt;tutorial&lt;/a&gt;    &lt;/li&gt;
  
&lt;/ul&gt;
</description>
 <pubDate>Tue, 15 Sep 2009 08:32:53 +0000</pubDate>
 <dc:creator>David</dc:creator>
 <guid isPermaLink="false">550 at https://www.stress-free.co.nz</guid>
</item>
</channel>
</rss>
