]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - actions/designadminpanel.php
Merge branch '0.9.x' into activityexport
[quix0rs-gnu-social.git] / actions / designadminpanel.php
1 <?php
2 /**
3  * StatusNet, the distributed open-source microblogging tool
4  *
5  * Design administration panel
6  *
7  * PHP version 5
8  *
9  * LICENCE: This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU Affero General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Affero General Public License for more details.
18  *
19  * You should have received a copy of the GNU Affero General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  * @category  Settings
23  * @package   StatusNet
24  * @author    Evan Prodromou <evan@status.net>
25  * @author    Zach Copley <zach@status.net>
26  * @author    Sarven Capadisli <csarven@status.net>
27  * @copyright 2008-2009 StatusNet, Inc.
28  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
29  * @link      http://status.net/
30  */
31
32 if (!defined('STATUSNET')) {
33     exit(1);
34 }
35
36 /**
37  * Administer design settings
38  *
39  * @category Admin
40  * @package  StatusNet
41  * @author   Evan Prodromou <evan@status.net>
42  * @author   Zach Copley <zach@status.net>
43  * @author   Sarven Capadisli <csarven@status.net>
44  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
45  * @link     http://status.net/
46  */
47
48 class DesignadminpanelAction extends AdminPanelAction
49 {
50
51     /* The default site design */
52     var $design = null;
53
54     /**
55      * Returns the page title
56      *
57      * @return string page title
58      */
59
60     function title()
61     {
62         // TRANS: Message used as title for design settings for the site.
63         return _('Design');
64     }
65
66     /**
67      * Instructions for using this form.
68      *
69      * @return string instructions
70      */
71
72     function getInstructions()
73     {
74         return _('Design settings for this StatusNet site');
75     }
76
77     /**
78      * Get the default design and show the design admin panel form
79      *
80      * @return void
81      */
82
83     function showForm()
84     {
85         $this->design = Design::siteDesign();
86         $form = new DesignAdminPanelForm($this);
87         $form->show();
88         return;
89     }
90
91     /**
92      * Save settings from the form
93      *
94      * @return void
95      */
96
97     function saveSettings()
98     {
99         if ($this->arg('save')) {
100             $this->saveDesignSettings();
101         } else if ($this->arg('defaults')) {
102             $this->restoreDefaults();
103         } else {
104             $this->clientError(_('Unexpected form submission.'));
105         }
106     }
107
108     /**
109      * Save the new design settings
110      *
111      * @return void
112      */
113
114     function saveDesignSettings()
115     {
116         // Workaround for PHP returning empty $_POST and $_FILES when POST
117         // length > post_max_size in php.ini
118
119         if (empty($_FILES)
120             && empty($_POST)
121             && ($_SERVER['CONTENT_LENGTH'] > 0)
122         ) {
123             $msg = _('The server was unable to handle that much POST ' .
124                 'data (%s bytes) due to its current configuration.');
125             $this->clientException(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
126             return;
127         }
128
129         // check for file uploads
130
131         $bgimage = $this->saveBackgroundImage();
132         $customTheme = $this->saveCustomTheme();
133
134         $oldtheme = common_config('site', 'theme');
135         if ($customTheme) {
136             // This feels pretty hacky :D
137             $this->args['theme'] = $customTheme;
138             $themeChanged = true;
139         } else {
140             $themeChanged = ($this->trimmed('theme') != $oldtheme);
141         }
142
143         static $settings = array('theme', 'logo');
144
145         $values = array();
146
147         foreach ($settings as $setting) {
148             $values[$setting] = $this->trimmed($setting);
149         }
150
151         $this->validate($values);
152
153         $config = new Config();
154
155         $config->query('BEGIN');
156
157         if ($themeChanged) {
158             // If the theme has changed, reset custom colors and let them pick
159             // up the new theme's defaults.
160             $colors = array('background', 'content', 'sidebar', 'text', 'link');
161             foreach ($colors as $colorKey) {
162                 // Clear from global config so we see defaults on this page...
163                 $GLOBALS['config']['design'][$colorKey . 'color'] = false;
164
165                 // And remove old settings from DB...
166                 $this->deleteSetting('design', $colorKey . 'color');
167             }
168         } else {
169             // Only save colors from the form if the theme has not changed.
170             //
171             // @fixme a future more ajaxy form should allow theme switch
172             // and color customization in one step.
173
174             $bgcolor = new WebColor($this->trimmed('design_background'));
175             $ccolor  = new WebColor($this->trimmed('design_content'));
176             $sbcolor = new WebColor($this->trimmed('design_sidebar'));
177             $tcolor  = new WebColor($this->trimmed('design_text'));
178             $lcolor  = new WebColor($this->trimmed('design_links'));
179
180             Config::save('design', 'backgroundcolor', $bgcolor->intValue());
181             Config::save('design', 'contentcolor', $ccolor->intValue());
182             Config::save('design', 'sidebarcolor', $sbcolor->intValue());
183             Config::save('design', 'textcolor', $tcolor->intValue());
184             Config::save('design', 'linkcolor', $lcolor->intValue());
185         }
186
187         $onoff = $this->arg('design_background-image_onoff');
188
189         $on   = false;
190         $off  = false;
191
192         if ($onoff == 'on') {
193             $on = true;
194         } else {
195             $off = true;
196         }
197
198         $tile = $this->boolean('design_background-image_repeat');
199
200         // Hack to use Design's bit setter
201         $scratch = new Design();
202         $scratch->setDisposition($on, $off, $tile);
203
204         Config::save('design', 'disposition', $scratch->disposition);
205
206         foreach ($settings as $setting) {
207             Config::save('site', $setting, $values[$setting]);
208         }
209
210         if (isset($bgimage)) {
211             Config::save('design', 'backgroundimage', $bgimage);
212         }
213
214         if (common_config('custom_css', 'enabled')) {
215             $css = $this->arg('css');
216             if ($css != common_config('custom_css', 'css')) {
217                 Config::save('custom_css', 'css', $css);
218             }
219         }
220
221         $config->query('COMMIT');
222     }
223
224     /**
225       * Restore the default design
226       *
227       * @return void
228       */
229
230     function restoreDefaults()
231     {
232         $this->deleteSetting('site', 'logo');
233         $this->deleteSetting('site', 'theme');
234
235         $settings = array(
236             'theme', 'backgroundimage', 'backgroundcolor', 'contentcolor',
237             'sidebarcolor', 'textcolor', 'linkcolor', 'disposition'
238         );
239
240         foreach ($settings as $setting) {
241             $this->deleteSetting('design', $setting);
242         }
243
244         // XXX: Should we restore the default dir settings, etc.? --Z
245
246         // XXX: I can't get it to show the new settings without forcing
247         // this terrible reload -- FIX ME!
248         common_redirect(common_local_url('designadminpanel'), 303);
249     }
250
251     /**
252      * Save the background image if the user uploaded one
253      *
254      * @return string $filename the filename of the image
255      */
256
257     function saveBackgroundImage()
258     {
259         $filename = null;
260
261         if ($_FILES['design_background-image_file']['error'] ==
262             UPLOAD_ERR_OK) {
263
264             $filepath = null;
265
266             try {
267                 $imagefile =
268                     ImageFile::fromUpload('design_background-image_file');
269             } catch (Exception $e) {
270                 $this->clientError('Unable to save background image.');
271                 return;
272             }
273
274             // Note: site design background image has a special filename
275
276             $filename = Design::filename('site-design-background',
277                 image_type_to_extension($imagefile->type),
278                     common_timestamp());
279
280             $filepath = Design::path($filename);
281
282             move_uploaded_file($imagefile->filepath, $filepath);
283
284             // delete any old backround img laying around
285
286             if (isset($this->design->backgroundimage)) {
287                 @unlink(Design::path($design->backgroundimage));
288             }
289
290             return $filename;
291         }
292     }
293
294     /**
295      * Save the custom theme if the user uploaded one.
296      * 
297      * @return mixed custom theme name, if succesful, or null if no theme upload.
298      * @throws ClientException for invalid theme archives
299      * @throws ServerException if trouble saving the theme files
300      */
301
302     function saveCustomTheme()
303     {
304         if (common_config('theme_upload', 'enabled') &&
305             $_FILES['design_upload_theme']['error'] == UPLOAD_ERR_OK) {
306
307             $upload = ThemeUploader::fromUpload('design_upload_theme');
308             $basedir = common_config('local', 'dir');
309             if (empty($basedir)) {
310                 $basedir = INSTALLDIR . '/local';
311             }
312             $name = 'custom'; // @todo allow multiples, custom naming?
313             $outdir = $basedir . '/theme/' . $name;
314             $upload->extract($outdir);
315             return $name;
316         } else {
317             return null;
318         }
319     }
320
321     /**
322      * Attempt to validate setting values
323      *
324      * @return void
325      */
326
327     function validate(&$values)
328     {
329         if (!empty($values['logo']) &&
330             !Validate::uri($values['logo'], array('allowed_schemes' => array('http', 'https')))) {
331             $this->clientError(_('Invalid logo URL.'));
332         }
333
334         if (!in_array($values['theme'], Theme::listAvailable())) {
335             $this->clientError(sprintf(_("Theme not available: %s."), $values['theme']));
336         }
337     }
338
339     /**
340      * Add the Farbtastic stylesheet
341      *
342      * @return void
343      */
344
345     function showStylesheets()
346     {
347         parent::showStylesheets();
348         $this->cssLink('js/farbtastic/farbtastic.css',null,'screen, projection, tv');
349     }
350
351     /**
352      * Add the Farbtastic scripts
353      *
354      * @return void
355      */
356
357     function showScripts()
358     {
359         parent::showScripts();
360
361         $this->script('farbtastic/farbtastic.js');
362         $this->script('userdesign.go.js');
363
364         $this->autofocus('design_background-image_file');
365     }
366
367 }
368
369 class DesignAdminPanelForm extends AdminForm
370 {
371
372     /**
373      * ID of the form
374      *
375      * @return int ID of the form
376      */
377
378     function id()
379     {
380         return 'form_design_admin_panel';
381     }
382
383     /**
384      * class of the form
385      *
386      * @return string class of the form
387      */
388
389     function formClass()
390     {
391         return 'form_settings';
392     }
393
394     /**
395      * HTTP method used to submit the form
396      *
397      * For image data we need to send multipart/form-data
398      * so we set that here too
399      *
400      * @return string the method to use for submitting
401      */
402
403     function method()
404     {
405         $this->enctype = 'multipart/form-data';
406
407         return 'post';
408     }
409
410     /**
411      * Action of the form
412      *
413      * @return string URL of the action
414      */
415
416     function action()
417     {
418         return common_local_url('designadminpanel');
419     }
420
421     /**
422      * Data elements of the form
423      *
424      * @return void
425      */
426
427     function formData()
428     {
429         $this->showLogo();
430         $this->showTheme();
431         $this->showBackground();
432         $this->showColors();
433         $this->showAdvanced();
434     }
435
436     function showLogo()
437     {
438         $this->out->elementStart('fieldset', array('id' => 'settings_design_logo'));
439         $this->out->element('legend', null, _('Change logo'));
440
441         $this->out->elementStart('ul', 'form_data');
442
443         $this->li();
444         $this->input('logo', _('Site logo'), 'Logo for the site (full URL)');
445         $this->unli();
446
447         $this->out->elementEnd('ul');
448
449         $this->out->elementEnd('fieldset');
450
451     }
452
453     function showTheme()
454     {
455         $this->out->elementStart('fieldset', array('id' => 'settings_design_theme'));
456         $this->out->element('legend', null, _('Change theme'));
457
458         $this->out->elementStart('ul', 'form_data');
459
460         $themes = Theme::listAvailable();
461
462         // XXX: listAvailable() can return an empty list if you
463         // screw up your settings, so just in case:
464
465         if (empty($themes)) {
466             $themes = array('default', 'default');
467         }
468
469         asort($themes);
470         $themes = array_combine($themes, $themes);
471
472         $this->li();
473         $this->out->dropdown('theme', _('Site theme'),
474                              $themes, _('Theme for the site.'),
475                              false, $this->value('theme'));
476         $this->unli();
477
478         if (common_config('theme_upload', 'enabled')) {
479             $this->li();
480             $this->out->element('label', array('for' => 'design_upload_theme'), _('Custom theme'));
481             $this->out->element('input', array('id' => 'design_upload_theme',
482                                                'name' => 'design_upload_theme',
483                                                'type' => 'file'));
484             $this->out->element('p', 'form_guide', _('You can upload a custom StatusNet theme as a .ZIP archive.'));
485             $this->unli();
486         }
487
488         $this->out->elementEnd('ul');
489
490         $this->out->elementEnd('fieldset');
491     }
492
493     function showBackground()
494     {
495         $design = $this->out->design;
496
497         $this->out->elementStart('fieldset', array('id' =>
498             'settings_design_background-image'));
499         $this->out->element('legend', null, _('Change background image'));
500         $this->out->elementStart('ul', 'form_data');
501
502         $this->li();
503         $this->out->element('label', array('for' => 'design_background-image_file'),
504                                 _('Background'));
505         $this->out->element('input', array('name' => 'design_background-image_file',
506                                      'type' => 'file',
507                                      'id' => 'design_background-image_file'));
508         $this->out->element('p', 'form_guide',
509             sprintf(_('You can upload a background image for the site. ' .
510               'The maximum file size is %1$s.'), ImageFile::maxFileSize()));
511         $this->out->element('input', array('name' => 'MAX_FILE_SIZE',
512                                           'type' => 'hidden',
513                                           'id' => 'MAX_FILE_SIZE',
514                                           'value' => ImageFile::maxFileSizeInt()));
515         $this->unli();
516
517         if (!empty($design->backgroundimage)) {
518
519             $this->out->elementStart('li', array('id' =>
520                 'design_background-image_onoff'));
521
522             $this->out->element('img', array('src' =>
523                 Design::url($design->backgroundimage)));
524
525             $attrs = array('name' => 'design_background-image_onoff',
526                            'type' => 'radio',
527                            'id' => 'design_background-image_on',
528                            'class' => 'radio',
529                            'value' => 'on');
530
531             if ($design->disposition & BACKGROUND_ON) {
532                 $attrs['checked'] = 'checked';
533             }
534
535             $this->out->element('input', $attrs);
536
537             $this->out->element('label', array('for' => 'design_background-image_on',
538                                           'class' => 'radio'),
539                                           // TRANS: Used as radio button label to add a background image.
540                                           _('On'));
541
542             $attrs = array('name' => 'design_background-image_onoff',
543                            'type' => 'radio',
544                            'id' => 'design_background-image_off',
545                            'class' => 'radio',
546                            'value' => 'off');
547
548             if ($design->disposition & BACKGROUND_OFF) {
549                 $attrs['checked'] = 'checked';
550             }
551
552             $this->out->element('input', $attrs);
553
554             $this->out->element('label', array('for' => 'design_background-image_off',
555                                           'class' => 'radio'),
556                                           // TRANS: Used as radio button label to not add a background image.
557                                           _('Off'));
558             $this->out->element('p', 'form_guide', _('Turn background image on or off.'));
559             $this->unli();
560
561             $this->li();
562             $this->out->checkbox('design_background-image_repeat',
563                             _('Tile background image'),
564                             ($design->disposition & BACKGROUND_TILE) ? true : false);
565             $this->unli();
566         }
567
568         $this->out->elementEnd('ul');
569         $this->out->elementEnd('fieldset');
570     }
571
572     function showColors()
573     {
574         $design = $this->out->design;
575
576         $this->out->elementStart('fieldset', array('id' => 'settings_design_color'));
577         $this->out->element('legend', null, _('Change colours'));
578
579         $this->out->elementStart('ul', 'form_data');
580
581         try {
582             // @fixme avoid loop unrolling in non-performance-critical contexts like this
583
584             $bgcolor = new WebColor($design->backgroundcolor);
585
586             $this->li();
587             $this->out->element('label', array('for' => 'swatch-1'), _('Background'));
588             $this->out->element('input', array('name' => 'design_background',
589                                           'type' => 'text',
590                                           'id' => 'swatch-1',
591                                           'class' => 'swatch',
592                                           'maxlength' => '7',
593                                           'size' => '7',
594                                           'value' => ''));
595             $this->unli();
596
597             $ccolor = new WebColor($design->contentcolor);
598
599             $this->li();
600             $this->out->element('label', array('for' => 'swatch-2'), _('Content'));
601             $this->out->element('input', array('name' => 'design_content',
602                                           'type' => 'text',
603                                           'id' => 'swatch-2',
604                                           'class' => 'swatch',
605                                           'maxlength' => '7',
606                                           'size' => '7',
607                                           'value' => ''));
608             $this->unli();
609
610             $sbcolor = new WebColor($design->sidebarcolor);
611
612             $this->li();
613             $this->out->element('label', array('for' => 'swatch-3'), _('Sidebar'));
614             $this->out->element('input', array('name' => 'design_sidebar',
615                                         'type' => 'text',
616                                         'id' => 'swatch-3',
617                                         'class' => 'swatch',
618                                         'maxlength' => '7',
619                                         'size' => '7',
620                                         'value' => ''));
621             $this->unli();
622
623             $tcolor = new WebColor($design->textcolor);
624
625             $this->li();
626             $this->out->element('label', array('for' => 'swatch-4'), _('Text'));
627             $this->out->element('input', array('name' => 'design_text',
628                                         'type' => 'text',
629                                         'id' => 'swatch-4',
630                                         'class' => 'swatch',
631                                         'maxlength' => '7',
632                                         'size' => '7',
633                                         'value' => ''));
634             $this->unli();
635
636             $lcolor = new WebColor($design->linkcolor);
637
638             $this->li();
639             $this->out->element('label', array('for' => 'swatch-5'), _('Links'));
640             $this->out->element('input', array('name' => 'design_links',
641                                          'type' => 'text',
642                                          'id' => 'swatch-5',
643                                          'class' => 'swatch',
644                                          'maxlength' => '7',
645                                          'size' => '7',
646                                          'value' => ''));
647             $this->unli();
648
649         } catch (WebColorException $e) {
650             // @fixme normalize them individually!
651             common_log(LOG_ERR, 'Bad color values in site design: ' .
652                 $e->getMessage());
653         }
654
655         $this->out->elementEnd('fieldset');
656
657         $this->out->elementEnd('ul');
658     }
659
660     function showAdvanced()
661     {
662         if (common_config('custom_css', 'enabled')) {
663             $this->out->elementStart('fieldset', array('id' => 'settings_design_advanced'));
664             $this->out->element('legend', null, _('Advanced'));
665             $this->out->elementStart('ul', 'form_data');
666
667             $this->li();
668             $this->out->element('label', array('for' => 'css'), _('Custom CSS'));
669             $this->out->element('textarea', array('name' => 'css',
670                                             'id' => 'css',
671                                             'cols' => '50',
672                                             'rows' => '10'),
673                                 strval(common_config('custom_css', 'css')));
674             $this->unli();
675
676             $this->out->elementEnd('fieldset');
677             $this->out->elementEnd('ul');
678         }
679     }
680
681     /**
682      * Action elements
683      *
684      * @return void
685      */
686
687     function formActions()
688     {
689         $this->out->submit('defaults', _('Use defaults'), 'submit form_action-default',
690                 'defaults', _('Restore default designs'));
691
692         $this->out->element('input', array('id' => 'settings_design_reset',
693                                          'type' => 'reset',
694                                          'value' => 'Reset',
695                                          'class' => 'submit form_action-primary',
696                                          'title' => _('Reset back to default')));
697
698         $this->out->submit('save', _('Save'), 'submit form_action-secondary',
699                 'save', _('Save design'));
700     }
701
702 }