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