'. T_('Admins, your installation is in "Debug Mode" ($debugMode = true). To go in "Normal Mode" and hide debugging messages, change $debugMode to false into config.php.') ."
\n";
+}
+?>
diff --git a/doc/ChangeLog b/doc/ChangeLog
index 8fa208c..ade19af 100644
--- a/doc/ChangeLog
+++ b/doc/ChangeLog
@@ -1,6 +1,9 @@
ChangeLog for SemantiScuttle
============================
+- Implement request #1989987: Theming support
+
+
0.98.0 - 2011-XX-XX
-------------------
- Switch to jQuery and drop dojo
diff --git a/doc/themes.rst b/doc/themes.rst
new file mode 100644
index 0000000..6f34a36
--- /dev/null
+++ b/doc/themes.rst
@@ -0,0 +1,48 @@
+======================
+SemanticScuttle Themes
+======================
+SemanticScuttle may be changed visually by supplying custom "themes" (skins)
+that modify the visual appearance.
+
+
+Changing the current theme
+==========================
+In ``data/config.php``, set your theme like this: ::
+
+ $theme = 'darkmood';
+
+The available themes are the folders in ``www/themes/``.
+By default, SemanticScuttle ships only one usable theme ("default") and one
+to demonstrate how to create your own theme ("testdummy").
+
+
+Creating your own theme
+=======================
+Have a look at the "testdummy" theme in ``www/themes/testdummy/``.
+
+CSS and image files
+-------------------
+Since both file types need to be accessible via the web server directly,
+they are located in the ``www/`` folder: ::
+
+ www/themes/$themename/
+
+The main CSS file that automatically gets included is ::
+
+ www/themes/$themename/scuttle.css
+
+Several template files in SemanticScuttle include image files. If they do not
+exist in your theme, the default ones are used automatically.
+Note that this is not true for images that are specified in the CSS files.
+
+
+Template files
+--------------
+The templates of the default file are located in ::
+
+ data/templates/default/
+
+You may put your theme template files into ::
+
+ data/templates/$themename/
+
diff --git a/src/SemanticScuttle/Model/Template.php b/src/SemanticScuttle/Model/Template.php
index ff5fbbe..234e23f 100644
--- a/src/SemanticScuttle/Model/Template.php
+++ b/src/SemanticScuttle/Model/Template.php
@@ -76,6 +76,9 @@ class SemanticScuttle_Model_Template
* Sets variables and includes the template file,
* causing it to be rendered.
*
+ * Does not take care of themes and so.
+ * The include path must be set so the correct theme is used.
+ *
* @return void
*/
public function parse()
diff --git a/src/SemanticScuttle/Model/Theme.php b/src/SemanticScuttle/Model/Theme.php
new file mode 100644
index 0000000..65861b8
--- /dev/null
+++ b/src/SemanticScuttle/Model/Theme.php
@@ -0,0 +1,97 @@
+
+ * @license AGPL v3 or later http://www.gnu.org/licenses/agpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+
+/**
+ * A theme, the visual representation of SemanticScuttle.
+ *
+ * @category Bookmarking
+ * @package SemanticScuttle
+ * @author Christian Weiske
+ * @license AGPL v3 or later http://www.gnu.org/licenses/agpl.html
+ * @link http://sourceforge.net/projects/semanticscuttle
+ */
+class SemanticScuttle_Model_Theme
+{
+ /**
+ * Theme name. Also the path part of template and resource files
+ *
+ * @var string
+ */
+ protected $name = null;
+
+ /**
+ * Local path to the www themes directory.
+ * Needs to have a trailing slash.
+ *
+ * @var string
+ */
+ protected $wwwThemeDir = null;
+
+
+
+ /**
+ * Create a new theme instance.
+ *
+ * @param string $name Theme name "data/templates/(*)/"
+ */
+ public function __construct($name = 'default')
+ {
+ $this->name = $name;
+ $this->wwwThemeDir = $GLOBALS['wwwdir'] . '/themes/';
+ //TODO: implement theme hierarchies with parent fallback
+ }
+
+
+
+ /**
+ * Returns the URL path to a resource file (www/themes/$name/$file).
+ * Automatically falls back to the parent theme if the file does not exist
+ * in the theme.
+ *
+ * Must always be used when adding i.e. images to the output.
+ *
+ * @param string $file File name to find the path for, i.e. "scuttle.css".
+ *
+ * @return string Full path
+ */
+ public function resource($file)
+ {
+ $themeFile = $this->wwwThemeDir . $this->name . '/' . $file;
+ if (file_exists($themeFile)) {
+ return ROOT . 'themes/' . $this->name . '/' . $file;
+ }
+
+ $defaultFile = $this->wwwThemeDir . 'default/' . $file;
+ if (file_exists($defaultFile)) {
+ return ROOT . 'themes/default/' . $file;
+ }
+
+ //file does not exist. fall back to the theme file
+ // to guide the theme author a bit.
+ // TODO: logging. in admin mode, there should be a message
+ return ROOT . 'themes/' . $this->name . '/' . $file;
+ }
+
+
+
+ /**
+ * Returns the theme name.
+ *
+ * @return string Theme name
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+}
+?>
\ No newline at end of file
diff --git a/src/SemanticScuttle/Service/Template.php b/src/SemanticScuttle/Service/Template.php
index efa8d28..b5d4cfa 100644
--- a/src/SemanticScuttle/Service/Template.php
+++ b/src/SemanticScuttle/Service/Template.php
@@ -14,6 +14,7 @@
*/
require_once 'SemanticScuttle/Model/Template.php';
+require_once 'SemanticScuttle/Model/Theme.php';
/**
* SemanticScuttle template service.
@@ -38,6 +39,14 @@ class SemanticScuttle_Service_Template extends SemanticScuttle_Service
*/
protected $basedir;
+ /**
+ * The template theme to use.
+ * Set in constructor based on $GLOBALS['theme']
+ *
+ * @var SemanticScuttle_Model_Theme
+ */
+ protected $theme;
+
/**
@@ -64,6 +73,8 @@ class SemanticScuttle_Service_Template extends SemanticScuttle_Service
protected function __construct()
{
$this->basedir = $GLOBALS['TEMPLATES_DIR'];
+ $this->theme = new SemanticScuttle_Model_Theme($GLOBALS['theme']);
+ //FIXME: verify the theme exists
}
@@ -74,19 +85,33 @@ class SemanticScuttle_Service_Template extends SemanticScuttle_Service
* @param string $template Template filename relative
* to template dir
* @param array $vars Array of template variables.
+ * The current theme object will be added
+ * automatically with name "theme".
*
* @return SemanticScuttle_Model_Template Template object
*/
- function loadTemplate($template, $vars = null)
+ public function loadTemplate($template, $vars = null)
{
if (substr($template, -4) != '.php') {
$template .= '.php';
}
+
+ $oldIncPath = get_include_path();
+ set_include_path(
+ $this->basedir . $this->theme->getName()
+ . PATH_SEPARATOR . $this->basedir . 'default'
+ //needed since services are instantiated in templates
+ . PATH_SEPARATOR . $oldIncPath
+ );
+
+ $vars['theme'] = $this->theme;
$tpl = new SemanticScuttle_Model_Template(
- $this->basedir .'/'. $template, $vars, $this
+ $template, $vars, $this
);
$tpl->parse();
+ set_include_path($oldIncPath);
+
return $tpl;
}
}
diff --git a/src/SemanticScuttle/header.php b/src/SemanticScuttle/header.php
index 098e5c3..6c0d4df 100644
--- a/src/SemanticScuttle/header.php
+++ b/src/SemanticScuttle/header.php
@@ -18,9 +18,12 @@
if ('@data_dir@' == '@' . 'data_dir@') {
//non pear-install
$datadir = dirname(__FILE__) . '/../../data/';
+ $wwwdir = dirname(__FILE__) . '/../../www/';
} else {
//pear installation; files are in include path
$datadir = '@data_dir@/SemanticScuttle/';
+ //FIXME: when you have multiple installations, the www_dir will be wrong
+ $wwwdir = '@www_dir@/SemanticScuttle/';
}
if (!file_exists($datadir . '/config.php')) {
diff --git a/www/jsScuttle.php b/www/jsScuttle.php
index c166755..5e5f31b 100644
--- a/www/jsScuttle.php
+++ b/www/jsScuttle.php
@@ -3,6 +3,7 @@ $GLOBALS['saveInLastUrl'] = false;
$httpContentType = 'text/javascript';
require_once 'www-header.php';
require_once 'SemanticScuttle/functions.php';
+$theme = new SemanticScuttle_Model_Theme($GLOBALS['theme']);
$player_root = ROOT .'includes/player/';
?>
@@ -62,7 +63,7 @@ function isAvailable(input, response){
username = username.trim();
var availability = document.getElementById("availability");
if (username != '') {
- usernameField.style.backgroundImage = 'url(images/loading.gif)';
+ usernameField.style.backgroundImage = 'url(resource('images/loading.gif'); ?>)';
if (response != '') {
usernameField.style.backgroundImage = 'none';
if (response == 'true') {
@@ -92,7 +93,7 @@ function useAddress(ele) {
function getTitle(input, response){
var title = document.getElementById('titleField');
if (title.value == '') {
- title.style.backgroundImage = 'url(images/loading.gif)';
+ title.style.backgroundImage = 'url(resource('images/loading.gif');?>)';
if (response != null) {
title.style.backgroundImage = 'none';
title.value = response;
diff --git a/www/icon.png b/www/themes/default/icon.png
similarity index 100%
rename from www/icon.png
rename to www/themes/default/icon.png
diff --git a/www/images/b_edit.png b/www/themes/default/images/b_edit.png
similarity index 100%
rename from www/images/b_edit.png
rename to www/themes/default/images/b_edit.png
diff --git a/www/images/bg_admin.png b/www/themes/default/images/bg_admin.png
similarity index 100%
rename from www/images/bg_admin.png
rename to www/themes/default/images/bg_admin.png
diff --git a/www/images/bg_bar.png b/www/themes/default/images/bg_bar.png
similarity index 100%
rename from www/images/bg_bar.png
rename to www/themes/default/images/bg_bar.png
diff --git a/www/images/bg_header.png b/www/themes/default/images/bg_header.png
similarity index 100%
rename from www/images/bg_header.png
rename to www/themes/default/images/bg_header.png
diff --git a/www/images/bg_sidebar.png b/www/themes/default/images/bg_sidebar.png
similarity index 100%
rename from www/images/bg_sidebar.png
rename to www/themes/default/images/bg_sidebar.png
diff --git a/www/images/loading.gif b/www/themes/default/images/loading.gif
similarity index 100%
rename from www/images/loading.gif
rename to www/themes/default/images/loading.gif
diff --git a/www/images/logo.png b/www/themes/default/images/logo.png
similarity index 100%
rename from www/images/logo.png
rename to www/themes/default/images/logo.png
diff --git a/www/images/logo_24.gif b/www/themes/default/images/logo_24.gif
similarity index 100%
rename from www/images/logo_24.gif
rename to www/themes/default/images/logo_24.gif
diff --git a/www/images/rss.gif b/www/themes/default/images/rss.gif
similarity index 100%
rename from www/images/rss.gif
rename to www/themes/default/images/rss.gif
diff --git a/www/images/thumbs_up.orig.png b/www/themes/default/images/thumbs_up.orig.png
similarity index 100%
rename from www/images/thumbs_up.orig.png
rename to www/themes/default/images/thumbs_up.orig.png
diff --git a/www/images/vote-against-voted.png b/www/themes/default/images/vote-against-voted.png
similarity index 100%
rename from www/images/vote-against-voted.png
rename to www/themes/default/images/vote-against-voted.png
diff --git a/www/images/vote-against.png b/www/themes/default/images/vote-against.png
similarity index 100%
rename from www/images/vote-against.png
rename to www/themes/default/images/vote-against.png
diff --git a/www/images/vote-for-voted.png b/www/themes/default/images/vote-for-voted.png
similarity index 100%
rename from www/images/vote-for-voted.png
rename to www/themes/default/images/vote-for-voted.png
diff --git a/www/images/vote-for.png b/www/themes/default/images/vote-for.png
similarity index 100%
rename from www/images/vote-for.png
rename to www/themes/default/images/vote-for.png
diff --git a/www/scuttle.css b/www/themes/default/scuttle.css
similarity index 100%
rename from www/scuttle.css
rename to www/themes/default/scuttle.css
diff --git a/www/themes/testdummy/images/logo.png b/www/themes/testdummy/images/logo.png
new file mode 100644
index 0000000..4a8f8ba
Binary files /dev/null and b/www/themes/testdummy/images/logo.png differ
diff --git a/www/themes/testdummy/scuttle.css b/www/themes/testdummy/scuttle.css
new file mode 100644
index 0000000..c62cd19
--- /dev/null
+++ b/www/themes/testdummy/scuttle.css
@@ -0,0 +1,12 @@
+@import url(../default/scuttle.css);
+
+body {
+ background-color: #FEA;
+}
+
+html > body h1 {
+ background: url('images/logo.png') no-repeat 10px;
+}
+div#header {
+ background: #FEA;
+}