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

Contents of /contributions/modules/render/render.module

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


Revision 1.12 - (show annotations) (download) (as text)
Thu Oct 1 01:49:07 2009 UTC (8 weeks, 3 days ago) by sun
Branch: MAIN
CVS Tags: HEAD
Changes since 1.11: +121 -160 lines
File MIME type: text/x-php
by sun: Ported to Drupal 7.
1 <?php
2 // $Id: render.module,v 1.11 2009/01/17 20:13:55 sun Exp $
3
4 /**
5 * @file
6 * Implements an administrative interface for various CSS/JS-based rendering
7 * technologies.
8 *
9 * @todo Implement generic download handler for all generated files.
10 * @todo Allow different rules for each site.
11 */
12
13 /**
14 * Implementation of hook_menu().
15 */
16 function render_menu() {
17 $items['admin/config/media/render'] = array(
18 'title' => 'Dynamic Rendering',
19 'description' => 'Configure which elements in a theme are rendered.',
20 'access arguments' => array('administer site configuration'),
21 'page callback' => 'render_rules',
22 );
23 $items['admin/config/media/render/rules'] = array(
24 'title' => 'Rules',
25 'access arguments' => array('administer site configuration'),
26 'page callback' => 'render_rules',
27 'type' => MENU_DEFAULT_LOCAL_TASK,
28 'weight' => -2,
29 );
30 $items['admin/config/media/render/addrule'] = array(
31 'title' => 'Add rule',
32 'access arguments' => array('administer site configuration'),
33 'page callback' => 'drupal_get_form',
34 'page arguments' => array('render_add_rule'),
35 'type' => MENU_LOCAL_ACTION,
36 'weight' => -1,
37 );
38 $items['admin/config/media/render/manage'] = array(
39 'title' => 'Manage fonts',
40 'access arguments' => array('administer site configuration'),
41 'page callback' => 'render_manage',
42 'type' => MENU_LOCAL_TASK,
43 'weight' => 2,
44 );
45 $items['admin/config/media/render/cache-clear'] = array(
46 'title' => 'Clear cache',
47 'access arguments' => array('administer site configuration'),
48 'page callback' => 'render_flush_caches',
49 'page arguments' => array(TRUE),
50 'type' => MENU_LOCAL_TASK,
51 'weight' => 10,
52 );
53 $items['render/download'] = array(
54 'title' => 'Download font file',
55 'page callback' => 'render_octet_download',
56 'access arguments' => array('administer site configuration'),
57 'type' => MENU_CALLBACK,
58 );
59 $items['admin/config/media/render/edit/%'] = array(
60 'title' => 'Edit',
61 'access arguments' => array('administer site configuration'),
62 'page callback' => 'render_edit_rule',
63 'page arguments' => array(5),
64 );
65 $items['admin/config/media/render/duplicate'] = array(
66 'access arguments' => array('administer site configuration'),
67 'page callback' => 'render_duplicate_rule',
68 'type' => MENU_CALLBACK,
69 'weight' => 5,
70 );
71 $items['admin/config/media/render/rules/delete/%'] = array(
72 'title' => 'Delete rule',
73 'access arguments' => array('administer site configuration'),
74 'page callback' => 'drupal_get_form',
75 'page arguments' => array('render_delete_rule_form', 6),
76 );
77 $items['admin/config/media/render/deletefont'] = array(
78 'title' => 'Delete font',
79 'access arguments' => array('administer site configuration'),
80 'page callback' => 'render_fontdelete_confirm',
81 'type' => MENU_CALLBACK,
82 'weight' => 6,
83 );
84 return $items;
85 }
86
87 /**
88 * Implementation of hook_init().
89 */
90 function render_init() {
91 render_all_pages();
92 }
93
94 /**
95 * Retrieve all available plugins.
96 */
97 function render_plugins() {
98 static $plugins;
99
100 if (isset($plugins)) {
101 return $plugins;
102 }
103
104 $dir = drupal_get_path('module', 'render') .'/plugins';
105 $listing = file_scan_directory($dir, '/.+\.inc/', array('nomask' => '/(\.\.?|CVS|\.svn)$/', 'recurse' => FALSE));
106 $plugins = array();
107 foreach ($listing as $key => $file) {
108 include_once $file->uri;
109 $function = $file->name .'_render_info';
110 if (function_exists($function)) {
111 $plugin = $function();
112 $plugins[$plugin['name']] = $plugin;
113 }
114 }
115
116 return $plugins;
117 }
118
119 /**
120 * List the rules.
121 */
122 function render_rules() {
123 $plugins = render_plugins();
124 $rules = render_get_rules();
125 $renderdir = drupal_get_path('module', 'render');
126 $header = array(t('Plugin'), t('Rule Name'), t('CSS Selector'), t('Font'), t('Colors'), t('Weight'), t('Operations'));
127 $rows = array();
128 $img_edit = theme('image', $renderdir .'/images/editor.png');
129 $img_duplicate = theme('image', $renderdir .'/images/copy.png');
130 $img_delete = theme('image', $renderdir .'/images/delete.png');
131 foreach ($rules as $r) {
132 $function = $r['plugin'] .'_render_rules';
133 if (function_exists($function)) {
134 $fontstyle = $function($r);
135 }
136 $color_names = array(
137 'color' => t('Font color'),
138 'linkcolor' => t('Link color'),
139 'hovercolor' => t('Hover color'),
140 'bgcolor' => t('Background color'),
141 );
142 $colors = '';
143 foreach ($fontstyle['colors'] as $key => $color) {
144 $colors .= '<div class="render-font-color" style="background-color: '. $color .';" title="'. $color_names[$key] .'"> </div>';
145 }
146 $operations = array(
147 l($img_edit, 'admin/config/media/render/edit/'. $r['rid'], array('attributes' => array('title' => t('Edit'), 'class' => array('render-rule-op')), 'html' => TRUE)),
148 l($img_duplicate, 'admin/config/media/render/duplicate/'. $r['rid'], array('attributes' => array('title' => t('Duplicate'), 'class' => array('render-rule-op')), 'html' => TRUE)),
149 l($img_delete, 'admin/config/media/render/rules/delete/'. $r['rid'], array('attributes' => array('title' => t('Delete'), 'class' => array('render-rule-op')), 'html' => TRUE)),
150 );
151 $rows[] = array(
152 $plugins[$r['plugin']]['title'],
153 $r['name'],
154 $r['selector'],
155 $fontstyle['font'],
156 $colors,
157 $r['weight'],
158 implode('', $operations),
159 );
160 }
161 $output = theme('table', $header, $rows, array('class' => array('render-rules')));
162
163 // Display logos of supported plugins.
164 $output .= '<h4>'. t('Supported plugins') .'</h4>';
165 $plugin_links = array();
166 foreach ($plugins as $plugin) {
167 $logo = $renderdir .'/plugins/'. $plugin['name'] .'.png';
168 if (file_exists($logo)) {
169 $plugin_links[] = l(theme('image', $logo, $plugin['title'], $plugin['title'], array()), $plugin['url'], array('html' => TRUE));
170 }
171 else {
172 $plugin_links[] = l($plugin['title'], $plugin['url']);
173 }
174 }
175 $output .= implode(' | ', $plugin_links);
176
177 drupal_add_css($renderdir .'/render.css');
178
179 return $output;
180 }
181
182 /**
183 * Clear cache after module is enabled.
184 */
185 function render_enable() {
186 render_flush_caches();
187 }
188
189 /**
190 * Clear dynamic rendering cache.
191 */
192 function render_flush_caches($redirect = FALSE) {
193 // Re-scan plugin directories.
194 render_find_render(NULL, TRUE);
195
196 // Regenerate list of active plugins.
197 render_get_rules();
198
199 // Regenerate all JavaScript and Stylesheet files for all plugins and rules.
200 $active_plugins = variable_get('render_plugins', array());
201 foreach ($active_plugins as $plugin) {
202 render_render_rules_js($plugin);
203 render_css_screen($plugin);
204 }
205
206 if ($redirect) {
207 drupal_goto('admin/config/media/render');
208 }
209 }
210
211 /**
212 * Manage font files page; menu callback.
213 */
214 function render_manage() {
215 $delimg = theme('image', drupal_get_path('module', 'render') .'/images/delete.png');
216 $header = array(t('Font'), t('Location'), '');
217 $rows = array();
218 foreach (render_get_fonts() as $file => $name) {
219 $rows[] = array(
220 $name,
221 $file,
222 l($delimg .' '. t('delete'), 'admin/config/media/render/deletefont/'. urlencode($file), array('html' => TRUE)),
223 );
224 }
225 $output = theme('table', $header, $rows, array('style' => 'width:100%'));
226 $output .= drupal_render(drupal_get_form('render_upload'));
227 return $output;
228 }
229
230 /**
231 * Render a form to upload a new font file.
232 */
233 function render_upload($form, &$form_state) {
234 $form['#attributes'] = array('enctype' => 'multipart/form-data');
235
236 // Display help for each plugin.
237 $form['help'] = array(
238 '#markup' => '<p />',
239 );
240 $plugins = render_plugins();
241 foreach ($plugins as $plugin) {
242 $function = $plugin['name'] .'_render_help';
243 if (function_exists($function) && $plugin_help = $function()) {
244 $form['help'][$plugin['name']] = array(
245 '#type' => 'fieldset',
246 '#collapsible' => TRUE,
247 '#collapsed' => TRUE,
248 '#title' => t('@plugin instructions', array('@plugin' => $plugin['title'])),
249 );
250 $form['help'][$plugin['name']]['help'] = array(
251 '#markup' => $plugin_help,
252 );
253 }
254 }
255 $form['renderfile'] = array(
256 '#type' => 'file',
257 '#title' => t('Upload font file'),
258 '#description' => t('Select the font file that you created using the instructions above.'),
259 '#size' => 40,
260 );
261 $form['submit'] = array(
262 '#type' => 'submit',
263 '#value' => t('Save'),
264 );
265
266 return $form;
267 }
268
269 /**
270 * Form validation callback for render_upload form.
271 */
272 function render_upload_validate($form, &$form_state) {
273 // @todo Implement file mime type info in render plugins and return FALSE
274 // here, if the uploaded file is not supported by at least one plugin.
275 }
276
277 /**
278 * Form submit callback for render_upload form.
279 */
280 function render_upload_submit($form, &$form_state) {
281 $dir = 'public://render';
282 // Creates directory if needed.
283 if (file_prepare_directory($dir, FILE_CREATE_DIRECTORY)) {
284 if ($file = file_save_upload('renderfile', array(), $dir)) {
285 $file->status = FILE_STATUS_PERMANENT;
286 file_save($file);
287 drupal_set_message(t('New font file uploaded.'));
288 }
289 else {
290 drupal_set_message(t('File could not be saved.'), 'error');
291 }
292 }
293 else {
294 drupal_set_message(t('Cannot create render directory in files.'), 'error');
295 }
296 }
297
298 /**
299 * Create a new rendering rule.
300 *
301 * Since multiple rendering plugins can support the same font file types
302 * we need to ask for the plugin to use before a rule can be created.
303 */
304 function render_add_rule($form, &$form_state) {
305 $form['plugin_select'] = array(
306 '#type' => 'fieldset',
307 '#title' => t('Available plugins'),
308 );
309 $form['plugin'] = array(
310 '#type' => 'value',
311 '#required' => TRUE,
312 );
313 foreach (render_plugins() as $plugin) {
314 $function = $plugin['name'] .'_render_setup';
315 if (function_exists($function)) {
316 if ($function()) {
317 $form['plugin_select'][$plugin['name']] = array(
318 '#type' => 'radio',
319 '#title' => $plugin['title'],
320 '#name' => 'plugin',
321 '#return_value' => $plugin['name'],
322 );
323 }
324 }
325 }
326 $form['submit'] = array(
327 '#type' => 'submit',
328 '#value' => t('Next'),
329 );
330 return $form;
331 }
332
333 /**
334 * Form submit callback for render_add_rule form.
335 */
336 function render_add_rule_submit($form, &$form_state) {
337 if (isset($form_state['values']['plugin'])) {
338 drupal_goto('admin/config/media/render/edit/'. check_plain($form_state['values']['plugin']));
339 }
340 }
341
342 /**
343 * Edit a rendering rule; menu callback.
344 *
345 * @param $edit
346 * An array or object of rule parameters.
347 *
348 * @return
349 * A form to edit a rule.
350 */
351 function render_edit_rule($edit = NULL) {
352 if (is_numeric($edit)) {
353 if (isset($_POST['op']) && $_POST['op'] == t('Delete')) {
354 drupal_goto('admin/config/media/render/rules/delete/'. $edit);
355 }
356 else {
357 $rule = render_load_rule($edit);
358 }
359 }
360 else if (is_string($edit)) {
361 $rule['plugin'] = check_plain($edit);
362 }
363 $font_select = $rule = (array)$rule;
364 $rules = array();
365
366 // Override font colors for font selection.
367 $font_select['color'] = '#000000';
368 $font_select['linkcolor'] = '#000000';
369 $font_select['hovercolor'] = '#000000';
370 $font_select['bgcolor'] = '#FFFFFF';
371 $font_select['fontsize'] = '16px';
372 $fonts = render_get_fonts($rule['plugin']);
373 foreach ($fonts as $path => $name) {
374 $font_select['selector'] = '.render-font-'. str_replace(array(' ', '_', '.'), '-', $name);
375 $font_select['font'] = $path;
376 $rules[] = render_render_rule_js($font_select);
377 }
378
379 // Ensure that plugin is properly loaded.
380 $function = $rule['plugin'] .'_render_load';
381 if (function_exists($function)) {
382 $function();
383 }
384 // Certain plugins like sIFR require some basic CSS to work properly. Thus, we
385 // need to create the CSS and JS files, even if there are no rules yet.
386 render_css_screen($rule['plugin'], FALSE);
387 drupal_add_css(file_create_url('public://render/' . $rule['plugin'] . '-screen.css'), array('weight' => CSS_THEME, 'preprocess' => FALSE));
388 drupal_add_js(render_wrap_rules($rule['plugin'], $rules, TRUE), array('type' => 'inline', 'scope' => 'header'));
389
390 drupal_add_css(drupal_get_path('module', 'render') . '/render.css', array('preprocess' => FALSE));
391
392 return drupal_get_form('render_rule', $rule);
393 }
394
395 /**
396 * Form builder function for add/edit rule form.
397 *
398 * @param $edit
399 * An array containing a rendering rule.
400 */
401 function render_rule($form, &$form_state, $edit) {
402 if (!empty($edit['rid'])) {
403 $form['rid'] = array(
404 '#type' => 'hidden',
405 '#value' => $edit['rid'],
406 );
407 drupal_set_title(t('Edit replacement rule %name', array('%name' => $edit['name'])), PASS_THROUGH);
408 }
409 else {
410 drupal_set_title(t('Add a new rendering rule'));
411 }
412 $edit += array(
413 'name' => '',
414 'selector' => '',
415 'font' => '',
416 'weight' => 0,
417 );
418
419 $form['plugin'] = array(
420 '#type' => 'value',
421 '#value' => $edit['plugin'],
422 '#required' => TRUE,
423 );
424
425 $form['basics'] = array(
426 '#type' => 'fieldset',
427 '#title' => t('Basics'),
428 );
429 $form['basics']['name'] = array(
430 '#type' => 'textfield',
431 '#title' => t('Rule Name'),
432 '#default_value' => $edit['name'],
433 '#required' => TRUE,
434 '#description' => t('The human readable name for this rule.'),
435 );
436 $form['basics']['selector'] = array(
437 '#type' => 'textfield',
438 '#title' => t('Selector'),
439 '#default_value' => $edit['selector'],
440 '#description' => t('A CSS / HTML selector for the elements you want to replace.'),
441 '#required' => TRUE,
442 );
443 $form['basics']['selectorhelp'] = array(
444 '#type' => 'fieldset',
445 '#title' => t('CSS Selector Help'),
446 '#collapsible' => TRUE,
447 '#collapsed' => TRUE,
448 );
449 $form['basics']['selectorhelp']['contents'] = array(
450 '#markup' => '<p>'. t('The supported CSS selectors are !selector1, !selector2 and !selector3. Whitespace is used to select descendants. Whitespace should only be used for this, so instead of !whitespace1 use !whitespace2. Multiple selectors may be used by seperating them with a comma ("!separator").', array(
451 '!selector1' => '<code>#</code>',
452 '!selector2' => '<code>&gt;</code>',
453 '!selector3' => '<code>.</code>',
454 '!whitespace1' => '<code>#foo &gt; p</code>',
455 '!whitespace2' => '<code>#foo&gt;p</code>',
456 '!separator' => '<code>,</code>',
457 )) .'</p>',
458 );
459 $form['basics']['selectorhelp']['contents']['#markup'] .= '<p>'. t('Selectors are dependent on your theme. Examine the HTML source to find appropriate classes and ids being output by your current theme. The following are example selectors based on the default Garland theme:') .'</p>';
460 $form['basics']['selectorhelp']['contents']['#markup'] .= '<ul>'. t('!site-title-prefix Site title !suffix
461 !page-title-prefix Page title !suffix
462 !node-titles-prefix Node titles !suffix
463 !block-titles-prefix Block titles !suffix
464 !comment-titles-prefix Comment titles !suffix', array(
465 '!site-title-prefix' => '<li><a href="" onclick="document.getElementById(\'edit-selector\').value = \'#header h1 span\';return false;">',
466 '!page-title-prefix' => '<li><a href="" onclick="document.getElementById(\'edit-selector\').value = \'#tabs-wrapper h2\';return false;">',
467 '!node-titles-prefix' => '<li><a href="" onclick="document.getElementById(\'edit-selector\').value = \' .node h2\';return false;">',
468 '!block-titles-prefix' => '<li><a href="" onclick="document.getElementById(\'edit-selector\').value = \' .block h2\';return false;">',
469 '!comment-titles-prefix' => '<li><a href="" onclick="document.getElementById(\'edit-selector\').value = \' .comment h3\';return false;">',
470 '!suffix' => '</a></li>',
471 )) .'</ul>';
472
473 $form['basics']['weight'] = array(
474 '#type' => 'weight',
475 '#title' => t('Weight'),
476 '#description' => t('You can adjust the order in which the rules are executed by changing their weights. Lighter items are executed before heavier.'),
477 '#delta' => 10,
478 '#default_value' => $edit['weight'],
479 );
480
481 $function = $edit['plugin'] .'_render_rule';
482 if (function_exists($function)) {
483 $function($form, $edit);
484 }
485
486 $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Save'));
487 if (!empty($edit['rid'])) {
488 $form['actions']['delete'] = array('#type' => 'button', '#value' => t('Delete'));
489 }
490
491 return $form;
492 }
493
494 /**
495 * Form submit callback for render_rule form.
496 */
497 function render_rule_submit($form, &$form_state) {
498 render_save_rule($form_state['values']);
499
500 // Regenerate list of active plugins.
501 render_get_rules($form_state['values']['plugin'], TRUE);
502 // Create the CSS file.
503 render_css_screen($form_state['values']['plugin']);
504 // Create the JS file.
505 render_render_rules_js($form_state['values']['plugin']);
506 // Change query-strings on CSS/JS files to enforce reload for all users.
507 _drupal_flush_css_js();
508
509 $form_state['redirect'] = 'admin/config/media/render';
510 }
511
512 /**
513 * Form builder function for delete rule form.
514 */
515 function render_delete_rule_form($form, &$form_state, $rid = NULL) {
516 if (!isset($rid) || !($rule = render_load_rule($rid))) {
517 drupal_goto('admin/config/media/render');
518 }
519 $form['rid'] = array(
520 '#type' => 'hidden',
521 '#value' => $rule['rid'],
522 );
523 return confirm_form($form, t('Are you sure you want to delete the rule %rule?', array('%rule' => $rule['name'])), 'admin/config/media/render');
524 };
525
526 /**
527 * Form submit callback for delete rule form.
528 */
529 function render_delete_rule_form_submit($form, &$form_state) {
530 if ($form_state['values']['op'] == t('Confirm')) {
531 $rule = render_load_rule($form_state['values']['rid']);
532 db_query('DELETE FROM {render} WHERE rid = :rid', array(':rid' => $form_state['values']['rid']));
533 drupal_set_message(t('Rule %name has been deleted.', array('%name' => $rule['name'])));
534 render_render_rules_js($rule['plugin']);
535 render_css_screen($rule['plugin']);
536 }
537
538 $form_state['redirect'] = 'admin/config/media/render';
539 }
540
541 /**
542 * Delete font page; menu callback.
543 */
544 function render_fontdelete_confirm() {
545 $args = func_get_args();
546 $fontfile = implode('/', $args);
547
548 if (!$fontfile) {
549 drupal_goto('admin/config/media/render/manage');
550 }
551 return drupal_get_form('render_fontdelete_confirm_form', $fontfile);
552 }
553
554 /**
555 * Form builder function for delete font form.
556 */
557 function render_fontdelete_confirm_form($form, &$form_state, $fontfile) {
558 $form['render_fontfile'] = array('#type' => 'hidden', '#value' => $fontfile);
559 return confirm_form($form, t("Are you sure you want to delete the file &quot;%fontfile&quot;?", array('%fontfile' => $fontfile)), 'admin/config/media/render/manage');
560 }
561
562 /**
563 * Form submit callback for delete font form.
564 */
565 function render_fontdelete_confirm_form_submit($form, &$form_state) {
566 if ($form_state['values']['op'] == t('Confirm')) {
567 render_font_delete($form_state['values']['render_fontfile']);
568 }
569 $form_state['redirect'] = 'admin/config/media/render/manage';
570 }
571
572 /**
573 * Delete a font file.
574 */
575 function render_font_delete($fontfile) {
576 if (file_delete($fontfile)) {
577 drupal_set_message(t('The font %file has been deleted.', array('%file' => $fontfile)));
578 }
579 else {
580 drupal_set_message(t('There was a problem deleting the font file.'), 'error');
581 }
582 }
583
584 /**
585 * Return form radio items for font selection.
586 *
587 * @param array $form
588 * A form array containing rule definitions.
589 * @param string $field
590 * A field name to append the selection.
591 */
592 function render_font_select($form, $field) {
593 $fonts = render_get_fonts($form['plugin']);
594
595 $font_select = array();
596 foreach ($fonts as $path => $filename) {
597 $name = substr($filename, 0, strrpos($filename, '.'));
598 $font_select[][$field] = array(
599 '#type' => 'radio',
600 '#title' => '<span class="render-font-'. str_replace(array(' ', '_', '.'), '-', $filename) .' render-font">'. $filename .'</span>',
601 '#return_value' => $path,
602 '#default_value' => $form[$field],
603 );
604 }
605 return $font_select;
606 }
607
608 /**
609 * Scan files/render for font files.
610 *
611 * @return array
612 * An array with filepath => filename pairs.
613 *
614 * @todo Additionally search current theme for font files.
615 */
616 function render_get_fonts($plugin = NULL) {
617 $fonts = array();
618
619 // Build the file mask.
620 $mask = array();
621 $plugins = render_plugins();
622 if (isset($plugin)) {
623 if (isset($plugins[$plugin]) && !empty($plugins[$plugin]['file_masks'])) {
624 $mask = $plugins[$plugin]['file_masks'];
625 }
626 }
627 else {
628 foreach ($plugins as $plugin) {
629 if (!empty($plugin['file_masks'])) {
630 $mask = array_merge($mask, $plugin['file_masks']);
631 }
632 }
633 }
634 $mask = '/' . implode('|', $mask) . '/';
635
636 $files = file_scan_directory('public://render', $mask, array('recurse' => FALSE));
637 foreach ($files as $file) {
638 $fonts[$file->uri] = basename($file->name);
639 }
640
641 return $fonts;
642 }
643
644 /**
645 * Fetch rules from database.
646 *
647 * @param $plugin
648 * An optional plugin name to limit the query.
649 * @param $reset
650 * (optional) TRUE to reload rules from the database.
651 *
652 * @return
653 * An array of rules.
654 */
655 function render_get_rules($plugin = NULL, $reset = FALSE) {
656 static $rules = array();
657
658 if (!empty($rules) && !$reset) {
659 return $rules;
660 }
661
662 if (isset($plugin)) {
663 $result = db_query("SELECT * FROM {render} WHERE plugin = :plugin ORDER BY weight", array(':plugin' => $plugin), array('fetch' => PDO::FETCH_ASSOC));
664 }
665 else {
666 $result = db_query('SELECT * FROM {render} ORDER BY weight', array(), array('fetch' => PDO::FETCH_ASSOC));
667 }
668 foreach ($result as $rule) {
669 // Unserialize custom plugin properties.
670 $properties = unserialize($rule['properties']);
671 $rule = array_merge($rule, $properties);
672
673 $rules[$rule['rid']] = $rule;
674 }
675
676 // Store active plugins in a variable.
677 if (!isset($plugin)) {
678 $active_plugins = array();
679 foreach ($rules as $rule) {
680 $active_plugins[$rule['plugin']] = $rule['plugin'];
681 }
682 variable_set('render_plugins', $active_plugins);
683 }
684
685 return $rules;
686 }
687
688 /**
689 * Fetch a rule from the database.
690 */
691 function render_load_rule($rid) {
692 $rule = db_query('SELECT * FROM {render} WHERE rid = :rid', array(':rid' => $rid))->fetchAssoc();
693
694 // Unserialize custom plugin properties.
695 $properties = unserialize($rule['properties']);
696 $rule = array_merge($rule, $properties);
697
698 return $rule;
699 }
700
701 /**
702 * Save a rule in the database.
703 *
704 * @param $edit
705 * An array from the rule add/edit form.
706 */
707 function render_save_rule($edit) {
708 // Serialize custom plugin properties.
709 $plugins = render_plugins();
710 $fields = array_flip(_render_fields());
711 $properties = array();
712 foreach ($edit as $key => $val) {
713 if (in_array($key, $plugins[$edit['plugin']]['properties'])) {
714 $properties[$key] = $val;
715 unset($edit[$key]);
716 }
717 elseif (!isset($fields[$key])) {
718 unset($edit[$key]);
719 }
720 }
721 $edit['properties'] = serialize($properties);
722
723 db_merge('render')
724 ->key(array(
725 'rid' => !empty($edit['rid']) ? $edit['rid'] : NULL,
726 ))
727 ->fields($edit)
728 ->execute();
729
730 drupal_set_message(t('Rule %rule has been saved.', array('%rule' => $edit['name'])));
731 }
732
733 /**
734 * Duplicate an existing render rule.
735 *
736 * @param int $rid
737 * A rule id of an existing rule.
738 */
739 function render_duplicate_rule($rid) {
740 $rule = render_load_rule($rid);
741 $rule['name'] = $rule['name'] .' ('. t('Dupe') .')';
742 unset($rule['rid']);
743 render_save_rule($rule);
744
745 drupal_goto('admin/config/media/render');
746 }
747
748 /**
749 * Find the directory of each plugin and save locations in a variable.
750 *
751 * Recurses into the render.module's directory to find the shallowest
752 * file dependency of all plugins.
753 *
754 * This removes the need for administrators to rename a plugin's directory
755 * when they place it into the module directory.
756 *
757 * @param string $plugin
758 * An internal name of a plugin. (optional)
759 * @param bool $reset
760 * Whether to clear the plugin directory cache.
761 *
762 * @return array
763 * An array containing the directory for each plugin.
764 */
765 function render_find_render($plugin_name = NULL, $reset = FALSE) {
766 $plugins = render_plugins();
767
768 if (!$reset) {
769 $dirs = variable_get('render_plugin_dirs', array());
770 }
771 else {
772 variable_del('render_plugin_dirs');
773 $dirs = array();
774 }
775 // If plugin name is given and we already know the directory, return it.
776 if (isset($plugin_name) && isset($dirs[$plugin_name]) && $dirs[$plugin_name]) {
777 return $dirs[$plugin_name];
778 }
779
780 $dirs = array();
781 $renderdir = drupal_get_path('module', 'render');
782 foreach ($plugins as $name => $plugin) {
783 if (!isset($dirs[$name])) {
784 $mask = '/' . preg_quote($plugin['dependencies'][0], '/') . '/';
785 $files = file_scan_directory($renderdir, $mask);
786 // The first one is the shallowest, use it.
787 if ($file = array_shift($files)) {
788 $dirs[$name] = substr($file->uri, 0, strlen($file->uri) - strlen(basename($file->uri)) - 1);
789 }
790 }
791 }
792
793 // Cache the results.
794 if (!empty($dirs)) {
795 variable_set('render_plugin_dirs', $dirs);
796 }
797
798 // If plugin name is given, return only that plugin directory.
799 if (isset($plugin_name)) {
800 if (isset($dirs[$plugin_name])) {
801 return $dirs[$plugin_name];
802 }
803 else {
804 // Plugin is malformed, directory was not found, or name does not exist.
805 return FALSE;
806 }
807 }
808 else {
809 return !empty($dirs) ? $dirs : FALSE;
810 }
811 }
812
813 /**
814 * Add JavaScripts and Stylesheets for all active plugins to all pages.
815 */
816 function render_all_pages() {
817 static $done;
818 if ($done) {
819 return;
820 }
821 $plugins = render_plugins();
822 $active_plugins = variable_get('render_plugins', array());
823 foreach ($plugins as $plugin) {
824 if (in_array($plugin['name'], $active_plugins)) {
825 $function = $plugin['name'] .'_render_load';
826 if (function_exists($function)) {
827 $function();
828 }
829 drupal_add_css('public://render/' . $plugin['name'] .'-screen.css', array('weight' => CSS_THEME));
830 drupal_add_js('public://render/' . $plugin['name'] .'-rules.js');
831 }
832 }
833 $done = true;
834 }
835
836 /**
837 * Save rendering JavaScript files for each plugin.
838 */
839 function render_render_rules_js($edited_plugin = NULL) {
840 // Hide messages from other users.
841 $verbose = user_access('administer site configuration') ? TRUE : FALSE;
842
843 $dir = 'public://render';
844 if (!file_prepare_directory($dir, FILE_CREATE_DIRECTORY)) {
845 if ($verbose) {
846 drupal_set_message(t('Cannot create directory %render.', array('%render' => $dir)), 'error');
847 }
848 return FALSE;
849 }
850
851 $rules = array();
852
853 // Render JavaScript rules for each plugin.
854 foreach (render_get_rules($edited_plugin) as $rule) {
855 $rules[$rule['plugin']][] = render_render_rule_js($rule);
856 }
857
858 // Wrap rules with execution handler and save to files.
859 foreach ($rules as $plugin => $plugin_rules) {
860 $plugin_js = render_wrap_rules($plugin, $plugin_rules);
861 $filename = $plugin .'-rules.js';
862 if ($file = file_unmanaged_save_data($plugin_js, $dir .'/'. $filename, FILE_EXISTS_REPLACE)) {
863 if ($verbose) {
864 drupal_set_message(t('JavaScript file !file has been saved.', array('!file' => l($filename, file_create_url($dir .'/'. $filename)))));
865 }
866 }
867 elseif ($verbose) {
868 drupal_set_message(t('JavaScript file !file could not be saved.', array('!file' => $dir .'/'. $filename)), 'error');
869 }
870 }
871 }
872
873 /**
874 * Return a single JavaScript rendering rule.
875 */
876 function render_render_rule_js($rule) {
877 $plugins = render_plugins();
878 $function = $rule['plugin'] .'_render_render_rule_js';
879 if (function_exists($function)) {
880 return $function($rule);
881 }
882 else {
883 $message = t("Error: @plugin does not provide a method to build JavaScript rendering invocations.", array('@plugin' => $rule['plugin']));
884 drupal_set_message($message, 'error');
885 return ' /* '. $message ." */\n";
886 }
887 }
888
889 /**
890 * Wrap execution handler around JavaScript rules.
891 */
892 function render_wrap_rules($plugin, $rules, $inline = FALSE) {
893 $output = '';
894 if (is_array($rules)) {
895 $plugins = render_plugins();
896 $function = $plugin .'_render_wrap_rules';
897 if (function_exists($function)) {
898 $output .= $function($rules);
899 }
900 }
901 return $output;
902 }
903
904 /**
905 * Create a stylesheet file for a plugin.
906 *
907 * @param string $plugin
908 * A plugin name.
909 * @param bool $verbose
910 * Whether to output positive status messages, defaults to TRUE.
911 * @param bool $print
912 * Whehter to output the contents directly, defaults to FALSE.
913 */
914 function render_css_screen($plugin, $verbose = TRUE, $print = FALSE) {
915 // Hide messages from other users.
916 if (!user_access('administer site configuration')) {
917 $verbose = FALSE;
918 }
919
920 if ($print) {
921 header("Content-Type: text/css");
922 header("Expires: Sun, 19 Nov 1978 05:00:00 GMT");
923 header("Last-Modified: ". gmdate("D, d M Y H:i:s") ." GMT");
924 header("Cache-Control: no-store, no-cache, must-revalidate");
925 header("Cache-Control: post-check=0, pre-check=0", FALSE);
926 header("Pragma: no-cache");
927 }
928
929 $plugin = $plugin;
930 $plugins = render_plugins();
931 $function = $plugin .'_render_css_screen';
932 if (function_exists($function)) {
933 $output = $function(render_get_rules($plugin));
934 }
935 else {
936 $message = t("Error: @plugin does not provide a method to build a CSS file.", array('@plugin' => $plugin));
937 if ($verbose) {
938 drupal_set_message($message, 'error');
939 }
940 return ' /* '. $message ." */\n";
941 }
942
943 $dir = 'public://render';
944 if (file_prepare_directory($dir, FILE_CREATE_DIRECTORY)) {
945 $filename = $plugin .'-screen.css';
946 if ($file = file_unmanaged_save_data($output, $dir .'/'. $filename, FILE_EXISTS_REPLACE)) {
947 if ($verbose) {
948 drupal_set_message(t('CSS file !file has been saved.', array('!file' => l($filename, file_create_url($dir .'/'. $filename)))));
949 }
950 }
951 else {
952 if ($verbose) {
953 drupal_set_message(t('CSS file !file could not be saved.', array('!file' => $dir .'/'. $filename)), 'error');
954 }
955 }
956 }
957 else {
958 if ($verbose) {
959 drupal_set_message(t('Cannot create directory %render.', array('%render' => $dir)), 'error');
960 }
961 }
962 if ($print) {
963 print $output;
964 }
965 }
966
967 /**
968 * Creates a download link for render_octet_download().
969 *
970 * @param string $plugin_name
971 * A name of a plugin.
972 * @param string $filename
973 * A filename of a plugin to link to.
974 *
975 * @return string
976 * An already rendered HTML link.
977 */
978 function render_download_link($plugin_name, $filename) {
979 $plugindir = render_find_render($plugin_name);
980 if (!$plugindir) {
981 $plugindir = drupal_get_path('module', 'render') .'/'. $plugin_name;
982 }
983 return l($filename, 'render/download/'. urlencode($plugindir .'/'. $filename));
984 }
985
986 /**
987 * Downloads files using application/octet-stream mime type.
988 *
989 * Makes text files download rather than appearing in browser.
990 *
991 * @param string $file
992 * The file to be downloaded.
993 */
994 function render_octet_download() {
995 $args = func_get_args();
996 $file = implode('/', $args);
997 if (user_access('administer site configuration') && is_file($file)) {
998 header('Content-Type: application/octet-stream');
999 header('Content-Disposition: attachment; filename="'. basename($file) .'"');
1000 ob_start();
1001 include $file;
1002 $contents = ob_get_contents();
1003 ob_end_clean();
1004 print $contents;
1005 }
1006 else {
1007 drupal_not_found();
1008 }
1009 }
1010
1011 /**
1012 * Return database fields that make up a rendering rule.
1013 */
1014 function _render_fields() {
1015 return array('rid', 'plugin', 'name', 'selector', 'properties', 'weight');
1016 }
1017

  ViewVC Help
Powered by ViewVC 1.1.2