]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - actions/designadminpanel.php
Fix PHP notice when submitting 'design' admin panel on a browser that doesn't support...
[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', 'ssllogo');
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', 'ssllogo');
234         $this->deleteSetting('site', 'theme');
235
236         $settings = array(
237             'theme', 'backgroundimage', 'backgroundcolor', 'contentcolor',
238             'sidebarcolor', 'textcolor', 'linkcolor', 'disposition'
239         );
240
241         foreach ($settings as $setting) {
242             $this->deleteSetting('design', $setting);
243         }
244
245         // XXX: Should we restore the default dir settings, etc.? --Z
246
247         // XXX: I can't get it to show the new settings without forcing
248         // this terrible reload -- FIX ME!
249         common_redirect(common_local_url('designadminpanel'), 303);
250     }
251
252     /**
253      * Save the background image if the user uploaded one
254      *
255      * @return string $filename the filename of the image
256      */
257
258     function saveBackgroundImage()
259     {
260         $filename = null;
261         if (isset($_FILES['design_background-image_file']['error']) &&
262             $_FILES['design_background-image_file']['error'] ==
263             UPLOAD_ERR_OK) {
264
265             $filepath = null;
266
267             try {
268                 $imagefile =
269                     ImageFile::fromUpload('design_background-image_file');
270             } catch (Exception $e) {
271                 $this->clientError('Unable to save background image.');
272                 return;
273             }
274
275             // Note: site design background image has a special filename
276
277             $filename = Design::filename('site-design-background',
278                 image_type_to_extension($imagefile->type),
279                     common_timestamp());
280
281             $filepath = Design::path($filename);
282
283             move_uploaded_file($imagefile->filepath, $filepath);
284
285             // delete any old backround img laying around
286
287             if (isset($this->design->backgroundimage)) {
288                 @unlink(Design::path($design->backgroundimage));
289             }
290
291             return $filename;
292         }
293     }
294
295     /**
296      * Save the custom theme if the user uploaded one.
297      *
298      * @return mixed custom theme name, if succesful, or null if no theme upload.
299      * @throws ClientException for invalid theme archives
300      * @throws ServerException if trouble saving the theme files
301      */
302
303     function saveCustomTheme()
304     {
305         if (common_config('theme_upload', 'enabled') &&
306             $_FILES['design_upload_theme']['error'] == UPLOAD_ERR_OK) {
307
308             $upload = ThemeUploader::fromUpload('design_upload_theme');
309             $basedir = common_config('local', 'dir');
310             if (empty($basedir)) {
311                 $basedir = INSTALLDIR . '/local';
312             }
313             $name = 'custom'; // @todo allow multiples, custom naming?
314             $outdir = $basedir . '/theme/' . $name;
315             $upload->extract($outdir);
316             return $name;
317         } else {
318             return null;
319         }
320     }
321
322     /**
323      * Attempt to validate setting values
324      *
325      * @return void
326      */
327
328     function validate(&$values)
329     {
330         if (!empty($values['logo']) &&
331             !Validate::uri($values['logo'], array('allowed_schemes' => array('http', 'https')))) {
332             $this->clientError(_('Invalid logo URL.'));
333         }
334
335         if (!empty($values['ssllogo']) &&
336             !Validate::uri($values['ssllogo'], array('allowed_schemes' => array('https')))) {
337             $this->clientError(_('Invalid SSL logo URL.'));
338         }
339
340         if (!in_array($values['theme'], Theme::listAvailable())) {
341             $this->clientError(sprintf(_("Theme not available: %s."), $values['theme']));
342         }
343     }
344
345     /**
346      * Add the Farbtastic stylesheet
347      *
348      * @return void
349      */
350
351     function showStylesheets()
352     {
353         parent::showStylesheets();
354         $this->cssLink('js/farbtastic/farbtastic.css',null,'screen, projection, tv');
355     }
356
357     /**
358      * Add the Farbtastic scripts
359      *
360      * @return void
361      */
362
363     function showScripts()
364     {
365         parent::showScripts();
366
367         $this->script('farbtastic/farbtastic.js');
368         $this->script('userdesign.go.js');
369
370         $this->autofocus('design_background-image_file');
371     }
372
373 }
374
375 class DesignAdminPanelForm extends AdminForm
376 {
377
378     /**
379      * ID of the form
380      *
381      * @return int ID of the form
382      */
383
384     function id()
385     {
386         return 'form_design_admin_panel';
387     }
388
389     /**
390      * class of the form
391      *
392      * @return string class of the form
393      */
394
395     function formClass()
396     {
397         return 'form_settings';
398     }
399
400     /**
401      * HTTP method used to submit the form
402      *
403      * For image data we need to send multipart/form-data
404      * so we set that here too
405      *
406      * @return string the method to use for submitting
407      */
408
409     function method()
410     {
411         $this->enctype = 'multipart/form-data';
412
413         return 'post';
414     }
415
416     /**
417      * Action of the form
418      *
419      * @return string URL of the action
420      */
421
422     function action()
423     {
424         return common_local_url('designadminpanel');
425     }
426
427     /**
428      * Data elements of the form
429      *
430      * @return void
431      */
432
433     function formData()
434     {
435         $this->showLogo();
436         $this->showTheme();
437         $this->showBackground();
438         $this->showColors();
439         $this->showAdvanced();
440     }
441
442     function showLogo()
443     {
444         $this->out->elementStart('fieldset', array('id' => 'settings_design_logo'));
445         $this->out->element('legend', null, _('Change logo'));
446
447         $this->out->elementStart('ul', 'form_data');
448
449         $this->li();
450         $this->input('logo', _('Site logo'), 'Logo for the site (full URL)');
451         $this->unli();
452
453         $this->li();
454         $this->input('ssllogo', _('SSL logo'), 'Logo to show on SSL pages');
455         $this->unli();
456
457         $this->out->elementEnd('ul');
458
459         $this->out->elementEnd('fieldset');
460
461     }
462
463     function showTheme()
464     {
465         $this->out->elementStart('fieldset', array('id' => 'settings_design_theme'));
466         $this->out->element('legend', null, _('Change theme'));
467
468         $this->out->elementStart('ul', 'form_data');
469
470         $themes = Theme::listAvailable();
471
472         // XXX: listAvailable() can return an empty list if you
473         // screw up your settings, so just in case:
474
475         if (empty($themes)) {
476             $themes = array('default', 'default');
477         }
478
479         asort($themes);
480         $themes = array_combine($themes, $themes);
481
482         $this->li();
483         $this->out->dropdown('theme', _('Site theme'),
484                              $themes, _('Theme for the site.'),
485                              false, $this->value('theme'));
486         $this->unli();
487
488         if (common_config('theme_upload', 'enabled')) {
489             $this->li();
490             $this->out->element('label', array('for' => 'design_upload_theme'), _('Custom theme'));
491             $this->out->element('input', array('id' => 'design_upload_theme',
492                                                'name' => 'design_upload_theme',
493                                                'type' => 'file'));
494             $this->out->element('p', 'form_guide', _('You can upload a custom StatusNet theme as a .ZIP archive.'));
495             $this->unli();
496         }
497
498         $this->out->elementEnd('ul');
499
500         $this->out->elementEnd('fieldset');
501     }
502
503     function showBackground()
504     {
505         $design = $this->out->design;
506
507         $this->out->elementStart('fieldset', array('id' =>
508             'settings_design_background-image'));
509         $this->out->element('legend', null, _('Change background image'));
510         $this->out->elementStart('ul', 'form_data');
511
512         $this->li();
513         $this->out->element('label', array('for' => 'design_background-image_file'),
514                                 _('Background'));
515         $this->out->element('input', array('name' => 'design_background-image_file',
516                                      'type' => 'file',
517                                      'id' => 'design_background-image_file'));
518         $this->out->element('p', 'form_guide',
519             sprintf(_('You can upload a background image for the site. ' .
520               'The maximum file size is %1$s.'), ImageFile::maxFileSize()));
521         $this->out->element('input', array('name' => 'MAX_FILE_SIZE',
522                                           'type' => 'hidden',
523                                           'id' => 'MAX_FILE_SIZE',
524                                           'value' => ImageFile::maxFileSizeInt()));
525         $this->unli();
526
527         if (!empty($design->backgroundimage)) {
528
529             $this->out->elementStart('li', array('id' =>
530                 'design_background-image_onoff'));
531
532             $this->out->element('img', array('src' =>
533                 Design::url($design->backgroundimage)));
534
535             $attrs = array('name' => 'design_background-image_onoff',
536                            'type' => 'radio',
537                            'id' => 'design_background-image_on',
538                            'class' => 'radio',
539                            'value' => 'on');
540
541             if ($design->disposition & BACKGROUND_ON) {
542                 $attrs['checked'] = 'checked';
543             }
544
545             $this->out->element('input', $attrs);
546
547             $this->out->element('label', array('for' => 'design_background-image_on',
548                                           'class' => 'radio'),
549                                           // TRANS: Used as radio button label to add a background image.
550                                           _('On'));
551
552             $attrs = array('name' => 'design_background-image_onoff',
553                            'type' => 'radio',
554                            'id' => 'design_background-image_off',
555                            'class' => 'radio',
556                            'value' => 'off');
557
558             if ($design->disposition & BACKGROUND_OFF) {
559                 $attrs['checked'] = 'checked';
560             }
561
562             $this->out->element('input', $attrs);
563
564             $this->out->element('label', array('for' => 'design_background-image_off',
565                                           'class' => 'radio'),
566                                           // TRANS: Used as radio button label to not add a background image.
567                                           _('Off'));
568             $this->out->element('p', 'form_guide', _('Turn background image on or off.'));
569             $this->unli();
570
571             $this->li();
572             $this->out->checkbox('design_background-image_repeat',
573                             _('Tile background image'),
574                             ($design->disposition & BACKGROUND_TILE) ? true : false);
575             $this->unli();
576         }
577
578         $this->out->elementEnd('ul');
579         $this->out->elementEnd('fieldset');
580     }
581
582     function showColors()
583     {
584         $design = $this->out->design;
585
586         $this->out->elementStart('fieldset', array('id' => 'settings_design_color'));
587         $this->out->element('legend', null, _('Change colours'));
588
589         $this->out->elementStart('ul', 'form_data');
590
591         try {
592             // @fixme avoid loop unrolling in non-performance-critical contexts like this
593
594             $bgcolor = new WebColor($design->backgroundcolor);
595
596             $this->li();
597             $this->out->element('label', array('for' => 'swatch-1'), _('Background'));
598             $this->out->element('input', array('name' => 'design_background',
599                                           'type' => 'text',
600                                           'id' => 'swatch-1',
601                                           'class' => 'swatch',
602                                           'maxlength' => '7',
603                                           'size' => '7',
604                                           'value' => ''));
605             $this->unli();
606
607             $ccolor = new WebColor($design->contentcolor);
608
609             $this->li();
610             $this->out->element('label', array('for' => 'swatch-2'), _('Content'));
611             $this->out->element('input', array('name' => 'design_content',
612                                           'type' => 'text',
613                                           'id' => 'swatch-2',
614                                           'class' => 'swatch',
615                                           'maxlength' => '7',
616                                           'size' => '7',
617                                           'value' => ''));
618             $this->unli();
619
620             $sbcolor = new WebColor($design->sidebarcolor);
621
622             $this->li();
623             $this->out->element('label', array('for' => 'swatch-3'), _('Sidebar'));
624             $this->out->element('input', array('name' => 'design_sidebar',
625                                         'type' => 'text',
626                                         'id' => 'swatch-3',
627                                         'class' => 'swatch',
628                                         'maxlength' => '7',
629                                         'size' => '7',
630                                         'value' => ''));
631             $this->unli();
632
633             $tcolor = new WebColor($design->textcolor);
634
635             $this->li();
636             $this->out->element('label', array('for' => 'swatch-4'), _('Text'));
637             $this->out->element('input', array('name' => 'design_text',
638                                         'type' => 'text',
639                                         'id' => 'swatch-4',
640                                         'class' => 'swatch',
641                                         'maxlength' => '7',
642                                         'size' => '7',
643                                         'value' => ''));
644             $this->unli();
645
646             $lcolor = new WebColor($design->linkcolor);
647
648             $this->li();
649             $this->out->element('label', array('for' => 'swatch-5'), _('Links'));
650             $this->out->element('input', array('name' => 'design_links',
651                                          'type' => 'text',
652                                          'id' => 'swatch-5',
653                                          'class' => 'swatch',
654                                          'maxlength' => '7',
655                                          'size' => '7',
656                                          'value' => ''));
657             $this->unli();
658
659         } catch (WebColorException $e) {
660             // @fixme normalize them individually!
661             common_log(LOG_ERR, 'Bad color values in site design: ' .
662                 $e->getMessage());
663         }
664
665         $this->out->elementEnd('fieldset');
666
667         $this->out->elementEnd('ul');
668     }
669
670     function showAdvanced()
671     {
672         if (common_config('custom_css', 'enabled')) {
673             $this->out->elementStart('fieldset', array('id' => 'settings_design_advanced'));
674             $this->out->element('legend', null, _('Advanced'));
675             $this->out->elementStart('ul', 'form_data');
676
677             $this->li();
678             $this->out->element('label', array('for' => 'css'), _('Custom CSS'));
679             $this->out->element('textarea', array('name' => 'css',
680                                             'id' => 'css',
681                                             'cols' => '50',
682                                             'rows' => '10'),
683                                 strval(common_config('custom_css', 'css')));
684             $this->unli();
685
686             $this->out->elementEnd('fieldset');
687             $this->out->elementEnd('ul');
688         }
689     }
690
691     /**
692      * Action elements
693      *
694      * @return void
695      */
696
697     function formActions()
698     {
699         $this->out->submit('defaults', _('Use defaults'), 'submit form_action-default',
700                 'defaults', _('Restore default designs'));
701
702         $this->out->element('input', array('id' => 'settings_design_reset',
703                                          'type' => 'reset',
704                                          'value' => 'Reset',
705                                          'class' => 'submit form_action-primary',
706                                          'title' => _('Reset back to default')));
707
708         $this->out->submit('save', _('Save'), 'submit form_action-secondary',
709                 'save', _('Save design'));
710     }
711
712 }