3 * StatusNet, the distributed open-source microblogging tool
5 * Design administration panel
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.
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.
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/>.
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/
32 if (!defined('STATUSNET')) {
37 * Administer design settings
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/
48 class DesignadminpanelAction extends AdminPanelAction
51 /* The default site design */
55 * Returns the page title
57 * @return string page title
62 // TRANS: Message used as title for design settings for the site.
67 * Instructions for using this form.
69 * @return string instructions
72 function getInstructions()
74 return _('Design settings for this StatusNet site');
78 * Get the default design and show the design admin panel form
85 $this->design = Design::siteDesign();
86 $form = new DesignAdminPanelForm($this);
92 * Save settings from the form
97 function saveSettings()
99 if ($this->arg('save')) {
100 $this->saveDesignSettings();
101 } else if ($this->arg('defaults')) {
102 $this->restoreDefaults();
104 $this->clientError(_('Unexpected form submission.'));
109 * Save the new design settings
114 function saveDesignSettings()
116 // Workaround for PHP returning empty $_POST and $_FILES when POST
117 // length > post_max_size in php.ini
121 && ($_SERVER['CONTENT_LENGTH'] > 0)
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']));
129 // check for file uploads
131 $bgimage = $this->saveBackgroundImage();
132 $customTheme = $this->saveCustomTheme();
134 $oldtheme = common_config('site', 'theme');
136 // This feels pretty hacky :D
137 $this->args['theme'] = $customTheme;
138 $themeChanged = true;
140 $themeChanged = ($this->trimmed('theme') != $oldtheme);
143 static $settings = array('theme', 'logo');
147 foreach ($settings as $setting) {
148 $values[$setting] = $this->trimmed($setting);
151 $this->validate($values);
153 $config = new Config();
155 $config->query('BEGIN');
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;
165 // And remove old settings from DB...
166 $this->deleteSetting('design', $colorKey . 'color');
169 // Only save colors from the form if the theme has not changed.
171 // @fixme a future more ajaxy form should allow theme switch
172 // and color customization in one step.
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'));
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());
187 $onoff = $this->arg('design_background-image_onoff');
192 if ($onoff == 'on') {
198 $tile = $this->boolean('design_background-image_repeat');
200 // Hack to use Design's bit setter
201 $scratch = new Design();
202 $scratch->setDisposition($on, $off, $tile);
204 Config::save('design', 'disposition', $scratch->disposition);
206 foreach ($settings as $setting) {
207 Config::save('site', $setting, $values[$setting]);
210 if (isset($bgimage)) {
211 Config::save('design', 'backgroundimage', $bgimage);
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);
221 $config->query('COMMIT');
225 * Restore the default design
230 function restoreDefaults()
232 $this->deleteSetting('site', 'logo');
233 $this->deleteSetting('site', 'theme');
236 'theme', 'backgroundimage', 'backgroundcolor', 'contentcolor',
237 'sidebarcolor', 'textcolor', 'linkcolor', 'disposition'
240 foreach ($settings as $setting) {
241 $this->deleteSetting('design', $setting);
244 // XXX: Should we restore the default dir settings, etc.? --Z
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);
252 * Save the background image if the user uploaded one
254 * @return string $filename the filename of the image
257 function saveBackgroundImage()
261 if ($_FILES['design_background-image_file']['error'] ==
268 ImageFile::fromUpload('design_background-image_file');
269 } catch (Exception $e) {
270 $this->clientError('Unable to save background image.');
274 // Note: site design background image has a special filename
276 $filename = Design::filename('site-design-background',
277 image_type_to_extension($imagefile->type),
280 $filepath = Design::path($filename);
282 move_uploaded_file($imagefile->filepath, $filepath);
284 // delete any old backround img laying around
286 if (isset($this->design->backgroundimage)) {
287 @unlink(Design::path($design->backgroundimage));
295 * Save the custom theme if the user uploaded one.
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
302 function saveCustomTheme()
304 if (common_config('theme_upload', 'enabled') &&
305 $_FILES['design_upload_theme']['error'] == UPLOAD_ERR_OK) {
307 $upload = ThemeUploader::fromUpload('design_upload_theme');
308 $basedir = common_config('local', 'dir');
309 if (empty($basedir)) {
310 $basedir = INSTALLDIR . '/local';
312 $name = 'custom'; // @todo allow multiples, custom naming?
313 $outdir = $basedir . '/theme/' . $name;
314 $upload->extract($outdir);
322 * Attempt to validate setting values
327 function validate(&$values)
329 if (!empty($values['logo']) &&
330 !Validate::uri($values['logo'], array('allowed_schemes' => array('http', 'https')))) {
331 $this->clientError(_('Invalid logo URL.'));
334 if (!in_array($values['theme'], Theme::listAvailable())) {
335 $this->clientError(sprintf(_("Theme not available: %s."), $values['theme']));
340 * Add the Farbtastic stylesheet
345 function showStylesheets()
347 parent::showStylesheets();
348 $this->cssLink('js/farbtastic/farbtastic.css',null,'screen, projection, tv');
352 * Add the Farbtastic scripts
357 function showScripts()
359 parent::showScripts();
361 $this->script('farbtastic/farbtastic.js');
362 $this->script('userdesign.go.js');
364 $this->autofocus('design_background-image_file');
369 class DesignAdminPanelForm extends AdminForm
375 * @return int ID of the form
380 return 'form_design_admin_panel';
386 * @return string class of the form
391 return 'form_settings';
395 * HTTP method used to submit the form
397 * For image data we need to send multipart/form-data
398 * so we set that here too
400 * @return string the method to use for submitting
405 $this->enctype = 'multipart/form-data';
413 * @return string URL of the action
418 return common_local_url('designadminpanel');
422 * Data elements of the form
431 $this->showBackground();
433 $this->showAdvanced();
438 $this->out->elementStart('fieldset', array('id' => 'settings_design_logo'));
439 $this->out->element('legend', null, _('Change logo'));
441 $this->out->elementStart('ul', 'form_data');
444 $this->input('logo', _('Site logo'), 'Logo for the site (full URL)');
447 $this->out->elementEnd('ul');
449 $this->out->elementEnd('fieldset');
455 $this->out->elementStart('fieldset', array('id' => 'settings_design_theme'));
456 $this->out->element('legend', null, _('Change theme'));
458 $this->out->elementStart('ul', 'form_data');
460 $themes = Theme::listAvailable();
462 // XXX: listAvailable() can return an empty list if you
463 // screw up your settings, so just in case:
465 if (empty($themes)) {
466 $themes = array('default', 'default');
470 $themes = array_combine($themes, $themes);
473 $this->out->dropdown('theme', _('Site theme'),
474 $themes, _('Theme for the site.'),
475 false, $this->value('theme'));
478 if (common_config('theme_upload', 'enabled')) {
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',
484 $this->out->element('p', 'form_guide', _('You can upload a custom StatusNet theme as a .ZIP archive.'));
488 $this->out->elementEnd('ul');
490 $this->out->elementEnd('fieldset');
493 function showBackground()
495 $design = $this->out->design;
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');
503 $this->out->element('label', array('for' => 'design_background-image_file'),
505 $this->out->element('input', array('name' => 'design_background-image_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',
513 'id' => 'MAX_FILE_SIZE',
514 'value' => ImageFile::maxFileSizeInt()));
517 if (!empty($design->backgroundimage)) {
519 $this->out->elementStart('li', array('id' =>
520 'design_background-image_onoff'));
522 $this->out->element('img', array('src' =>
523 Design::url($design->backgroundimage)));
525 $attrs = array('name' => 'design_background-image_onoff',
527 'id' => 'design_background-image_on',
531 if ($design->disposition & BACKGROUND_ON) {
532 $attrs['checked'] = 'checked';
535 $this->out->element('input', $attrs);
537 $this->out->element('label', array('for' => 'design_background-image_on',
539 // TRANS: Used as radio button label to add a background image.
542 $attrs = array('name' => 'design_background-image_onoff',
544 'id' => 'design_background-image_off',
548 if ($design->disposition & BACKGROUND_OFF) {
549 $attrs['checked'] = 'checked';
552 $this->out->element('input', $attrs);
554 $this->out->element('label', array('for' => 'design_background-image_off',
556 // TRANS: Used as radio button label to not add a background image.
558 $this->out->element('p', 'form_guide', _('Turn background image on or off.'));
562 $this->out->checkbox('design_background-image_repeat',
563 _('Tile background image'),
564 ($design->disposition & BACKGROUND_TILE) ? true : false);
568 $this->out->elementEnd('ul');
569 $this->out->elementEnd('fieldset');
572 function showColors()
574 $design = $this->out->design;
576 $this->out->elementStart('fieldset', array('id' => 'settings_design_color'));
577 $this->out->element('legend', null, _('Change colours'));
579 $this->out->elementStart('ul', 'form_data');
582 // @fixme avoid loop unrolling in non-performance-critical contexts like this
584 $bgcolor = new WebColor($design->backgroundcolor);
587 $this->out->element('label', array('for' => 'swatch-1'), _('Background'));
588 $this->out->element('input', array('name' => 'design_background',
597 $ccolor = new WebColor($design->contentcolor);
600 $this->out->element('label', array('for' => 'swatch-2'), _('Content'));
601 $this->out->element('input', array('name' => 'design_content',
610 $sbcolor = new WebColor($design->sidebarcolor);
613 $this->out->element('label', array('for' => 'swatch-3'), _('Sidebar'));
614 $this->out->element('input', array('name' => 'design_sidebar',
623 $tcolor = new WebColor($design->textcolor);
626 $this->out->element('label', array('for' => 'swatch-4'), _('Text'));
627 $this->out->element('input', array('name' => 'design_text',
636 $lcolor = new WebColor($design->linkcolor);
639 $this->out->element('label', array('for' => 'swatch-5'), _('Links'));
640 $this->out->element('input', array('name' => 'design_links',
649 } catch (WebColorException $e) {
650 // @fixme normalize them individually!
651 common_log(LOG_ERR, 'Bad color values in site design: ' .
655 $this->out->elementEnd('fieldset');
657 $this->out->elementEnd('ul');
660 function showAdvanced()
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');
668 $this->out->element('label', array('for' => 'css'), _('Custom CSS'));
669 $this->out->element('textarea', array('name' => 'css',
673 strval(common_config('custom_css', 'css')));
676 $this->out->elementEnd('fieldset');
677 $this->out->elementEnd('ul');
687 function formActions()
689 $this->out->submit('defaults', _('Use defaults'), 'submit form_action-default',
690 'defaults', _('Restore default designs'));
692 $this->out->element('input', array('id' => 'settings_design_reset',
695 'class' => 'submit form_action-primary',
696 'title' => _('Reset back to default')));
698 $this->out->submit('save', _('Save'), 'submit form_action-secondary',
699 'save', _('Save design'));