Project

General

Profile

Feature #2973 ยป theme_manager.class.php

Laurent Opprecht, 28/02/2011 16:00

 
1
<?php
2

    
3
namespace common\libraries;
4

    
5
/**
6
 * Theme manager. Used to create, copy, update themes.
7
 *
8
 * To create a theme based on differences only:
9
 *
10
 *      Create a "diff" theme containing only differences by calling create_empty_theme. Add differences to it - css, images, templates.
11
 *      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.
12
 *      Reaply make if base_theme or diff_them have changed.
13
 *
14
 * To update changes from a base theme to an exsiting theme.
15
 *
16
 *      Call update(from_them, to_them)
17
 *
18
 * 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.
19
 *
20
 * Note: at this point there is no differences between a diff theme and a normal theme. 
21
 * Creating diff theme is simply creating a theme that contains only differences from a base theme.
22
 * 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.
23
 *
24
 * @copyright (c) 2011 University of Geneva
25
 * @license GNU General Public License - http://www.gnu.org/copyleft/gpl.html
26
 * @author laurent.opprecht@unige.ch
27
 */
28
class ThemeManager {
29

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

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

    
48
    /**
49
     * List of existing themes.
50
     * 
51
     * @return array
52
     */
53
    public static function get_themes() {
54
        return Theme::get_themes();
55
    }
56

    
57
    /**
58
     * Make $target_theme by copying $base_theme and adding the differences contained in $diff_theme.
59
     * That is CSS files contained in $diff_theme are appended at the end. Taking precendence.
60
     * Image and Template files contained in $diff_theme overwrite existing files.
61
     *
62
     * Note that files already present in $target_theme are deleted!
63
     *
64
     * @param string $base_theme
65
     * @param string $diff_theme
66
     * @param string $target_theme
67
     */
68
    public static function make($base_theme, $diff_theme, $target_theme){
69
        set_time_limit(60*5);
70
        ThemeManager::delete($target_theme);
71
        ThemeManager::update($base_theme, $target_theme);
72
        ThemeManager::update($diff_theme, $target_theme, true, true);
73
    }
74

    
75
    /**
76
     * Create an empty theme. That is the directory theme directory structure but with no file. Can be called several times.
77
     * 
78
     * @param string $theme
79
     * @param string $root
80
     */
81
    public static function create_empty_theme($theme, $root = '') {
82
        $root = $root ? $root : Path::get('SYS_PATH');
83
        $files = Filesystem :: get_directory_content($root, Filesystem :: LIST_FILES_AND_DIRECTORIES, false);
84
        foreach ($files as $file) {
85
            $path = $root . '/' . $file;
86
            if (StringUtilities::start_with($file, '.')) {
87
                //do nothing those are sys files i.e. mercurial or apache, etc
88
            } else if ($file == 'resources') {
89
                $resource_types = self::get_resource_types();
90
                foreach ($resource_types as $type) {
91
                    $resource_dir = $path . '/' . $type . '/' . $theme;
92
                    Filesystem::create_dir($resource_dir);
93
                }
94
            } else if (is_dir($path)) {
95
                self::create_empty_theme($theme, $path);
96
            } else {
97
                //do nothing
98
            }
99
        }
100
    }
101

    
102
    /**
103
     * Delete a theme. Will not delete system themes.
104
     * Wrapper function to hide the $root parameter.
105
     *
106
     * @param string $theme
107
     * @param string $root
108
     */
109
    public static function delete($theme) {
110
        self::delete_exec($theme);
111
    }
112

    
113
    /**
114
     * Copy files from FROM theme to TO theme. Create TO theme if it doesn't exist.
115
     *
116
     * @param string $from_theme The theme to copy resources from
117
     * @param string $to_theme  The theme to copy resources to
118
     * @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.
119
     * @param string $force_overwrite If true overwrite existing files. By default overwite older files with new ones based on the "change" file timestamp.
120
     * @param string $root root directory to update from. Leave blank to update everything.
121
     */
122
    public static function update($from_theme, $to_theme, $merge = false, $force_overwrite = false, $root = '') {
123
        $root = $root ? $root : Path::get('SYS_PATH');
124
        $files = Filesystem :: get_directory_content($root, Filesystem :: LIST_FILES_AND_DIRECTORIES, false);
125
        foreach ($files as $file) {
126
            $path = $root . '/' . $file;
127
            if ($comp = StringUtilities::start_with($file, '.')) {
128
                //do nothing those are sys files i.e. mercurial or apache, etc
129
            } else if ($file == 'resources') {
130
                $resource_types = self::get_resource_types();
131
                foreach ($resource_types as $type) {
132
                    $f = $path . '/' . $type . '/' . $from_theme;
133
                    $t = $path . '/' . $type . '/' . $to_theme;
134
                    self::copy_ressources($f, $t, $type, $merge, $force_overwrite);
135
                }
136
            } else if (is_dir($path)) {
137
                self::update($from_theme, $to_theme, $merge, $force_overwrite, $path);
138
            } else {
139
                //do nothing
140
            }
141
        }
142
    }
143

    
144
    /**
145
     * Delete a theme. Will not delete system themes.
146
     *
147
     * @param string $theme
148
     * @param string $root
149
     */
150
    private static function delete_exec($theme, $root = '') {
151

    
152
        //protection against possible attacks
153
        $theme = str_replace('/', '', $theme);
154
        $theme = str_replace('\\', '', $theme);
155

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

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

    
166
        $files = Filesystem :: get_directory_content($root, Filesystem :: LIST_FILES_AND_DIRECTORIES, false);
167
        foreach ($files as $file) {
168
            $path = $root . '/' . $file;
169
            if (StringUtilities::start_with($file, '.')) {
170
                //do nothing those are sys files i.e. mercurial or apache, etc
171
            } else if ($file == 'resources') {
172
                $resource_types = self::get_resource_types();
173
                foreach ($resource_types as $type) {
174
                    $resource_dir = $path . '/' . $type . '/' . $theme;
175
                    Filesystem::remove($resource_dir);
176
                }
177
            } else if (is_dir($path)) {
178
                self::delete_exec($theme, $path);
179
            } else {
180
                //do nothing
181
            }
182
        }
183
    }
184

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

    
189
    /**
190
     * 
191
     */
192
    private static function recurse_copy($source, $destination, $type, $merge, $force_overwrite) {
193
        if (!is_dir($source)) {
194
            return self :: copy_resource($source, $destination, $type, $merge, $force_overwrite);
195
        }
196
        $result = true;
197

    
198
        $content = Filesystem :: get_directory_content($source, Filesystem :: LIST_FILES_AND_DIRECTORIES, false);
199
        foreach ($content as $file) {
200
            $path_to_file = $source . '/' . $file;
201
            $path_to_new_file = $destination . '/' . $file;
202
            if (!is_dir($path_to_file)) {
203
                $result &= self :: copy_resource($path_to_file, $path_to_new_file, $type, $merge, $force_overwrite);
204
            } else {
205
                Filesystem :: create_dir($path_to_new_file);
206
                $result &= self :: recurse_copy($path_to_file, $path_to_new_file, $type, $merge, $force_overwrite);
207
            }
208
        }
209

    
210
        return $result;
211
    }
212

    
213
    /**
214
     * Copy file to destination if souce is newer OR we do a force overwrite. I.e. we don't ovewrite changes.
215
     */
216
    private static function copy_resource($source, $destination, $type, $merge, $force_overwrite) {
217
        $exists = file_exists($destination);
218
        if ($exists && $merge && $type == 'css') {
219
            $data = "\n" . file_get_contents($source);
220
            return file_put_contents($destination, $data, FILE_APPEND);
221
        } else if ($exists && !$force_overwrite) {
222
            $d_time = filectime($destination);
223
            $s_time = filectime($source);
224
            if ($s_time < $d_time) {
225
                return false;
226
            }
227
        }
228

    
229
        $destination_dir = dirname($destination);
230
        if (file_exists($source) && Filesystem :: create_dir($destination_dir)) {
231
            copy($source, $destination);
232
        }
233
    }
234

    
235
}
236

    
237
?>
    (1-1/1)