Project

General

Profile

Feature #2973 ยป theme_manager.class.php

Laurent Opprecht, 28/02/2011 16:00

 
<?php

namespace common\libraries;

/**
* Theme manager. Used to create, copy, update themes.
*
* To create a theme based on differences only:
*
* Create a "diff" theme containing only differences by calling create_empty_theme. Add differences to it - css, images, templates.
* Call make(base_theme, diff_theme, target_theme) where base theme is the base theme you want to copy, diff_theme is the theme that contains your differences and target_theme is the final them to use in Chamilo.
* Reaply make if base_theme or diff_them have changed.
*
* To update changes from a base theme to an exsiting theme.
*
* Call update(from_them, to_them)
*
* This will overwrite existing files based on the file change time stamp. Note that css files will not be merged! To do that create first a diff_theme containing differences and do a make.
*
* Note: at this point there is no differences between a diff theme and a normal theme.
* Creating diff theme is simply creating a theme that contains only differences from a base theme.
* As a result diff themes will be listed in the user interface and selected as the main theme. Which should not be done since they will not contain enough information.
*
* @copyright (c) 2011 University of Geneva
* @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html
* @author laurent.opprecht@unige.ch
*/
class ThemeManager {

/**
* Return the list of resource types to copy from a theme to another. That is CSS, IMAGES and TEMPLATES.
*
* @return array
*/
public static function get_resource_types() {
$result = array('css', 'images', 'templates');
return $result;
}

/*
* List of system themes. That is themes that are necessary for the system to work.
*/
public static function get_system_themes() {
$result = array('aqua');
return $result;
}

/**
* List of existing themes.
*
* @return array
*/
public static function get_themes() {
return Theme::get_themes();
}

/**
* Make $target_theme by copying $base_theme and adding the differences contained in $diff_theme.
* That is CSS files contained in $diff_theme are appended at the end. Taking precendence.
* Image and Template files contained in $diff_theme overwrite existing files.
*
* Note that files already present in $target_theme are deleted!
*
* @param string $base_theme
* @param string $diff_theme
* @param string $target_theme
*/
public static function make($base_theme, $diff_theme, $target_theme){
set_time_limit(60*5);
ThemeManager::delete($target_theme);
ThemeManager::update($base_theme, $target_theme);
ThemeManager::update($diff_theme, $target_theme, true, true);
}

/**
* Create an empty theme. That is the directory theme directory structure but with no file. Can be called several times.
*
* @param string $theme
* @param string $root
*/
public static function create_empty_theme($theme, $root = '') {
$root = $root ? $root : Path::get('SYS_PATH');
$files = Filesystem :: get_directory_content($root, Filesystem :: LIST_FILES_AND_DIRECTORIES, false);
foreach ($files as $file) {
$path = $root . '/' . $file;
if (StringUtilities::start_with($file, '.')) {
//do nothing those are sys files i.e. mercurial or apache, etc
} else if ($file == 'resources') {
$resource_types = self::get_resource_types();
foreach ($resource_types as $type) {
$resource_dir = $path . '/' . $type . '/' . $theme;
Filesystem::create_dir($resource_dir);
}
} else if (is_dir($path)) {
self::create_empty_theme($theme, $path);
} else {
//do nothing
}
}
}

/**
* Delete a theme. Will not delete system themes.
* Wrapper function to hide the $root parameter.
*
* @param string $theme
* @param string $root
*/
public static function delete($theme) {
self::delete_exec($theme);
}

/**
* Copy files from FROM theme to TO theme. Create TO theme if it doesn't exist.
*
* @param string $from_theme The theme to copy resources from
* @param string $to_theme The theme to copy resources to
* @param bool $merge If true attemp to merge the FROM files with the TO files. That is for css append the css at the end of the css if it exists.
* @param string $force_overwrite If true overwrite existing files. By default overwite older files with new ones based on the "change" file timestamp.
* @param string $root root directory to update from. Leave blank to update everything.
*/
public static function update($from_theme, $to_theme, $merge = false, $force_overwrite = false, $root = '') {
$root = $root ? $root : Path::get('SYS_PATH');
$files = Filesystem :: get_directory_content($root, Filesystem :: LIST_FILES_AND_DIRECTORIES, false);
foreach ($files as $file) {
$path = $root . '/' . $file;
if ($comp = StringUtilities::start_with($file, '.')) {
//do nothing those are sys files i.e. mercurial or apache, etc
} else if ($file == 'resources') {
$resource_types = self::get_resource_types();
foreach ($resource_types as $type) {
$f = $path . '/' . $type . '/' . $from_theme;
$t = $path . '/' . $type . '/' . $to_theme;
self::copy_ressources($f, $t, $type, $merge, $force_overwrite);
}
} else if (is_dir($path)) {
self::update($from_theme, $to_theme, $merge, $force_overwrite, $path);
} else {
//do nothing
}
}
}

/**
* Delete a theme. Will not delete system themes.
*
* @param string $theme
* @param string $root
*/
private static function delete_exec($theme, $root = '') {

//protection against possible attacks
$theme = str_replace('/', '', $theme);
$theme = str_replace('\\', '', $theme);

//protection, system themes which are necessary for the system to work should not be deletable
$system_themes = self::get_system_themes();
foreach ($system_themes as $system_theme) {
if (strtolower($system_theme) == $theme) {
return false;
}
}

$root = $root ? $root : Path::get('SYS_PATH');

$files = Filesystem :: get_directory_content($root, Filesystem :: LIST_FILES_AND_DIRECTORIES, false);
foreach ($files as $file) {
$path = $root . '/' . $file;
if (StringUtilities::start_with($file, '.')) {
//do nothing those are sys files i.e. mercurial or apache, etc
} else if ($file == 'resources') {
$resource_types = self::get_resource_types();
foreach ($resource_types as $type) {
$resource_dir = $path . '/' . $type . '/' . $theme;
Filesystem::remove($resource_dir);
}
} else if (is_dir($path)) {
self::delete_exec($theme, $path);
} else {
//do nothing
}
}
}

private static function copy_ressources($from, $to, $type, $merge, $force_overwrite) {
self::recurse_copy($from, $to, $type, $merge, $force_overwrite);
}

/**
*
*/
private static function recurse_copy($source, $destination, $type, $merge, $force_overwrite) {
if (!is_dir($source)) {
return self :: copy_resource($source, $destination, $type, $merge, $force_overwrite);
}
$result = true;

$content = Filesystem :: get_directory_content($source, Filesystem :: LIST_FILES_AND_DIRECTORIES, false);
foreach ($content as $file) {
$path_to_file = $source . '/' . $file;
$path_to_new_file = $destination . '/' . $file;
if (!is_dir($path_to_file)) {
$result &= self :: copy_resource($path_to_file, $path_to_new_file, $type, $merge, $force_overwrite);
} else {
Filesystem :: create_dir($path_to_new_file);
$result &= self :: recurse_copy($path_to_file, $path_to_new_file, $type, $merge, $force_overwrite);
}
}

return $result;
}

/**
* Copy file to destination if souce is newer OR we do a force overwrite. I.e. we don't ovewrite changes.
*/
private static function copy_resource($source, $destination, $type, $merge, $force_overwrite) {
$exists = file_exists($destination);
if ($exists && $merge && $type == 'css') {
$data = "\n" . file_get_contents($source);
return file_put_contents($destination, $data, FILE_APPEND);
} else if ($exists && !$force_overwrite) {
$d_time = filectime($destination);
$s_time = filectime($source);
if ($s_time < $d_time) {
return false;
}
}

$destination_dir = dirname($destination);
if (file_exists($source) && Filesystem :: create_dir($destination_dir)) {
copy($source, $destination);
}
}

}

?>
    (1-1/1)