skiquel / drupal-style

A stylesheet override/editing system for Drupal

This URL has Read+Write access

drupal-style / style.module
100755 506 lines (423 sloc) 16.041 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
<?php
// $ID:$
/**
 * @file
 * Style.module is a stylesheet override system for Drupal themes.
 */
 
/**
 * See if styles exist in a directory
 *
 * Checks for directories
 *
 * @param string $dir
 * Directory to view
 * @param boolean $refresh
 * Bypass cache
 * @return
 * TRUE if styles exist. FALSE if not.
 */
function style_styles_exist($style_dir, $refresh = FALSE) {
  static $style_styles_exist;
  
  // Caching, can be bypased with $refresh being TRUE
  if ($refresh === FALSE && isset($style_styles_exist) && isset($style_styles_exist[$style_dir])) {
    return $style_styles_exist[$style_dir];
  }
 
  // Open link with our dir, as $handle
  if ($handle = opendir($style_dir)) {
    // Recursive through the items in this directory
    while (FALSE !== ($file = readdir($handle))) {
      // Is this item a directory?
      if (is_dir($file)) {
        // We found a style, we can finish.
        closedir($handle);
 
        // Return TRUE, a style directory exists
        return $style_styles_exist[$style_dir] = TRUE;
      }
    }
 
    // No directory (styles) found. Let's close link and return FALSE.
    closedir($handle);
    return $style_styles_exist[$style_dir] = FALSE;
  }
}
 
function style_scan_styles2($theme_name) {
  $files_dir = style_get_files_skin_dir($theme_name);
  $local_dir = style_get_local_skin_dir($theme_name);
    
  $styles = array();
  drupal_set_message("files {$files_dir} | local: {$local_dir}");
 
  if (is_dir($files_dir) && style_styles_exist($files_dir)) {
     $styles += style_file_tree($files_dir);
  }
  if (is_dir($local_dir) && style_styles_exist($local_dir)) {
    $styles += style_file_tree($local_dir);
  }
  return $styles;
}
 
/**
 * Scans for directories, not recursive.
 *
 * @param string $dir
 * Directory of the theme or module styles
 * @param boolean $refresh
      Bypass cache
 * @return array
 * All styles in folder
 */
function style_scan_styles($dir, $refresh = FALSE) {
  static $styles;
 
  // Caching system. Can be bypassed with refresh
  if ($refresh === FALSE && isset($styles) && isset($styles[$dir])) {
    return $styles[$dir];
  }
 
  // Open up directory
  if ($handle = opendir($dir)) {
    // Make array
    $styles[$dir] = array();
    // Scans inside the /style dir of a theme.
    // Each dir in /style is an individual style
    while (FALSE !== ($file = readdir($handle))) {
      // PHP will throw up '.' and '..'. No need to include.
      if ($file != "." && $file != ".." && is_dir($dir . $file)) {
        // Append this style dir to our array of styles
        $styles[$dir][] = $file;
      }
    }
 
    // Finish up
    closedir($handle);
  }
 
  // Return list of styles as an array
  return $styles[$dir];
}
 
/**
 * Return files in an array tree. :D
 *
 * @param string $dir
 * Directory of style
 * @param boolean $refresh
 * Bypass cache
 * @return array
 * Recursive tree of files.
 */
function style_file_tree($dir, $refresh = FALSE) {
  static $style;
 
  if (!is_dir($dir)) {
    drupal_set_message("{$dir} is not a directory");
  }
 
  // Caching, can be bypassed with $refresh
  if (isset($style[$dir]) && $refresh !== TRUE) {
    return $style[$dir];
  }
 
  $style[$dir] = _style_file_tree($dir);
  return $style[$dir];
}
 
/**
 * Get the structure and files of a style
 *
 * @param string $dir
 * Directory of style
 * @param boolean $show_files
 * Scan files
 * @return array
 * Recursive output of files.
 */
function _style_file_tree($dir, $show_files = TRUE) {
  $d = dir($dir); $x = array();
  while (($r = $d->read()) !== FALSE) {
    if ($r != "." && $r != ".." && (($show_files == FALSE && is_dir($dir.$r)) || $show_files == TRUE)) {
      $x[$r] = (is_dir($dir.$r) ? array() : (is_file($dir.$r) ? TRUE : FALSE));
    }
  }
 
  foreach ($x as $key => $value) {
    if (is_dir($dir.$key."/")) {
      // Move recursively into directories
      $x[$key] = _style_file_tree($dir.$key."/", $show_files);
    }
  }
  ksort($x);
  return $x;
}
 
/**
 * Return list of styles as a keyed array.
 *
 * @param string $directory
 * Directory to return formatted list from
 * @return array
 * Formatted as array[full_path + /styledir] = stylename
 */
function style_dirs($directory, $files = FALSE) {
  // If styles exists in the directory
  // First by seeing if styles dir itself exists
  // Then by seeing if individual styles are present in dir
  if (file_exists($directory) && style_styles_exist($directory)) {
    // Scan for styles in style dir
    $styles = style_scan_styles($directory);
 
    // See if this list of styles in the files section
    if (strpos($directory, file_directory_path()) !== FALSE) {
      $file = TRUE;
    }
 
    // Add each style to our list array
    foreach ($styles as $style) {
      // output: array[full_path + dir name] = style name
      $style_list[$directory . $style] = $style;
 
      if ($file) {
        // Add a * so users can identity generated/contributed styles
        $style_list[$directory . $style] .= " *";
      }
    }
  }
 
  // Return an array of styles
  return $style_list;
}
 
function style_menu() {
  $items['admin/build/styles'] = array(
    'page arguments' => array('style_settings_page'),
    'page callback' => 'drupal_get_form',
    'title' => 'Styles',
  );
 
  $items['style_test'] = array(
    'page callback' => 'style_test_ahah',
    'title' => 'color',
    'type' => MENU_CALLBACK,
    'access arguments' => array('access content'),
  );
  return $items;
}
 
/**
 * Form for our settings page
 *
 */
function style_settings_page() {
  # Get a list of themes
  $themes = array_keys(list_themes());
  
  $form['diagnostic'] = array(
    '#title' => t('Diagnostics'),
    '#type' => 'fieldset',
    '#description' => t('This module currently is set to detect themes stored
                        in /styles in your theme folders. It will also include
                        /files/themes/themename/styles.'),
    );
  foreach ($themes as $theme) {
    $style_info = style_dirs('theme', $theme);
 
    $style_list = $style_info['list'];
 
    if (isset($style_list) && count($style_list) > 0) {
      $form['diagnostic'][$theme] = array(
        '#type' => 'fieldset',
        '#title' => t('style_scan(\'theme\', \'' . $theme . '\')'),
        '#collapsible' => TRUE,
        '#attributes' => array('class' => 'style-select-fieldset'),
      );
      
      drupal_add_css(drupal_get_path('module', 'style') .'/style.form.css', 'module', 'all', TRUE);
      
      $form['diagnostic'][$theme]['theme_'.$theme.'_style'] = array(
        '#type' => 'select',
        '#title' => t('Style select box'),
        '#description' => t('* generated style'),
        '#options' => $style_list,
        '#default_value' => variable_get('theme_'.$theme.'_style', 0),
      );
    }
  }
  
  // Send our form to Drupal to make a settings page
  return system_settings_form($form);
}
 
/**
 * Inject pertinent style variables for current theme into Drupal.settings.style
 * scope.
 *
 */
function style_create_js_vars($theme_key) {
  
   $styles = style_scan_styles2($theme_key);
   drupal_add_js(array('style' => array('styles' => $styles)), 'setting');
   
}
 
function style_preprocess_page(&$vars) {
  // Our variables from Drupal's global scope
  global $theme_key, $theme_info, $base_url;
 
  // Get our theme directory
  $theme_dir = theme_get_setting('theme_'.$theme_key.'_style');
  
  $local_dir = style_get_local_skin_dir($theme_key);
  $files_dir = style_get_files_skin_dir($theme_key);
 
  // Does the directory exist?
  if (dir($theme_dir)) {
    // Initiate array
    $styles = array();
 
    // Cycle through our page's stylesheet files, by basename.
    foreach (array_keys($vars['css']['all']['theme']) as $css) {
      $styles[] = basename($css);
    }
  
    // Grab the CSS files from our style.
    $newcss = style_file_tree($theme_dir);
 
    // Create an array for new styles
    $new_styles = array();
    foreach (array_keys($newcss) as $css) {
      $new_styles[] = $theme_dir . '/' . $css; }
    $color_paths = $new_styles;
  }
                                                                    ///
  /*********************************************************^^^^^^**| | *
   * | D___| | *
  * W A R N I N G W A R N I N G W A R N I N G | R_____| *
   * /_ U| *
   * <STYLESHEET>___O P|__<VOILA!>
  * the machine | A| *
   * CSS OVERRIDE IN PROGRESS BEEP BOOP BEEP BOOP | L| *
  * |2008| *
  *****************************************************************/
  if (!empty($color_paths)) {
    // Loop over theme CSS files and try to Rebuild CSS array with rewritten
    // stylesheets. Keep the orginal order intact for CSS cascading.
    $new_theme_css = array();
 
    foreach ($vars['css']['all']['theme'] as $old_path => $old_preprocess) {
      // Add the non-colored stylesheet first as we might not find a
      // re-colored stylesheet for replacement later.
      $new_theme_css[$old_path] = $old_preprocess;
 
      // Loop over the path array with recolored CSS files to find matching
      // paths which could replace the non-recolored paths.
      foreach ($color_paths as $color_path) {
        if (basename($old_path) == basename($color_path)) {
          // Pull out the non-colored and add rewritten stylesheet.
          unset($new_theme_css[$old_path]);
          $new_theme_css[$color_path] = $old_preprocess;
 
          // If the current language is RTL and the CSS file had an RTL variant,
          // pull out the non-colored and add rewritten RTL stylesheet.
          if (defined('LANGUAGE_RTL') && $language->direction == LANGUAGE_RTL) {
            $rtl_old_path = str_replace('.css', '-rtl.css', $old_path);
            $rtl_color_path = str_replace('.css', '-rtl.css', $color_path);
            if (file_exists($rtl_color_path)) {
              unset($new_theme_css[$rtl_old_path]);
              $new_theme_css[$rtl_color_path] = $old_preprocess;
            }
          }
          break;
        }
      }
    }
    $vars['css']['all']['theme'] = $new_theme_css;
    $vars['styles'] = drupal_get_css($vars['css']);
  }
 
  // Some logo kung-fu. Replace logo.png at theme base.
  if (file_exists(theme_get_setting('theme_'.$theme_key.'_style') . '/logo.png')) {
    $vars['logo'] = $base_url . '/' . theme_get_setting('theme_'.$theme_key.'_style') . '/logo.png';
  }
  
}
 
 
function style_get_local_skin_dir($theme_name) {
  $local_dir = drupal_get_path('theme', $theme_name) . '/styles/';
  
  return $local_dir;
  
}
 
function style_get_files_skin_dir($theme_name) {
  $files_dir = file_directory_path() . '/' . drupal_get_path('theme', $theme_name) . '/styles/';
  
  return $files_dir;
}
 
/**
 * Implementation of hook_form_alter().
 */
function style_form_alter(&$form, $form_state, $form_id) {
  global $base_url;
  
  // Insert the color changer into the theme settings page.
  if ($form_id == 'system_theme_settings' && arg(4)) {
 
    if (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) != FILE_DOWNLOADS_PUBLIC) {
      // Disables the color changer when the private download method is used.
      // TODO: This should be solved in a different way. See issue #181003.
      drupal_set_message(t('The color picker only works if the <a href="@url">download method</a> is set to public.', array('@url' => url('admin/settings/file-system'))), 'warning');
    }
    else {
 
      // Via drupal argument (you'll see it in URL), we grab the theme name.
      $theme_name = arg(4);
 
      // Get the directories we want to scan for files in.
      $local_dir = style_get_local_skin_dir($theme_name);
      $files_dir = style_get_files_skin_dir($theme_name);
 
      $style_list = array();
      // Grab various information about styles.
      if (count($style_list_local = style_dirs($local_dir)) > 0) {
        $style_list += $style_list_local;
      }
      if (count($style_list_files = style_dirs($files_dir, TRUE)) > 0) {
        $style_list += $style_list_files;
      }
 
      // If styles are available for theme OR extensions are present in
      // $form['style']['extensions'], show our style fieldset.
      if (isset($style_list) && count($style_list) > 0 || count($form['style']['extensions']) > 0) {
 
        // Add our stylesheet to tighten things up
        drupal_add_css(drupal_get_path('module', 'style') .'/style.form.css', 'module', 'all', TRUE);
        
        drupal_add_js(drupal_get_path('module', 'style') . '/preloadCssImages.jQuery_v5.js');
        drupal_add_js(drupal_get_path('module', 'style') . '/style.js');
        
        // Add our theme's style roots to Drupal.Settings JS variable scope
        drupal_add_js(array('style' => array('local_dir' => $local_dir, 'files_dir' => $files_dir, 'theme_name' => $theme_name), ), 'setting');
 
        style_create_js_vars($theme_name);
 
        // Does $form['style'] already exist?
        if (!isset($form['style'])) {
          $form['style'] = array();
        }
 
        // Take on the ['style'], in case a module is loaded before.
        // This will assure order is not interfered with.
        $form['style'] += array(
        '#type' => 'fieldset',
        '#title' => t('Style'),
        '#description' => t('Change the look and feel your theme.'),
        '#collapsible' => TRUE,
        '#attributes' => array('class' => 'style-select-fieldset'),
        '#weight' => -15,
        );
        
        // http://api.drupal.org/api/function/theme_get_settings/6
        // We need to update theme_get_setting() in D7 to support per-theme
        // caching in certain events.
        $value = theme_get_settings($theme_name, 0);
        $value = $value['theme_'.$theme_name.'_style'];
        
        $form['style']['theme_name'] = array(
          '#type' => 'hidden',
          '#value' => $theme_name,
 
        );
 
        if (!isset($style_list) || count($style_list) == 0) {
          $form['style']['theme_'.$theme_name.'_style'] = array(
            '#type' => 'item',
            '#value' => t('No styles available'),
            '#default_value' => $value,
          );
        }
        else {
          // Select box of styles
          $form['style']['theme_'.$theme_name.'_style'] = array(
            '#type' => 'select',
            '#title' => t('Default'),
            // Generated styles have *
            '#description' => t('* generated style'),
            '#options' => $style_list,
            '#default_value' => $value,
            /*
            '#ahah' => array(
              'path' => 'style_test',
              'wrapper' => 'style-postgen-note',
              'method' => 'replace',
              'effect' => 'fade',
            ),
            */
           );
 
          $form['style']['markup'] = array(
            '#type' => 'markup',
            '#value' => '<div id=\'style-postgen-note\'></div>',
          );
         }
 
        // If style extensions have not yet been created, let's make it.
        if (count($form['style']['extensions']) > 0) {
          $form['style']['extensions'] += array(
          '#type' => 'fieldset',
          '#title' => t('Extensions'),
          '#collapsible' => TRUE,
          '#attributes' => array('class' => 'style-extensions-fieldset'),
          '#weight' => 5,
          );
        }
      }
    }
  }
}
 
function style_test_ahah() {
 
  $code = "<pre>";
  $code .= var_export($_REQUEST, 1);
  $code .= "</pre>";
 
  $theme_name = $_REQUEST['theme_name'];
 
  $path = $_REQUEST['theme_' . $theme_name . '_style'];
  if (strpos($path, file_directory_path()) !== FALSE) {
    drupal_json(array('status' => TRUE, 'data' => $theme_name . ' is a files style'));
  } else {
    drupal_json(array('status' => TRUE, 'data' => $code));
 
  }
 
 
 
}
 
?>