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