ISSO is no longer a product regularly released so we'll remove the issoversion tag...
[isso.git] / printer_css.php
1 <?php
2 /*=====================================================================*\
3 || ###################################################################
4 || # Blue Static ISSO Framework
5 || # Copyright ©2002-[#]year[#] Blue Static
6 || #
7 || # This program is free software; you can redistribute it and/or modify
8 || # it under the terms of the GNU General Public License as published by
9 || # the Free Software Foundation; version [#]gpl[#] of the License.
10 || #
11 || # This program is distributed in the hope that it will be useful, but
12 || # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 || # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 || # more details.
15 || #
16 || # You should have received a copy of the GNU General Public License along
17 || # with this program; if not, write to the Free Software Foundation, Inc.,
18 || # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
19 || ###################################################################
20 \*=====================================================================*/
21
22 /**
23 * Printer - CSS Blocks
24 * printer_css.php
25 *
26 * @package ISSO
27 */
28
29 require_once('ISSO/Functions.php');
30
31 /**
32 * Printer - CSS Blocks
33 *
34 * This framework works in conjunction with ISSO.Printer to create a unique
35 * CSS editor enviornment.
36 *
37 * Hooks:
38 * $this->fetch_modified_link_hook - Required hook that is run when
39 * preparing revert links for properties
40 *
41 * @author Blue Static
42 * @copyright Copyright ©2002 - [#]year[#], Blue Static
43 * @version $Revision$
44 * @package ISSO
45 *
46 */
47 class Printer_CSS
48 {
49 /**
50 * CSS block list
51 * @var array
52 */
53 private $descriptors = array();
54
55 /**
56 * Master data set
57 * @var array
58 */
59 private $masterdata = array();
60
61 /**
62 * Cutomized data set
63 * @var array
64 */
65 private $customdata = array();
66
67 /**
68 * Valid properties that can be used in CSS
69 * @var array
70 */
71 private $properties = array(
72 'background' => 'background',
73 'color' => 'color',
74 'font-style' => 'font_style',
75 'font-size' => 'font_size',
76 'font-family' => 'font_family',
77 'text-decoration' => 'text_decoration'
78 );
79
80 /**
81 * Hook ran in fetch_modified_link; takes three params: $descriptor, $property, $name
82 * @var string
83 */
84 private $modifiedLinkHook = ':undefined:';
85
86 // ###################################################################
87 /**
88 * Constructor
89 */
90 public function __construct()
91 {
92 BSRegister::RequiredModules(array('Db', 'Printer'));
93 }
94
95 // ###################################################################
96 /**
97 * Sets the function callback that creates a link for modified properties
98 * in the CSS blocks display. It should have the signature:
99 * public string _M(string $descriptor, string $property, string $name)
100 *
101 * @param string Callback function
102 */
103 public function setModifiedLinkHook($callback)
104 {
105 $this->modifiedLinkHook = $callback;
106 }
107
108 // ###################################################################
109 /**
110 * Adds a CSS information block to the array for later use
111 *
112 * @param string Block title to display in thead
113 * @param string CSS class/descriptor/element name
114 * @param bool Show the link CSS information
115 */
116 public function add_block($title, $descriptor, $dolink)
117 {
118 if (isset($this->descriptors["$descriptor"]))
119 {
120 trigger_error('The descriptor "' . $descriptor . '" already exists');
121 return;
122 }
123
124 $this->descriptors["$descriptor"] = array(
125 'title' => $title,
126 'descriptor' => $descriptor,
127 'dolink' => $dolink
128 );
129 }
130
131 // ###################################################################
132 /**
133 * Sets a master data key for a given descriptor and property
134 *
135 * @param string Descriptor
136 * @param string Property
137 * @param string Value
138 */
139 public function set_master_data($descriptor, $property, $value)
140 {
141 $this->masterdata["$descriptor"]["$property"] = $value;
142 }
143
144 // ###################################################################
145 /**
146 * Sets a custom data key for a given descriptor and property
147 *
148 * @param string Descriptor
149 * @param string Property
150 * @param string Value
151 */
152 public function set_custom_data($descriptor, $property, $value)
153 {
154 $this->customdata["$descriptor"]["$property"] = $value;
155 }
156
157 // ###################################################################
158 /**
159 * Generates the HTML needed to output the CSS editing blocks; this is
160 * done in the form of using ISSO.Printer
161 */
162 public function generate_blocks()
163 {
164 $print =& BSRegister::GetType('Printer');
165
166 $lang = array(
167 'standard_css_attributes' => _('Standard CSS Attributes'),
168 'extra_css_attributes' => _('Extra CSS Attributes'),
169
170 'links_normal' => _('Normal CSS Links'),
171 'links_visited' => _('Visited CSS Links'),
172 'links_hover' => _('Hover CSS Links'),
173
174 'background' => _('Background'),
175 'font_color' => _('Font Color'),
176 'font_style' => _('Font Style'),
177 'font_size' => _('Font Size'),
178 'font_family' => _('Font Family'),
179
180 'text_decoration' => _('Text Decoration'),
181
182 'css_selector' => _('CSS Selector'),
183 'save_css' => _('Save CSS')
184 );
185
186 foreach ($this->descriptors AS $descriptor)
187 {
188 $value = array();
189 $status = array();
190
191 $desc = $descriptor['descriptor'];
192
193 $print->table_start();
194 $print->table_head($descriptor['title']);
195
196 foreach ($this->properties AS $prop => $name)
197 {
198 $value["$name"] = $this->fetch_value($descriptor['descriptor'], $prop);
199 $status["$name"] = $this->fetch_modified_status($descriptor['descriptor'], $prop);
200 }
201
202 $value['extra'] = $this->fetch_value($descriptor['descriptor'], 'extra');
203
204 $html = "<table cellspacing=\"0\" cellpadding=\"4\" border=\"0\" width=\"100%\">
205 <tr valign=\"top\">
206 <td width=\"50%\">
207 <fieldset>
208 <legend><strong>$lang[standard_css_attributes]</strong></legend>
209
210 <table cellspacing=\"0\" cellpadding=\"2\" border=\"0\" width=\"100%\">
211 <tr>
212 <td width=\"45%\">" . $this->fetch_modified_link($desc, 'background', $lang['background']) . "</td>
213 <td><input name=\"css[$descriptor[descriptor]][background]\" class=\"input\" style=\"width: 100%\" value=\"$value[background]\" /></td>
214 </tr>
215 <tr>
216 <td width=\"45%\">" . $this->fetch_modified_link($desc, 'color', $lang['font_color']) . "</td>
217 <td><input name=\"css[$descriptor[descriptor]][color]\" class=\"input\" style=\"width: 100%\" value=\"$value[color]\" /></td>
218 </tr>
219 <tr>
220 <td width=\"45%\">" . $this->fetch_modified_link($desc, 'font-style', $lang['font_style']) . "</td>
221 <td><input name=\"css[$descriptor[descriptor]][font-style]\" class=\"input\" style=\"width: 100%\" value=\"$value[font_style]\" /></td>
222 </tr>
223 <tr>
224 <td width=\"45%\">" . $this->fetch_modified_link($desc, 'font-size', $lang['font_size']) . "</td>
225 <td><input name=\"css[$descriptor[descriptor]][font-size]\" class=\"input\" style=\"width: 100%\" value=\"$value[font_size]\" /></td>
226 </tr>
227 <tr>
228 <td width=\"45%\">" . $this->fetch_modified_link($desc, 'font-family', $lang['font_family']) . "</td>
229 <td><input name=\"css[$descriptor[descriptor]][font-family]\" class=\"input\" style=\"width: 100%\" value=\"$value[font_family]\" /></td>
230 </tr>
231 </table>
232 </fieldset>
233 </td>
234
235 <td>
236 <fieldset style=\"height: 115px\">
237 <legend><strong>" . $this->fetch_modified_link($desc, 'extra', $lang['extra_css_attributes']) . "</strong></legend>
238 <textarea name=\"css[$descriptor[descriptor]][extra]\" style=\"width: 100%; height: 90%\">$value[extra]</textarea>
239 </fieldset>
240 </td>
241 </tr>
242 </table>";
243 if ($descriptor['dolink'])
244 {
245 foreach (array('a:link' => 'a_link', 'a:visited' => 'a_visited', 'a:hover' => 'a_hover') AS $sel => $selname)
246 {
247 foreach (array('background' => 'background', 'color' => 'color', 'text-decoration' => 'text_decoration') AS $prop => $name)
248 {
249 $value["{$selname}_{$name}"] = $this->fetch_value($descriptor['descriptor'] . ' ' . $sel, $prop);
250 $status["{$selname}_{$name}"] = $this->fetch_modified_status($descriptor['descriptor'] . ' ' . $sel, $prop);
251 }
252 }
253
254 $html .= "
255
256 <table cellspacing=\"0\" cellpadding=\"4\" border=\"0\" width=\"100%\">
257 <tr valign=\"top\">
258 <td width=\"33%\">
259 <fieldset>
260 <legend><strong>$lang[links_normal]</strong></legend>
261
262 <table cellspacing=\"0\" cellpadding=\"2\" border=\"0\" width=\"100%\">
263 <tr>
264 <td width=\"45%\">" . $this->fetch_modified_link($desc . ' a:link', 'background', $lang['background']) . "</td>
265 <td><input name=\"css[$descriptor[descriptor] a:link][background]\" class=\"input\" style=\"width: 100%\" value=\"$value[a_link_background]\" /></td>
266 </tr>
267 <tr>
268 <td width=\"45%\">" . $this->fetch_modified_link($desc . ' a:link', 'color', $lang['font_color']) . "</td>
269 <td><input name=\"css[$descriptor[descriptor] a:link][color]\" class=\"input\" style=\"width: 100%\" value=\"$value[a_link_color]\" /></td>
270 </tr>
271 <tr>
272 <td width=\"45%\">" . $this->fetch_modified_link($desc . ' a:link', 'text-decoration', $lang['text_decoration']) . "</td>
273 <td><input name=\"css[$descriptor[descriptor] a:link][text-decoration]\" class=\"input\" style=\"width: 100%\" value=\"$value[a_link_text_decoration]\" /></td>
274 </tr>
275 </table>
276 </fieldset>
277 </td>
278
279 <td width=\"33%\">
280 <fieldset>
281 <legend><strong>$lang[links_visited]</strong></legend>
282
283 <table cellspacing=\"0\" cellpadding=\"2\" border=\"0\" width=\"100%\">
284 <tr>
285 <td width=\"45%\">" . $this->fetch_modified_link($desc . ' a:visited', 'background', $lang['background']) . "</td>
286 <td><input name=\"css[$descriptor[descriptor] a:visited][background]\" class=\"input\" style=\"width: 100%\" value=\"$value[a_visited_background]\" /></td>
287 </tr>
288 <tr>
289 <td width=\"45%\">" . $this->fetch_modified_link($desc . ' a:visited', 'color', $lang['font_color']) . "</td>
290 <td><input name=\"css[$descriptor[descriptor] a:visited][color]\" class=\"input\" style=\"width: 100%\" value=\"$value[a_visited_color]\" /></td>
291 </tr>
292 <tr>
293 <td width=\"45%\">" . $this->fetch_modified_link($desc . ' a:visited', 'text-decoration', $lang['text_decoration']) . "</td>
294 <td><input name=\"css[$descriptor[descriptor] a:visited][text-decoration]\" class=\"input\" style=\"width: 100%\" value=\"$value[a_visited_text_decoration]\" /></td>
295 </tr>
296 </table>
297 </fieldset>
298 </td>
299
300 <td width=\"33%\">
301 <fieldset>
302 <legend><strong>$lang[links_hover]</strong></legend>
303
304 <table cellspacing=\"0\" cellpadding=\"2\" border=\"0\" width=\"100%\">
305 <tr>
306 <td width=\"45%\">" . $this->fetch_modified_link($desc . ' a:hover', 'background', $lang['background']) . "</td>
307 <td><input name=\"css[$descriptor[descriptor] a:hover][background]\" class=\"input\" style=\"width: 100%\" value=\"$value[a_hover_background]\" /></td>
308 </tr>
309 <tr>
310 <td width=\"45%\">" . $this->fetch_modified_link($desc . ' a:hover', 'color', $lang['font_color']) . "</td>
311 <td><input name=\"css[$descriptor[descriptor] a:hover][color]\" class=\"input\" style=\"width: 100%\" value=\"$value[a_hover_color]\" /></td>
312 </tr>
313 <tr>
314 <td width=\"45%\">" . $this->fetch_modified_link($desc . ' a:hover', 'text-decoration', $lang['text_decoration']) . "</td>
315 <td><input name=\"css[$descriptor[descriptor] a:hover][text-decoration]\" class=\"input\" style=\"width: 100%\" value=\"$value[a_hover_text_decoration]\" /></td>
316 </tr>
317 </table>
318 </fieldset>
319 </td>
320 </tr>
321 </table>";
322 }
323
324 $print->row_span($html, 'alt2', 'left', 1);
325
326 $print->row_span('
327 <div class="alt1" style="border: inset 1px; padding: 2px 5px 2px 5px; float: left">' . $lang['css_selector'] . ': <code>' . $descriptor['descriptor'] . '</code></div>
328 <input type="submit" name="submit" value="' . $lang['save_css'] . '" class="button" />', 'tfoot', 'right', 1);
329
330 $print->table_end();
331 }
332 }
333
334 // ###################################################################
335 /**
336 * Returns the value of a given descriptor and property by comparing
337 * the mater set and custom set then returning the right one
338 *
339 * @param string Descriptor
340 * @param string Property
341 *
342 * @return string Value of the given property
343 */
344 private function fetch_value($descriptor, $property)
345 {
346 if (!isset($this->customdata["$descriptor"]["$property"]))
347 {
348 return $this->masterdata["$descriptor"]["$property"];
349 }
350 else
351 {
352 return $this->customdata["$descriptor"]["$property"];
353 }
354 }
355
356 // ###################################################################
357 /**
358 * Returns the state modified state (false for untouched and true
359 * for modified) from the descriptor-property value between the master
360 * set and the custom set of data
361 *
362 * @param string Descriptor
363 * @param string Property
364 *
365 * @return bool Modified from the master value?
366 */
367 private function fetch_modified_status($descriptor, $property)
368 {
369 return ($this->masterdata["$descriptor"]["$property"] != $this->customdata["$descriptor"]["$property"] AND isset($this->customdata["$descriptor"]["$property"]));
370 }
371
372 // ###################################################################
373 /**
374 * Fetches a link that shows a revert link for a given property
375 * that uses AJAX to revert when clicked
376 *
377 * @param string Descriptor
378 * @param string Property
379 * @param string Nominalized text
380 *
381 * @return string Output HTML
382 */
383 private function fetch_modified_link($descriptor, $property, $name)
384 {
385 $status = $this->fetch_modified_status($descriptor, $property);
386
387 if ($status)
388 {
389 if (is_callable($this->modifiedLinkHook))
390 {
391 return call_user_func($this->modifiedLinkHook, $descriptor, $property, $name);
392 }
393 else
394 {
395 trigger_error('Printer_CSS::fetch_modified_link() needs to have the fetchModifiedLinkHook( $descriptor , $property , $name ) defined');
396 }
397 }
398 else
399 {
400 return $name;
401 }
402 }
403
404 // ###################################################################
405 /**
406 * Generates an array of queries that should be run on your database to
407 * update CSS changes. All of the queries have sprintf() markers that
408 * need to be evaluated:
409 *
410 * %1$s - Database table
411 * %2$s - styleid field
412 * %3$s - descriptor field
413 * %4$s - property field
414 * %5$s - value field
415 * %6$d - Styleid value
416 *
417 * @param array Array of user-inputted information to be transformed into queries
418 *
419 * @return array Queries that need to be evaluated then ran
420 */
421 public function generate_change_query($data)
422 {
423 $queries[0] = '--- RESERVED FOR LATER USE ---';
424
425 $deletes = array();
426
427 foreach ($this->descriptors AS $descriptor => $opts)
428 {
429 $dolink = $opts['dolink'];
430
431 if ($dolink)
432 {
433 $loops = array('', ' a:link', ' a:visited', ' a:hover');
434 }
435 else
436 {
437 $loops = array('');
438 }
439
440 foreach ($loops AS $sel)
441 {
442 foreach ($data["$descriptor$sel"] AS $prop => $value)
443 {
444 // the given value matches the master -- no change
445 if ($this->masterdata["$descriptor$sel"]["$prop"] == $value)
446 {
447 continue;
448 }
449 // the given matches the custom -- no change
450 else if (isset($this->customdata["$descriptor$sel"]["$prop"]) AND $this->customdata["$descriptor$sel"]["$prop"] == $value)
451 {
452 continue;
453 }
454 // no matching, it's new
455 else
456 {
457 $value = str_replace('%', '%%', $value);
458 $deletes[] = "%3\$s = '" . $this->_escape($descriptor . $sel) . "' AND %4\$s = '" . $this->_escape($prop) . "'";
459 $queries[] = "INSERT INTO %1\$s (%2\$s, %3\$s, %4\$s, %5\$s) VALUES (%6\$d, '" . $this->_escape($descriptor . $sel) . "', '" . $this->_escape($prop) . "', '" . $this->_escape($value) . "')";
460 }
461 }
462 }
463 }
464
465 if (sizeof($deletes) < 1)
466 {
467 $queries[0] = '##';
468 }
469 else
470 {
471 $queries[0] = "DELETE FROM %1\$s WHERE styleid = %6\$d AND ((" . implode(') OR (', $deletes) . "))";
472 }
473
474 return $queries;
475 }
476
477 // ###################################################################
478 /**
479 * Wrapper for BSDb->escapeString()
480 *
481 * @param string Unprotected string
482 *
483 * @return string Sanitized string
484 */
485 private function _escape($string)
486 {
487 return BSRegister::GetType('Db')->escapeString($string);
488 }
489
490 // ###################################################################
491 /**
492 * Generates a linkable/usable CSS stylehseet content file; this can
493 * be outputted to the browser
494 *
495 * @return string CSS output
496 */
497 public function generate_css_output()
498 {
499 $data = array();
500
501 foreach ($this->descriptors AS $descriptor => $opts)
502 {
503 $dolink = $opts['dolink'];
504
505 if ($dolink)
506 {
507 $loops = array('', ' a:link', ' a:visited', ' a:hover');
508 }
509 else
510 {
511 $loops = array('');
512 }
513
514 foreach ($loops AS $sel)
515 {
516 foreach ($this->masterdata["$descriptor$sel"] AS $prop => $value)
517 {
518 $data["$descriptor$sel"]["$prop"] = $value;
519 }
520 if (is_array($this->customdata["$descriptor$sel"]))
521 {
522 foreach ($this->customdata["$descriptor$sel"] AS $prop => $value)
523 {
524 $data["$descriptor$sel"]["$prop"] = $value;
525 }
526 }
527 }
528 }
529
530 $output = '/* CSS Style Sheet (generated by ISSO.Printer.CSS $Revision$) */';
531
532 foreach ($data AS $selector => $props)
533 {
534 $output .= "\n\n$selector\n{";
535 foreach ($props AS $name => $value)
536 {
537 if ($name != 'extra' AND $value != '')
538 {
539 $output .= str_replace('&quot;', '"', "\n\t$name: $value;");
540 }
541 }
542
543 if ($props['extra'])
544 {
545 $extra = explode("\n", BSFunctions::ConvertLineBreaks($props['extra']));
546
547 foreach ($extra AS $prop)
548 {
549 $output .= "\n\t$prop";
550 }
551 }
552
553 $output .= "\n}";
554 }
555
556 return $output;
557 }
558 }
559
560 /*=====================================================================*\
561 || ###################################################################
562 || # $HeadURL$
563 || # $Id$
564 || ###################################################################
565 \*=====================================================================*/
566 ?>