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