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