/[drupal]/contributions/modules/icon/icon.module
ViewVC logotype

Contents of /contributions/modules/icon/icon.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.8 - (show annotations) (download) (as text)
Sun Jul 6 21:13:38 2008 UTC (16 months, 2 weeks ago) by ximo
Branch: MAIN
CVS Tags: DRUPAL-6--1-0-ALPHA1, HEAD
Branch point for: DRUPAL-6--1
Changes since 1.7: +123 -30 lines
File MIME type: text/x-php
Stylesheets are now being rewritten with icons from the selected icon sets. Three new functions:
icon_rewrite_stylesheets(), _icon_rewrite_stylesheet() and _icon_save_stylesheet()
1 <?php
2 // $Id: icon.module,v 1.7 2008/07/05 23:18:36 ximo Exp $
3
4 /**
5 * Implementation of hook_init().
6 */
7 function icon_init() {
8 init_theme(); // TODO: Must. Do. Better.
9 global $icons, $theme;
10
11 // Retrieve icons for current theme from cache (or rebuild).
12 $icons = icon_get_icons($theme);
13
14 // Add icon stylesheet file, if one has been generated for this theme.
15 $stylesheet = variable_get('icon_'. $theme .'_stylesheet', NULL);
16 if ($stylesheet) {
17 drupal_add_css($stylesheet, 'theme', all, FALSE);
18 }
19 }
20
21 /**
22 * Implementation of hook_theme().
23 */
24 function icon_theme() {
25 return array(
26 'icon' => array(
27 'arguments' => array('name' => NULL, 'size' => NULL),
28 ),
29 'icon_iconsets_form' => array(
30 'arguments' => array('form' => NULL),
31 ),
32 );
33 }
34
35 /**
36 * Implementation of hook_perm().
37 */
38 function icon_perm() {
39 return array('administer icons');
40 }
41
42 /**
43 * Implementation of hook_menu().
44 */
45 function icon_menu() {
46 // Global settings:
47 $items['admin/build/icons'] = array(
48 'title' => 'Icons',
49 'description' => 'Administer icon sets and choose which icons to use.',
50 'page callback' => 'drupal_get_form',
51 'page arguments' => array('icon_settings_form'),
52 'access arguments' => array('administer icons'),
53 );
54 $items['admin/build/icons/settings'] = array(
55 'title' => 'Global settings',
56 'type' => MENU_DEFAULT_LOCAL_TASK,
57 'weight' => -1,
58 );
59
60 // Icon configuration for enabled themes:
61 require_once('includes/theme.inc');
62 $themes = list_themes();
63 foreach ($themes as $key => $theme) {
64 // Only show themes with icons, or with base themes with icons.
65 if ($theme->status && (is_array($theme->info['icons']) || ($themes[$theme->base_theme]->status && $themes[$theme->base_theme]->info['icons']))) {
66 $items['admin/build/icons/'. $key] = array(
67 'title' => $theme->info['name'],
68 'page callback' => 'drupal_get_form',
69 'page arguments' => array('icon_iconsets_form', $key),
70 'access arguments' => array('administer icons'),
71 'type' => MENU_LOCAL_TASK,
72 );
73 }
74 }
75
76 return $items;
77 }
78
79 /**
80 * Menu callback; displays configuration options for all themes.
81 */
82 function icon_settings_form(&$form_state) {
83 $form['todo'] = array(
84 '#value' => '<p>TODO: Figure out if this page is really needed.</p><p>For now, use the tabs above to configure themes. If you see no other tabs, you have no icons enabled themes.</p>',
85 );
86
87 return $form;
88 }
89
90 /**
91 * Menu callback; displays a listing of all icon sets.
92 *
93 * @see theme_icon_iconsets_form()
94 */
95 function icon_iconsets_form(&$form_state, $theme) {
96 $themes = list_themes();
97
98 // Fetch all icon sets available to the specified theme.
99 $iconsets = icon_get_iconsets($theme);
100
101 // Fetch icon set statuses and weights for the specified theme.
102 icon_get_iconset_data($iconsets, $theme);
103
104 // Sort the icon sets.
105 uasort($iconsets, '_icon_sort_iconsets');
106
107 $form = array('#tree' => TRUE);
108
109 $form['theme'] = array(
110 '#type' => 'value',
111 '#value' => $theme,
112 );
113
114 // Add form elements for all available icon sets:
115 foreach ($iconsets as $key => $iconset) {
116 $screenshot = $iconset['path'] .'/'. $iconset['screenshot'];
117 if ($iconset['screenshot'] && file_exists($screenshot)) {
118 $form[$key]['screenshot']['#value'] = theme('image', $screenshot, t('Screenshot for %iconset icon set', array('%iconset' => $iconset['name'])), '', array('class' => 'screenshot'), FALSE);
119 }
120 else {
121 $form[$key]['screenshot']['#value'] = t('no screenshot');
122 }
123
124 $form[$key]['iconset'] = array(
125 '#type' => 'value',
126 '#value' => $iconset,
127 );
128
129 $form[$key]['status'] = array(
130 '#type' => 'checkbox',
131 '#default_value' => isset($form_state[$key]['status']) ? $form_state[$key]['status'] : $iconset['status'],
132 );
133
134 $form[$key]['weight'] = array(
135 '#type' => 'weight',
136 '#delta' => 10,
137 '#default_value' => isset($form_state[$key]['weight']) ? $form_state[$key]['weight'] : $iconset['weight'],
138 );
139 }
140
141 if (count($iconsets)) {
142 $form['buttons']['submit'] = array(
143 '#type' => 'submit',
144 '#value' => t('Save configuration'),
145 );
146 $form['buttons']['reset'] = array(
147 '#type' => 'submit',
148 '#value' => t('Reset to defaults'),
149 );
150 }
151
152 return $form;
153 }
154
155 /**
156 * Process icon_iconsets_form form submissions.
157 */
158 function icon_iconsets_form_submit($form, &$form_state) {
159 $theme = $form_state['values']['theme'];
160
161 // Build an array of icon sets and their statuses and weights.
162 $iconsets = array();
163 foreach ($form_state['values'] as $key => $value) {
164 if (is_array($form_state['values'][$key]['iconset'])) {
165 if ($form_state['values']['op'] == t('Save configuration')) {
166 $iconsets[$key] = array(
167 'status' => $form_state['values'][$key]['status'],
168 'weight' => $form_state['values'][$key]['weight'],
169 );
170 }
171 }
172 }
173
174 // Save changes to the icon sets.
175 _icon_save_iconsets($iconsets, $theme);
176
177 // Rebuild the cache of icons for the specified theme.
178 $icons = icon_get_icons($theme, TRUE);
179
180 // Rewrite stylesheets.
181 icon_rewrite_stylesheets($icons, $theme);
182
183 drupal_set_message(t('The configuration options have been saved.'));
184 }
185
186 /**
187 * Save statuses and weights for icon sets.
188 *
189 * @param $iconsets
190 * An array of icon sets to save.
191 * @param $theme
192 * The theme for which to save statuses.
193 */
194 function _icon_save_iconsets($iconsets, $theme) {
195 $result = db_query("SELECT iconset, status, weight FROM {iconsets} WHERE theme = '%s'", $theme);
196
197 while ($item = db_fetch_object($result)) {
198 if ($iconsets[$item->iconset] === NULL) {
199 // Delete the icon set from the database if it no longer exists.
200 db_query("DELETE FROM {iconsets} WHERE iconset = '%s' AND theme = '%s'", $item->iconset, $theme);
201 }
202 elseif ($iconsets[$item->iconset]['status'] != $item->status || $iconsets[$item->iconset]['weight'] != $item->weight) {
203 // Update the icon set if its status or weight has changed.
204 db_query("UPDATE {iconsets} SET status = %d, weight = %d WHERE iconset = '%s' AND theme = '%s'", $iconsets[$item->iconset]['status'], $iconsets[$item->iconset]['weight'], $item->iconset, $theme);
205 }
206
207 // Remove the processed icon set from the array.
208 unset($iconsets[$item->iconset]);
209 }
210
211 // Any remaining icon sets are new and should be saved.
212 foreach ($iconsets as $key => $iconset) {
213 db_query("INSERT INTO {iconsets} (iconset, theme, status, weight) VALUES ('%s', '%s', %d, %d)", $key, $theme, $iconset['status'], $iconset['weight']);
214 }
215 }
216
217 /**
218 * Adds the statuses and weights to icon sets for a particular theme.
219 *
220 * @param $iconsets
221 * An array of icon sets to check.
222 * @param $theme
223 * The theme to check for status against.
224 */
225 function icon_get_iconset_data(&$iconsets, $theme) {
226 $result = db_query("SELECT iconset, status, weight FROM {iconsets} WHERE theme = '%s'", $theme);
227 while ($item = db_fetch_object($result)) {
228 $data[$item->iconset] = $item;
229 }
230
231 foreach ($iconsets as $key => $iconset) {
232 if (!isset($data[$key])) {
233 $data[$key]->weight = 0;
234 $data[$key]->status = ($iconsets[$key]['type'] == 'iconset' ? 0 : 1);
235 }
236 $iconsets[$key]['weight'] = $data[$key]->weight;
237 $iconsets[$key]['status'] = ($iconsets[$key]['type'] == 'iconset' ? $data[$key]->status : 1);
238 }
239 }
240
241 /**
242 * Collect data about available icon sets.
243 *
244 * @param $theme
245 * The theme to get available icons for.
246 * @param $reset
247 * (optional) Set to TRUE to reset the in-memory cache.
248 * @return
249 * An array of icon sets and their icons.
250 */
251 function icon_get_iconsets($theme, $reset = FALSE) {
252 static $iconsets = array();
253
254 if (empty($iconsets) || $reset) {
255 $infos = array();
256
257 // Fetch information about standalone icon sets.
258 $standalone_iconsets = icon_find_iconsets();
259 foreach ($standalone_iconsets as $iconset) {
260 $infos[$iconset->name] = $iconset->info;
261 $iconsets[$iconset->name] = array(
262 'name' => $iconset->info['name'],
263 'description' => t($iconset->info['description']),
264 'screenshot' => $iconset->info['screenshot'],
265 'path' => dirname($iconset->filename),
266 'type' => 'iconset',
267 );
268 }
269
270 $themes = list_themes();
271
272 // Fetch information about enabled system components that provide icons.
273 $result = db_query("SELECT filename, name, type, info FROM {system} WHERE type <> 'iconset' AND status = 1 ORDER BY weight DESC");
274 while ($component = db_fetch_object($result)) {
275
276 // Make sure we don't fetch other themes' icons.
277 if ($component->type == 'theme' && $component->name != $theme && $component->name != $themes[$theme]->base_theme) {
278 continue;
279 }
280
281 // Only add components that provide icons.
282 $info = unserialize($component->info);
283 if ($info['icons']) {
284 $infos[$component->name] = $info;
285 $iconsets[$component->name] = array(
286 'name' => $info['name'],
287 'description' => t('Icons provided by %name.', array('%name' => $info['name'])),
288 'path' => dirname($component->filename),
289 'type' => $component->type,
290 );
291 }
292
293 }
294
295 // Locate icon files for the discovered icon sets.
296 foreach ($iconsets as $key => $iconset){
297
298 // Add paths to any icon size folders found.
299 if (is_array($infos[$key]['sizes'])) {
300 foreach ($infos[$key]['sizes'] as $size => $folder) {
301 $path = $iconset['path'] .'/'. $folder;
302 if (is_dir($path)) {
303 $iconsets[$key]['sizes'][$size] = $path;
304 }
305 }
306 }
307
308 // Add paths to all icons in any provided sizes.
309 if (is_array($infos[$key]['icons'])) {
310 foreach ($infos[$key]['icons'] as $icon_name => $value) {
311
312 // The icon has been declared with specific icon sizes, so look in the
313 // specified path.
314 if (is_array($value)) {
315 foreach ($value as $size => $file) {
316 $path = $iconset['path'] .'/'. $file;
317 if (file_exists($path)) {
318 $iconsets[$key]['icons'][$icon_name][$size] = $path;
319 }
320 }
321 }
322
323 // The icon has no specific size or path specified, so look in all
324 // specified size folders.
325 elseif ($iconsets[$key]['sizes']) {
326 foreach ($iconsets[$key]['sizes'] as $size => $size_folder) {
327 $path = $size_folder .'/'. $value;
328 if (file_exists($path)) {
329 $iconsets[$key]['icons'][$icon_name][$size] = $path;
330 }
331 }
332 }
333
334 }
335 }
336
337 // Remove any icon sets whose declared icon files doesn't exist.
338 if (empty($iconsets[$key]['icons'])) {
339 unset($iconsets[$key]);
340 }
341
342 }
343
344 }
345
346 return $iconsets;
347 }
348
349 /**
350 * Find all standalone icon sets.
351 *
352 * @return
353 * An array of icon set files.
354 */
355 function icon_find_iconsets() {
356 // Find standalone icon sets' .info files.
357 $iconsets = drupal_system_listing('\.info$', 'icons', 'name', 0);
358
359 // Extract current icon sets from database.
360 system_get_files_database($iconsets, 'iconsets');
361
362 // Set defaults for icon sets.
363 $defaults = array(
364 'name' => '',
365 'description' => '',
366 'screenshot' => 'screenshot.png',
367 );
368
369 // Add .info file's content to each icon set.
370 foreach ($iconsets as $key => $iconset) {
371 $iconsets[$key]->info = drupal_parse_info_file($iconset->filename) + $defaults;
372 }
373
374 $result = db_query("SELECT filename, name, info FROM {system} WHERE type = 'iconset'");
375 while ($item = db_fetch_object($result)) {
376 // Flag the icon set as processed.
377 $iconsets[$item->name]->processed = TRUE;
378
379 // Serialize the .info data.
380 $info = serialize($iconsets[$item->name]->info);
381
382 if (($iconsets[$item->name] === NULL) || !file_exists($item->filename)) {
383 // Delete {system} and array entries if the icon set no longer exists.
384 db_query("DELETE FROM {system} WHERE name = '%s'", $item->name);
385 unset($iconsets[$item->name]);
386 }
387 elseif ($item->info != $info) {
388 // Update the {system} entry if its info has changed.
389 db_query("UPDATE {system} SET info = '%s' WHERE name = '%s'", $info);
390 }
391 }
392
393 foreach ($iconsets as $key => $iconset) {
394 if ($iconset->processed) {
395 // The icon set has already been processed, so remove the flag.
396 unset($iconsets[$key]->processed);
397 }
398 else {
399 // Any icon sets that have not been processed should be stored.
400 db_query("INSERT INTO {system} (name, owner, info, type, filename, status, throttle, bootstrap) VALUES ('%s', '%s', '%s', '%s', '%s', %d, %d, %d)", $iconset->name, '', serialize($iconset->info), 'iconset', $iconset->filename, $iconset->status, 0, 0);
401 }
402 }
403
404 return $iconsets;
405 }
406
407 /**
408 * Collect an array of all icons available to the specified theme.
409 *
410 * @param $theme
411 * Name of the theme for which to get available icons.
412 * @param $reset
413 * (optional) Set to TRUE to reset the cache.
414 * @return
415 * An array of available icons for the specified theme.
416 */
417 function icon_get_icons($theme, $reset = FALSE) {
418 static $icons = array();
419
420 if (!isset($icons[$theme]) || $reset) {
421 if (!$reset && ($cache = cache_get('icon:'. $theme)) && !empty($cache->data)) {
422 $icons[$theme] = $cache->data;
423 }
424 else {
425 // Fetch icon sets, add data, sort as preferred.
426 $iconsets = icon_get_iconsets($theme);
427 icon_get_iconset_data($iconsets, $theme);
428 uasort($iconsets, '_icon_sort_iconsets');
429
430 // Combine all icons into one array.
431 // TODO: Build an array of icons based on icons declared in themes and
432 // modules, and use fallback to similar icons when none found.
433 $icons[$theme] = array();
434 foreach (array_reverse($iconsets) as $key => $iconset) {
435 if ($iconset['status']) {
436 $icons[$theme] = array_merge($icons[$theme], $iconset['icons']);
437 }
438 }
439
440 // Cache the available icons.
441 cache_set('icon:'. $theme, $icons[$theme]);
442 }
443 }
444
445 return $icons[$theme];
446 }
447
448 /**
449 * Rewrite stylesheets to add icons.
450 */
451 function icon_rewrite_stylesheets($icons, $theme) {
452 // Delete any existing stylesheet for this theme.
453 if ($old_stylesheet = variable_get('icon_'. $theme .'_stylesheet', NULL)) {
454 @unlink($old_stylesheet);
455 }
456
457 // Fetch stylesheets added with drupal_add_css().
458 $css = drupal_add_css();
459 $paths = array();
460 foreach ($css['all'] as $type) {
461 $paths = array_merge($paths, $type);
462 }
463
464 // Rewrite stylesheets and combine the results.
465 $style = '';
466 foreach ($paths as $path => $preprocess) {
467 $style .= _icon_rewrite_stylesheet($path, $icons, $theme);
468 }
469
470 // Save rewritten styles as .css file in files directory.
471 $filename = _icon_save_stylesheet($style, $theme);
472
473 variable_set('icon_'. $theme .'_stylesheet', $filename);
474 }
475
476 /**
477 * Rewrite a single stylesheet to add icons.
478 */
479 function _icon_rewrite_stylesheet($file, $icons, $theme) {
480 $themes = list_themes();
481 $theme = $themes[$theme];
482 if ($theme->base_theme) {
483 $theme = $themes[$theme->base_theme];
484 }
485
486 // Load original stylesheet.
487 $style = drupal_load_stylesheet($file, TRUE);
488
489 $chunks = preg_split('/\}/', $style, -1, PREG_SPLIT_NO_EMPTY);
490
491 foreach ($chunks as $chunk) {
492 foreach ($theme->info['icons'] as $icon => $sizes) {
493 foreach ($sizes as $size => $path) {
494 if (stripos($chunk, $path)) {
495 $new_icon = base_path() . $icons[$icon][$size];
496 $selector = substr($chunk, 0, stripos($chunk, '{'));
497 $output .= "$selector {background-image: url($new_icon);}";
498 }
499 }
500 }
501 }
502
503 return $output;
504 }
505
506 /**
507 * Save the rewritten stylesheet to disk.
508 *
509 * @param $style
510 * Content of the stylesheet to save.
511 * @return
512 * Filename of the saved stylesheet relative to the directory.
513 */
514 function _icon_save_stylesheet($style, $theme) {
515 // Prepare the directory.
516 $directory = file_directory_path() .'/icon';
517 file_check_directory($directory, FILE_CREATE_DIRECTORY);
518
519 // Generate a filename.
520 $filename = $directory .'/'. $theme .'-'. substr(md5($style . microtime()), 0, 8) .'.css';
521
522 // Write new stylesheet.
523 file_save_data($style, $filename, FILE_EXISTS_REPLACE);
524
525 // Set standard file permissions for webserver-generated files.
526 @chmod($file, 0664);
527
528 return $filename;
529 }
530
531 /**
532 * Array sorting callback; sorts icon sets.
533 */
534 function _icon_sort_iconsets($a, $b) {
535 if ($a['weight'] == $b['weight']) {
536 // Sort by name if equal weights.
537 return strcasecmp($a['name'], $b['name']);
538 }
539 else {
540 return $a['weight'] > $b['weight'];
541 }
542 }
543
544 /**
545 * Theme function for the icon sets overview form.
546 *
547 * @param $form
548 * An associative array containing the structure of the form.
549 * @see icon_iconsets_form()
550 */
551 function theme_icon_iconsets_form($form) {
552 drupal_add_tabledrag('icon-iconsets', 'order', 'sibling', 'iconset-weight');
553
554 $header = array(
555 t('Screenshot'),
556 t('Name'),
557 array('data' => t('Enabled'), 'class' => 'checkbox'),
558 t('Weight')
559 );
560
561 $rows = array();
562 foreach (element_children($form) as $key) {
563 // Only look for icon sets.
564 if (!isset($form[$key]['iconset'])) {
565 continue;
566 }
567
568 $element = &$form[$key];
569 $iconset = &$element['iconset']['#value'];
570
571 // Add special classes to be used with tabledrag.js.
572 $element['weight']['#attributes']['class'] .= 'iconset-weight';
573
574 // Make it only possible to check/uncheck standalone icon sets.
575 if ($iconset['type'] != 'iconset') {
576 $element['status']['#attributes']['disabled'] = 'disabled';
577 $element['status']['#value'] = '1';
578 }
579
580 // Style icon set info.
581 $info = '<div class="iconset-info"><h2>'. $iconset['name'] .'</h2><div class="description">'. $iconset['description'] .'</div></div>';
582
583 // Build row.
584 $row = array();
585 $row[] = drupal_render($element['screenshot']);
586 $row[] = $info;
587 $row[] = array('data' => drupal_render($element['status']), 'class' => 'checkbox', 'align' => 'center');
588 $row[] = drupal_render($element['weight']);
589 $rows[] = array('data' => $row, 'class' => 'draggable');
590 }
591 if (empty($rows)) {
592 $rows[] = array(array('data' => t('No icon sets available.'), 'colspan' => '4'));
593 }
594
595 // Build the table and render the form.
596 $output = theme('table', $header, $rows, array('id' => 'icon-iconsets')) . drupal_render($form);
597
598 return $output;
599 }
600
601 /**
602 * Theme function for displaying an icon.
603 *
604 * @param $name
605 * The name of the icon to display.
606 * @param $size
607 * (optional) The desired size of the icon to display.
608 * @return
609 * A string containing an image tag of the icon.
610 */
611 function theme_icon($name, $size = '32') {
612 global $icons;
613 return theme('image', $icons[$name][$size]);
614 }

  ViewVC Help
Powered by ViewVC 1.1.2