]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch '0.8.x' of git@gitorious.org:+laconica-developers/laconica/dev into...
authorRobin Millette <millette@controlyourself.ca>
Thu, 18 Jun 2009 10:02:54 +0000 (06:02 -0400)
committerRobin Millette <millette@controlyourself.ca>
Thu, 18 Jun 2009 10:02:54 +0000 (06:02 -0400)
47 files changed:
.gitignore
actions/blockedfromgroup.php
actions/designsettings.php [deleted file]
actions/editgroup.php
actions/groupblock.php
actions/groupdesignsettings.php [new file with mode: 0644]
actions/grouplogo.php
actions/groupmembers.php
actions/invite.php
actions/replies.php
actions/showfavorites.php
actions/showgroup.php
actions/userdesignsettings.php [new file with mode: 0644]
actions/usergroups.php
background/.gitignore [new file with mode: 0644]
classes/Design.php [new file with mode: 0644]
classes/User.php
classes/User_group.php
classes/laconica.ini [changed mode: 0644->0755]
db/laconica.sql
js/farbtastic/farbtastic.go.js [deleted file]
js/farbtastic/farbtastic.js
js/userdesign.go.js [new file with mode: 0644]
js/util.js
lib/accountsettingsaction.php
lib/attachmentlist.php
lib/common.php
lib/currentuserdesignaction.php [new file with mode: 0644]
lib/designsettings.php [new file with mode: 0644]
lib/galleryaction.php
lib/groupdesignaction.php [new file with mode: 0644]
lib/groupnav.php
lib/mailbox.php
lib/noticelist.php
lib/ownerdesignaction.php [new file with mode: 0644]
lib/personal.php [deleted file]
lib/profileaction.php
lib/router.php
lib/settingsaction.php
lib/stream.php [deleted file]
lib/webcolor.php [new file with mode: 0644]
theme/base/css/display.css
theme/base/css/ie.css
theme/default/css/display.css
theme/identica/css/display.css
theme/pigeonthoughts/css/base.css
theme/pigeonthoughts/css/display.css

index 3418d8ee54d1178448469dd417721c9831c1ea2d..8a7f5c65c8cefafbf527fef659146c0647c50566 100644 (file)
@@ -1,4 +1,5 @@
 avatar/*
+background/*
 files/*
 file/*
 _darcs/*
index 541ebcfd90701d1074f038a97d46b08300b1d002..5c1eab35425775693a8b067741352ac297c8e68b 100644 (file)
@@ -41,7 +41,7 @@ if (!defined('LACONICA')) {
  * @link     http://laconi.ca/
  */
 
-class BlockedfromgroupAction extends Action
+class BlockedfromgroupAction extends GroupDesignAction
 {
     var $page = null;
 
diff --git a/actions/designsettings.php b/actions/designsettings.php
deleted file mode 100644 (file)
index 5774b85..0000000
+++ /dev/null
@@ -1,264 +0,0 @@
-<?php
-/**
- * Laconica, the distributed open-source microblogging tool
- *
- * Change user password
- *
- * PHP version 5
- *
- * LICENCE: This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Settings
- * @package   Laconica
- * @author    Sarven Capadisli <csarven@controlyourself.ca>
- * @copyright 2008-2009 Control Yourself, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://laconi.ca/
- */
-
-if (!defined('LACONICA')) {
-    exit(1);
-}
-
-require_once INSTALLDIR.'/lib/accountsettingsaction.php';
-
-
-
-class DesignsettingsAction extends AccountSettingsAction
-{
-    /**
-     * Title of the page
-     *
-     * @return string Title of the page
-     */
-
-    function title()
-    {
-        return _('Profile design');
-    }
-
-    /**
-     * Instructions for use
-     *
-     * @return instructions for use
-     */
-
-    function getInstructions()
-    {
-        return _('Customize the way your profile looks with a background image and a colour palette of your choice.');
-    }
-
-    /**
-     * Content area of the page
-     *
-     * Shows a form for changing the password
-     *
-     * @return void
-     */
-
-    function showContent()
-    {
-        $user = common_current_user();
-        $this->elementStart('form', array('method' => 'post',
-                                          'id' => 'form_settings_design',
-                                          'class' => 'form_settings',
-                                          'action' =>
-                                          common_local_url('designsettings')));
-        $this->elementStart('fieldset');
-        $this->hidden('token', common_session_token());
-
-        $this->elementStart('fieldset', array('id' => 'settings_design_background-image'));
-        $this->element('legend', null, _('Change background image'));
-        $this->elementStart('ul', 'form_data');
-        $this->elementStart('li');
-        $this->element('label', array('for' => 'design_background-image_file'), 
-                                _('Upload file'));
-        $this->element('input', array('name' => 'design_background-image_file',
-                                      'type' => 'file',
-                                      'id' => 'design_background-image_file'));
-        $this->element('p', 'form_guide', _('You can upload your personal background image. The maximum file size is 2Mb.'));
-        $this->element('input', array('name' => 'MAX_FILE_SIZE',
-                                      'type' => 'hidden',
-                                      'id' => 'MAX_FILE_SIZE',
-                                      'value' => ImageFile::maxFileSizeInt()));
-        $this->elementEnd('li');
-        $this->elementEnd('ul');
-        $this->elementEnd('fieldset');
-
-        $this->elementStart('fieldset', array('id' => 'settings_design_color'));
-        $this->element('legend', null, _('Change colours'));
-        $this->elementStart('ul', 'form_data');
-
-        //This is a JSON object in the DB field. Here for testing. Remove later.
-        $userSwatch = '{"body":{"background-color":"#F0F2F5"},
-                        "#content":{"background-color":"#FFFFFF"},
-                        "#aside_primary":{"background-color":"#CEE1E9"},
-                        "html body":{"color":"#000000"},
-                        "a":{"color":"#002E6E"}}';
-
-        //Default theme swatch -- Where should this be stored?
-        $defaultSwatch = array('body' => array('background-color' => '#F0F2F5'),
-                               '#content' => array('background-color' => '#FFFFFF'),
-                               '#aside_primary' => array('background-color' => '#CEE1E9'),
-                               'html body' => array('color' => '#000000'),
-                               'a' => array('color' => '#002E6E'));
-
-        $userSwatch = ($userSwatch) ? json_decode($userSwatch, true) : $defaultSwatch;
-
-        $s = 0;
-        $labelSwatch = array('Background',
-                             'Content',
-                             'Sidebar',
-                             'Text',
-                             'Links');
-        foreach($userSwatch as $propertyvalue => $value) {
-            $foo = array_values($value);
-            $this->elementStart('li');
-            $this->element('label', array('for' => 'swatch-'.$s), _($labelSwatch[$s]));
-            $this->element('input', array('name' => 'swatch-'.$s, //prefer swatch[$s] ?
-                                          'type' => 'text',
-                                          'id' => 'swatch-'.$s,
-                                          'class' => 'swatch',
-                                          'maxlength' => '7',
-                                          'size' => '7',
-                                          'value' => $foo[0]));
-            $this->elementEnd('li');
-            $s++;
-        }
-
-        $this->elementEnd('ul');
-        $this->elementEnd('fieldset');
-
-        $this->element('input', array('id' => 'settings_design_reset',
-                                      'type' => 'reset',
-                                      'value' => 'Reset',
-                                      'class' => 'submit form_action-primary',
-                                      'title' => _('Reset back to default')));
-        $this->submit('save', _('Save'), 'submit form_action-secondary', 'save', _('Save design'));
-
-/*TODO: Check submitted form values: 
-json_encode(form values)
-if submitted Swatch == DefaultSwatch, don't store in DB.
-else store in BD
-*/
-        $this->elementEnd('fieldset');
-        $this->elementEnd('form');
-
-    }
-
-    /**
-     * Handle a post
-     *
-     * Validate input and save changes. Reload the form with a success
-     * or error message.
-     *
-     * @return void
-     */
-
-    function handlePost()
-    {
-    /*
-        // CSRF protection
-
-        $token = $this->trimmed('token');
-        if (!$token || $token != common_session_token()) {
-            $this->showForm(_('There was a problem with your session token. '.
-                               'Try again, please.'));
-            return;
-        }
-
-        $user = common_current_user();
-        assert(!is_null($user)); // should already be checked
-
-        // FIXME: scrub input
-
-        $newpassword = $this->arg('newpassword');
-        $confirm     = $this->arg('confirm');
-
-        # Some validation
-
-        if (strlen($newpassword) < 6) {
-            $this->showForm(_('Password must be 6 or more characters.'));
-            return;
-        } else if (0 != strcmp($newpassword, $confirm)) {
-            $this->showForm(_('Passwords don\'t match.'));
-            return;
-        }
-
-        if ($user->password) {
-            $oldpassword = $this->arg('oldpassword');
-
-            if (!common_check_user($user->nickname, $oldpassword)) {
-                $this->showForm(_('Incorrect old password'));
-                return;
-            }
-        }
-
-        $original = clone($user);
-
-        $user->password = common_munge_password($newpassword, $user->id);
-
-        $val = $user->validate();
-        if ($val !== true) {
-            $this->showForm(_('Error saving user; invalid.'));
-            return;
-        }
-
-        if (!$user->update($original)) {
-            $this->serverError(_('Can\'t save new password.'));
-            return;
-        }
-
-        $this->showForm(_('Password saved.'), true);
-        */
-    }
-
-
-    /**
-     * Add the Farbtastic stylesheet
-     *
-     * @return void
-     */
-
-    function showStylesheets()
-    {
-        parent::showStylesheets();
-        $farbtasticStyle =
-          common_path('theme/base/css/farbtastic.css?version='.LACONICA_VERSION);
-
-        $this->element('link', array('rel' => 'stylesheet',
-                                     'type' => 'text/css',
-                                     'href' => $farbtasticStyle,
-                                     'media' => 'screen, projection, tv'));
-    }
-
-    /**
-     * Add the Farbtastic scripts
-     *
-     * @return void
-     */
-
-    function showScripts()
-    {
-        parent::showScripts();
-
-        $farbtasticPack = common_path('js/farbtastic/farbtastic.js');
-        $farbtasticGo   = common_path('js/farbtastic/farbtastic.go.js');
-
-        $this->element('script', array('type' => 'text/javascript',
-                                       'src' => $farbtasticPack));
-        $this->element('script', array('type' => 'text/javascript',
-                                       'src' => $farbtasticGo));
-    }
-}
index 29a7bce437c661c582213254767d43b0a941bab2..6aa6f8b11f20d82d59b46a57bf8129d81e9b327e 100644 (file)
@@ -23,6 +23,7 @@
  * @package   Laconica
  * @author    Evan Prodromou <evan@controlyourself.ca>
  * @author    Sarven Capadisli <csarven@controlyourself.ca>
+ * @author   Zach Copley <zach@controlyourself.ca>
  * @copyright 2008-2009 Control Yourself, Inc.
  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  * @link      http://laconi.ca/
@@ -40,14 +41,15 @@ if (!defined('LACONICA')) {
  * @category Group
  * @package  Laconica
  * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @author   Zach Copley <zach@controlyourself.ca>
  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  * @link     http://laconi.ca/
  */
 
-class EditgroupAction extends Action
+class EditgroupAction extends GroupDesignAction
 {
+
     var $msg;
-    var $group = null;
 
     function title()
     {
index 28685b1d56fffb4e5876b58cd88482b8f885bcbb..93662da799af8977590a5be690928d7494406210 100644 (file)
@@ -151,19 +151,17 @@ class GroupblockAction extends Action
     function areYouSureForm()
     {
         $id = $this->profile->id;
-        $this->elementStart('form', array('id' => 'block-' . $id,
-                                           'method' => 'post',
-                                           'class' => 'form_settings form_entity_block',
-                                           'action' => common_local_url('groupblock')));
-        $this->elementStart('fieldset');
-        $this->hidden('token', common_session_token());
-        $this->element('legend', null, _('Block user'));
         $this->element('p', null,
                        sprintf(_('Are you sure you want to block user "%s" from the group "%s"? '.
                                  'They will be removed from the group, unable to post, and '.
                                  'unable to subscribe to the group in the future.'),
                                $this->profile->getBestName(),
                                $this->group->getBestName()));
+        $this->elementStart('form', array('id' => 'block-' . $id,
+                                           'method' => 'post',
+                                           'class' => 'block',
+                                           'action' => common_local_url('groupblock')));
+        $this->hidden('token', common_session_token());
         $this->hidden('blockto-' . $this->profile->id,
                       $this->profile->id,
                       'blockto');
@@ -175,9 +173,8 @@ class GroupblockAction extends Action
                 $this->hidden($k, $v);
             }
         }
-        $this->submit('form_action-no', _('No'), 'submit form_action-primary', 'no', _("Do not block this user from this group"));
-        $this->submit('form_action-yes', _('Yes'), 'submit form_action-secondary', 'yes', _('Block this user from this group'));
-        $this->elementEnd('fieldset');
+        $this->submit('no', _('No'));
+        $this->submit('yes', _('Yes'));
         $this->elementEnd('form');
     }
 
diff --git a/actions/groupdesignsettings.php b/actions/groupdesignsettings.php
new file mode 100644 (file)
index 0000000..7270bc8
--- /dev/null
@@ -0,0 +1,330 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Change user password
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Settings
+ * @package   Laconica
+ * @author    Sarven Capadisli <csarven@controlyourself.ca>
+ * @author    Zach Copley <zach@controlyourself.ca>
+ * @copyright 2008-2009 Control Yourself, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://laconi.ca/
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/lib/designsettings.php';
+
+class GroupDesignSettingsAction extends DesignSettingsAction
+{
+    var $group = null;
+
+    /**
+     * Prepare to run
+     */
+
+    function prepare($args)
+    {
+        parent::prepare($args);
+
+        if (!common_config('inboxes','enabled')) {
+            $this->serverError(_('Inboxes must be enabled for groups to work'));
+            return false;
+        }
+
+        if (!common_logged_in()) {
+            $this->clientError(_('You must be logged in to edit a group.'));
+            return false;
+        }
+
+        $nickname_arg = $this->trimmed('nickname');
+        $nickname = common_canonical_nickname($nickname_arg);
+
+        // Permanent redirect on non-canonical nickname
+
+        if ($nickname_arg != $nickname) {
+            $args = array('nickname' => $nickname);
+            common_redirect(common_local_url('groupdesignsettings', $args), 301);
+            return false;
+        }
+
+        if (!$nickname) {
+            $this->clientError(_('No nickname'), 404);
+            return false;
+        }
+
+        $groupid = $this->trimmed('groupid');
+
+        if ($groupid) {
+            $this->group = User_group::staticGet('id', $groupid);
+        } else {
+            $this->group = User_group::staticGet('nickname', $nickname);
+        }
+
+        if (!$this->group) {
+            $this->clientError(_('No such group'), 404);
+            return false;
+        }
+
+        $cur = common_current_user();
+
+        if (!$cur->isAdmin($this->group)) {
+            $this->clientError(_('You must be an admin to edit the group'), 403);
+            return false;
+        }
+
+        $this->submitaction = common_local_url('groupdesignsettings',
+            array('nickname' => $this->group->nickname));
+
+        return true;
+    }
+
+    /**
+     * A design for this action
+     *
+     * if the group attribute has been set, returns that group's
+     * design.
+     *
+     * @return Design a design object to use
+     */
+
+    function getDesign()
+    {
+
+        if (empty($this->group)) {
+            return null;
+        }
+
+        return $this->group->getDesign();
+    }
+
+    /**
+     * Title of the page
+     *
+     * @return string Title of the page
+     */
+
+    function title()
+    {
+        return _('Group design');
+    }
+
+    /**
+     * Instructions for use
+     *
+     * @return instructions for use
+     */
+
+    function getInstructions()
+    {
+        return _('Customize the way your group looks ' .
+        'with a background image and a colour palette of your choice.');
+    }
+
+    /**
+     * Override to show group nav stuff
+     *
+     * @return nothing
+     */
+
+    function showLocalNav()
+    {
+        $nav = new GroupNav($this, $this->group);
+        $nav->show();
+    }
+
+    /**
+     * Get the design we want to edit
+     *
+     * @return Design
+     */
+
+    function getWorkingDesign() {
+
+        $design = null;
+
+        if (isset($this->group)) {
+            $design = $this->group->getDesign();
+        }
+
+        if (empty($design)) {
+            $design = $this->defaultDesign();
+        }
+
+        return $design;
+    }
+
+    /**
+     * Content area of the page
+     *
+     * Shows a form for changing the design
+     *
+     * @return void
+     */
+
+    function showContent()
+    {
+        $this->showDesignForm($this->getWorkingDesign());
+    }
+
+    /**
+     * Save or update the group's design settings
+     *
+     * @return void
+     */
+
+    function saveDesign()
+    {
+        try {
+
+            $bgcolor = new WebColor($this->trimmed('design_background'));
+            $ccolor  = new WebColor($this->trimmed('design_content'));
+            $sbcolor = new WebColor($this->trimmed('design_sidebar'));
+            $tcolor  = new WebColor($this->trimmed('design_text'));
+            $lcolor  = new WebColor($this->trimmed('design_links'));
+
+        } catch (WebColorException $e) {
+            $this->showForm($e->getMessage());
+            return;
+        }
+
+        $onoff = $this->arg('design_background-image_onoff');
+
+        $on   = false;
+        $off  = false;
+        $tile = false;
+
+        if ($onoff == 'on') {
+            $on = true;
+        } else {
+            $off = true;
+        }
+
+        $repeat = $this->boolean('design_background-image_repeat');
+
+        if ($repeat) {
+            $tile = true;
+        }
+
+        $design = $this->group->getDesign();
+
+        if (!empty($design)) {
+
+            // update design
+
+            $original = clone($design);
+
+            $design->backgroundcolor = $bgcolor->intValue();
+            $design->contentcolor    = $ccolor->intValue();
+            $design->sidebarcolor    = $sbcolor->intValue();
+            $design->textcolor       = $tcolor->intValue();
+            $design->linkcolor       = $lcolor->intValue();
+            $design->backgroundimage = $filepath;
+
+            $design->setDisposition($on, $off, $tile);
+
+            $result = $design->update($original);
+
+            if ($result === false) {
+                common_log_db_error($design, 'UPDATE', __FILE__);
+                $this->showForm(_('Couldn\'t update your design.'));
+                return;
+            }
+
+        } else {
+
+            $this->group->query('BEGIN');
+
+            // save new design
+
+            $design = new Design();
+
+            $design->backgroundcolor = $bgcolor->intValue();
+            $design->contentcolor    = $ccolor->intValue();
+            $design->sidebarcolor    = $sbcolor->intValue();
+            $design->textcolor       = $tcolor->intValue();
+            $design->linkcolor       = $lcolor->intValue();
+            $design->backgroundimage = $filepath;
+
+            $design->setDisposition($on, $off, $tile);
+
+            $id = $design->insert();
+
+            if (empty($id)) {
+                common_log_db_error($id, 'INSERT', __FILE__);
+                $this->showForm(_('Unable to save your design settings!'));
+                return;
+            }
+
+            $original = clone($this->group);
+            $this->group->design_id = $id;
+            $result = $this->group->update($original);
+
+            if (empty($result)) {
+                common_log_db_error($original, 'UPDATE', __FILE__);
+                $this->showForm(_('Unable to save your design settings!'));
+                $this->group->query('ROLLBACK');
+                return;
+            }
+
+            $this->group->query('COMMIT');
+
+        }
+
+        $this->saveBackgroundImage($design);
+
+        $this->showForm(_('Design preferences saved.'), true);
+    }
+
+    /**
+     * Handle input and output a page (overrided)
+     *
+     * @param array $args $_REQUEST arguments
+     *
+     * @return void
+     */
+
+    function handle($args)
+    {
+        parent::handle($args);
+        if (!common_logged_in()) {
+            $this->clientError(_('Not logged in.'));
+            return;
+        } else if (!common_is_real_login()) {
+            // Cookie theft means that automatic logins can't
+            // change important settings or see private info, and
+            // _all_ our settings are important
+            common_set_returnto($this->selfUrl());
+            $user = common_current_user();
+            if ($user->hasOpenID()) {
+                common_redirect(common_local_url('openidlogin'), 303);
+            } else {
+                common_redirect(common_local_url('login'), 303);
+            }
+        } else if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+            $this->handlePost();
+        } else {
+            $this->showForm();
+        }
+    }
+
+}
index fe6127da296243a0c13f15625a8fc57f162f5c83..8f6158dacaafbbd9626b525284745d3451c3f548 100644 (file)
@@ -50,7 +50,7 @@ define('MAX_ORIGINAL', 480);
  * @link     http://laconi.ca/
  */
 
-class GrouplogoAction extends Action
+class GrouplogoAction extends GroupDesignAction
 {
     var $mode = null;
     var $imagefile = null;
index abfad3f0ddadab035f186ebe01af9146c4e0f456..d132cdf9670512d5f5083a713726af77b014d90b 100644 (file)
@@ -44,7 +44,7 @@ require_once INSTALLDIR.'/lib/publicgroupnav.php';
  * @link     http://laconi.ca/
  */
 
-class GroupmembersAction extends Action
+class GroupmembersAction extends GroupDesignAction
 {
     var $page = null;
 
index 7e52cdbcc6af6d20b88be1944f038a306d999c0c..c793f582491035d752ae1b696f4a2bed8764ebd5 100644 (file)
@@ -19,7 +19,7 @@
 
 if (!defined('LACONICA')) { exit(1); }
 
-class InviteAction extends Action
+class InviteAction extends CurrentUserDesignAction
 {
     var $mode = null;
     var $error = null;
index eac4d0a3aeeda3622eec4bcbaec8a787fbbe82ac..d7ed440e9237d0a32fd814eb1dd99269ad6393f9 100644 (file)
@@ -45,9 +45,8 @@ require_once INSTALLDIR.'/lib/feedlist.php';
  * @link     http://laconi.ca/
  */
 
-class RepliesAction extends Action
+class RepliesAction extends OwnerDesignAction
 {
-    var $user = null;
     var $page = null;
 
     /**
index 865045337aa9c07e8421f4801aeaa4f584f7e718..01f38a8927ced49a4a2f88864f905e9b4104bbaf 100644 (file)
@@ -45,7 +45,7 @@ require_once INSTALLDIR.'/lib/feedlist.php';
  * @link     http://laconi.ca/
  */
 
-class ShowfavoritesAction extends Action
+class ShowfavoritesAction extends CurrentUserDesignAction
 {
     /** User we're getting the faves of */
     var $user = null;
index 357f579d8f1c3d060b778ac19a3cd1ebad23624b..b6a0f4844e78a69752cd6c690203b54d869ede07 100644 (file)
@@ -47,10 +47,9 @@ define('MEMBERS_PER_SECTION', 27);
  * @link     http://laconi.ca/
  */
 
-class ShowgroupAction extends Action
+class ShowgroupAction extends GroupDesignAction
 {
-    /** group we're viewing. */
-    var $group = null;
+
     /** page we're viewing. */
     var $page = null;
 
diff --git a/actions/userdesignsettings.php b/actions/userdesignsettings.php
new file mode 100644 (file)
index 0000000..d6088aa
--- /dev/null
@@ -0,0 +1,208 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Change user password
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Settings
+ * @package   Laconica
+ * @author    Sarven Capadisli <csarven@controlyourself.ca>
+ * @author    Zach Copley <zach@controlyourself.ca>
+ * @copyright 2008-2009 Control Yourself, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://laconi.ca/
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/lib/designsettings.php';
+
+class UserDesignSettingsAction extends DesignSettingsAction
+{
+    function prepare($args)
+    {
+        parent::prepare($args);
+        $this->submitaction = common_local_url('userdesignsettings');
+        return true;
+    }
+     
+    /**
+     * Title of the page
+     *
+     * @return string Title of the page
+     */
+
+    function title()
+    {
+        return _('Profile design');
+    }
+
+    /**
+     * Instructions for use
+     *
+     * @return instructions for use
+     */
+
+    function getInstructions()
+    {
+        return _('Customize the way your profile looks ' .
+        'with a background image and a colour palette of your choice.');
+    }
+
+    /**
+     * Get the design we want to edit
+     *
+     * @return Design
+     */
+     
+    function getWorkingDesign() {
+        
+        $user = common_current_user();
+        $design = $user->getDesign();
+
+        if (empty($design)) {
+            $design = $this->defaultDesign();
+        }
+     
+        return $design;
+    }
+    
+    /**
+     * Content area of the page
+     *
+     * Shows a form for changing the design
+     *
+     * @return void
+     */
+     
+    function showContent()
+    {
+        $this->showDesignForm($this->getWorkingDesign());
+    }
+
+    /**
+     * Save or update the user's design settings
+     *
+     * @return void
+     */
+
+    function saveDesign()
+    {
+        try {
+
+            $bgcolor = new WebColor($this->trimmed('design_background'));
+            $ccolor  = new WebColor($this->trimmed('design_content'));
+            $sbcolor = new WebColor($this->trimmed('design_sidebar'));
+            $tcolor  = new WebColor($this->trimmed('design_text'));
+            $lcolor  = new WebColor($this->trimmed('design_links'));
+
+        } catch (WebColorException $e) {
+            $this->showForm($e->getMessage());
+            return;
+        }
+
+        $onoff = $this->arg('design_background-image_onoff');
+
+        $on   = false;
+        $off  = false;
+        $tile = false;
+
+        if ($onoff == 'on') {
+            $on = true;
+        } else {
+            $off = true;
+        }
+
+        $repeat = $this->boolean('design_background-image_repeat');
+
+        if ($repeat) {
+            $tile = true;
+        }
+
+        $user = common_current_user();
+        $design = $user->getDesign();
+
+        if (!empty($design)) {
+
+            $original = clone($design);
+
+            $design->backgroundcolor = $bgcolor->intValue();
+            $design->contentcolor    = $ccolor->intValue();
+            $design->sidebarcolor    = $sbcolor->intValue();
+            $design->textcolor       = $tcolor->intValue();
+            $design->linkcolor       = $lcolor->intValue();
+            $design->backgroundimage = $filepath;
+
+            $design->setDisposition($on, $off, $tile);
+
+            $result = $design->update($original);
+
+            if ($result === false) {
+                common_log_db_error($design, 'UPDATE', __FILE__);
+                $this->showForm(_('Couldn\'t update your design.'));
+                return;
+            }
+
+            // update design
+        } else {
+
+            $user->query('BEGIN');
+
+            // save new design
+            $design = new Design();
+
+            $design->backgroundcolor = $bgcolor->intValue();
+            $design->contentcolor    = $ccolor->intValue();
+            $design->sidebarcolor    = $sbcolor->intValue();
+            $design->textcolor       = $tcolor->intValue();
+            $design->linkcolor       = $lcolor->intValue();
+            $design->backgroundimage = $filepath;
+
+            $design->setDisposition($on, $off, $tile);
+
+            $id = $design->insert();
+
+            if (empty($id)) {
+                common_log_db_error($id, 'INSERT', __FILE__);
+                $this->showForm(_('Unable to save your design settings!'));
+                return;
+            }
+
+            $original = clone($user);
+            $user->design_id = $id;
+            $result = $user->update($original);
+
+            if (empty($result)) {
+                common_log_db_error($original, 'UPDATE', __FILE__);
+                $this->showForm(_('Unable to save your design settings!'));
+                $user->query('ROLLBACK');
+                return;
+            }
+
+            $user->query('COMMIT');
+
+        }
+
+        $this->saveBackgroundImage($design);
+
+        $this->showForm(_('Design preferences saved.'), true);
+    }
+}
index e3088dcbd83d6e26dc51110ecd633f626e78becc..7ead6e6e49172e4941035e0a4c5d433c210f9026 100644 (file)
@@ -46,9 +46,8 @@ require_once INSTALLDIR.'/lib/grouplist.php';
  * @link     http://laconi.ca/
  */
 
-class UsergroupsAction extends Action
+class UsergroupsAction extends OwnerDesignAction
 {
-    var $user = null;
     var $page = null;
     var $profile = null;
 
diff --git a/background/.gitignore b/background/.gitignore
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/classes/Design.php b/classes/Design.php
new file mode 100644 (file)
index 0000000..da4b670
--- /dev/null
@@ -0,0 +1,155 @@
+<?php
+/*
+ * Laconica - the distributed open-source microblogging tool
+ * Copyright (C) 2009, Control Yourself, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+define('BACKGROUND_ON', 1);
+define('BACKGROUND_OFF', 2);
+define('BACKGROUND_TILE', 4);
+
+/**
+ * Table Definition for design
+ */
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+require_once INSTALLDIR . '/lib/webcolor.php';
+
+class Design extends Memcached_DataObject
+{
+    ###START_AUTOCODE
+    /* the code below is auto generated do not remove the above tag */
+
+    public $__table = 'design';                          // table name
+    public $id;                              // int(4)  primary_key not_null
+    public $backgroundcolor;                 // int(4)
+    public $contentcolor;                    // int(4)
+    public $sidebarcolor;                    // int(4)
+    public $textcolor;                       // int(4)
+    public $linkcolor;                       // int(4)
+    public $backgroundimage;                 // varchar(255)
+    public $disposition;                     // tinyint(1)   default_1
+
+    /* Static get */
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Design',$k,$v); }
+
+    /* the code above is auto generated do not remove the tag below */
+    ###END_AUTOCODE
+
+    function showCSS($out)
+    {
+        try {
+
+            $bgcolor = new WebColor($this->backgroundcolor);
+            $ccolor  = new WebColor($this->contentcolor);
+            $sbcolor = new WebColor($this->sidebarcolor);
+            $tcolor  = new WebColor($this->textcolor);
+            $lcolor  = new WebColor($this->linkcolor);
+
+        } catch (WebColorException $e) {
+            // This shouldn't happen
+            common_log(LOG_ERR, "Unable to create color for design $id.",
+                __FILE__);
+        }
+
+        $css  = 'body { background-color: #' . $bgcolor->hexValue() . ' }' . "\n";
+        $css .= '#content, #site_nav_local_views .current a { background-color: #';
+        $css .= $ccolor->hexValue() . '} '."\n";
+        $css .= '#aside_primary { background-color: #'. $sbcolor->hexValue() . ' }' . "\n";
+        $css .= 'html body { color: #'. $tcolor->hexValue() . ' }'. "\n";
+        $css .= 'a { color: #' . $lcolor->hexValue() . ' }' . "\n";
+
+        if (!empty($this->backgroundimage) &&
+            $this->disposition & BACKGROUND_ON) {
+
+           $repeat = ($this->disposition & BACKGROUND_TILE) ?
+               'background-repeat:repeat;' :
+               'background-repeat:no-repeat;';
+
+            $css .= 'body { background-image:url(' .
+                Design::url($this->backgroundimage) .
+                '); ' . $repeat . ' }' . "\n";
+        }
+
+        $out->element('style', array('type' => 'text/css'), $css);
+
+    }
+
+    static function filename($id, $extension, $extra=null)
+    {
+        return $id . (($extra) ? ('-' . $extra) : '') . $extension;
+    }
+
+    static function path($filename)
+    {
+        $dir = common_config('background', 'dir');
+
+        if ($dir[strlen($dir)-1] != '/') {
+            $dir .= '/';
+        }
+
+        return $dir . $filename;
+    }
+
+    static function url($filename)
+    {
+        $path = common_config('background', 'path');
+
+        if ($path[strlen($path)-1] != '/') {
+            $path .= '/';
+        }
+
+        if ($path[0] != '/') {
+            $path = '/'.$path;
+        }
+
+        $server = common_config('background', 'server');
+
+        if (empty($server)) {
+            $server = common_config('site', 'server');
+        }
+
+        // XXX: protocol
+
+        return 'http://'.$server.$path.$filename;
+    }
+
+    function setDisposition($on, $off, $tile)
+    {
+        if ($on) {
+            $this->disposition |= BACKGROUND_ON;
+        } else {
+            $this->disposition &= ~BACKGROUND_ON;
+        }
+
+        if ($off) {
+            $this->disposition |= BACKGROUND_OFF;
+        } else {
+            $this->disposition &= ~BACKGROUND_OFF;
+        }
+
+        if ($tile) {
+            $this->disposition |= BACKGROUND_TILE;
+        } else {
+            $this->disposition &= ~BACKGROUND_TILE;
+        }
+    }
+
+}
index 08a166d5ae4a4478be5943cd69eee776834e54ec..c7eede94e19e81c4f6231744f23c085e104f37e7 100644 (file)
@@ -62,14 +62,13 @@ class User extends Memcached_DataObject
     public $autosubscribe;                   // tinyint(1)
     public $urlshorteningservice;            // varchar(50)   default_ur1.ca
     public $inboxed;                         // tinyint(1)
+    public $design_id;                       // int(4)
+    public $viewdesigns;                     // tinyint(1)   default_1
     public $created;                         // datetime()   not_null
     public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
 
     /* Static get */
-    function staticGet($k,$v=NULL)
-    {
-        return Memcached_DataObject::staticGet('User',$k,$v);
-    }
+    function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('User',$k,$v); }
 
     /* the code above is auto generated do not remove the tag below */
     ###END_AUTOCODE
@@ -684,4 +683,9 @@ class User extends Memcached_DataObject
 
         return ($cnt > 0);
     }
+
+    function getDesign()
+    {
+        return Design::staticGet('id', $this->design_id);
+    }
 }
index 1a24124bb176413e70bfe2b31702d39ddfbdcf14..8a56b9e52b32e893cab4fd5f0c582abd2380330b 100644 (file)
@@ -19,6 +19,7 @@ class User_group extends Memcached_DataObject
     public $homepage_logo;                   // varchar(255)
     public $stream_logo;                     // varchar(255)
     public $mini_logo;                       // varchar(255)
+    public $design_id;                       // int(4)
     public $created;                         // datetime()   not_null
     public $modified;                        // timestamp()   not_null default_CURRENT_TIMESTAMP
 
@@ -239,4 +240,10 @@ class User_group extends Memcached_DataObject
         }
         return null;
     }
+
+    function getDesign()
+    {
+        return Design::staticGet('id', $this->design_id);
+    }
+
 }
old mode 100644 (file)
new mode 100755 (executable)
index df292bb..5ced158
@@ -38,6 +38,19 @@ modified = 384
 [consumer__keys]
 consumer_key = K
 
+[design]
+id = 129
+backgroundcolor = 1
+contentcolor = 1
+sidebarcolor = 1
+textcolor = 1
+linkcolor = 1
+backgroundimage = 2
+disposition = 17
+
+[design__keys]
+id = N
+
 [fave]
 notice_id = 129
 user_id = 129
@@ -430,6 +443,8 @@ uri = 2
 autosubscribe = 17
 urlshorteningservice = 2
 inboxed = 17
+design_id = 1
+viewdesigns = 17
 created = 142
 modified = 384
 
@@ -453,6 +468,7 @@ original_logo = 2
 homepage_logo = 2
 stream_logo = 2
 mini_logo = 2
+design_id = 1
 created = 142
 modified = 384
 
index b8c0824f5aa765b024a924c124142d5ac482aab7..8d1d47d38d35c14d2d9d45b46f5410645cbc452b 100644 (file)
@@ -41,6 +41,7 @@ create table sms_carrier (
 /* local users */
 
 create table user (
+
     id integer primary key comment 'foreign key to profile table' references profile (id),
     nickname varchar(64) unique key comment 'nickname or username, duped in profile',
     password varchar(255) comment 'salted password, can be null for OpenID users',
@@ -69,6 +70,9 @@ create table user (
     autosubscribe tinyint default 0 comment 'automatically subscribe to users who subscribe to us',
     urlshorteningservice varchar(50) default 'ur1.ca' comment 'service to use for auto-shortening URLs',
     inboxed tinyint default 0 comment 'has an inbox been created for this user?',
+    design_id integer comment 'id of a design' references design(id),
+    viewdesigns tinyint default 1 comment 'whether to view user-provided designs',
+
     created datetime not null comment 'date this record was created',
     modified timestamp comment 'date this record was modified',
 
@@ -383,6 +387,7 @@ create table user_group (
     homepage_logo varchar(255) comment 'homepage (profile) size logo',
     stream_logo varchar(255) comment 'stream-sized logo',
     mini_logo varchar(255) comment 'mini logo',
+    design_id integer comment 'id of a design' references design(id),
 
     created datetime not null comment 'date this record was created',
     modified timestamp comment 'date this record was modified',
@@ -484,6 +489,17 @@ create table file_to_post (
     unique(file_id, post_id)
 ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
 
+create table design (
+    id integer primary key auto_increment comment 'design ID',
+    backgroundcolor integer comment 'main background color',
+    contentcolor integer comment 'content area background color',
+    sidebarcolor integer comment 'sidebar background color',
+    textcolor integer comment 'text color',
+    linkcolor integer comment 'link color',
+    backgroundimage varchar(255) comment 'background image, if any',
+    disposition tinyint default 1 comment 'bit 1 = hide background image, bit 2 = display background image, bit 4 = tile background image'
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
 create table group_block (
    group_id integer not null comment 'group profile is blocked from' references user_group (id),
    blocked integer not null comment 'profile that is blocked' references profile (id),
diff --git a/js/farbtastic/farbtastic.go.js b/js/farbtastic/farbtastic.go.js
deleted file mode 100644 (file)
index 0149eca..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-/** Init for Farbtastic library and page setup
- *
- * @package   Laconica
- * @author Sarven Capadisli <csarven@controlyourself.ca>
- * @copyright 2009 Control Yourself, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://laconi.ca/
- */
-$(document).ready(function() {
-    function UpdateColors(S) {
-        C = $(S).val();
-        switch (parseInt(S.id.slice(-1))) {
-            case 0: default:
-                $('body').css({'background-color':C});
-                break;
-            case 1:
-                $('#content').css({'background-color':C});
-                break;
-            case 2:
-                $('#aside_primary').css({'background-color':C});
-                break;
-            case 3:
-                $('body').css({'color':C});
-                break;
-            case 4:
-                $('a').css({'color':C});
-                break;
-        }
-    }
-
-    function UpdateFarbtastic(e) {
-        f.linked = e;
-        f.setColor(e.value);
-    }
-
-    function UpdateSwatch(e) {
-        $(e).css({"background-color": e.value,
-                  "color": f.hsl[2] > 0.5 ? "#000": "#fff"});
-    }
-
-    function SynchColors(e) {
-        var S = f.linked;
-        var C = f.color;
-
-        if (S && S.value && S.value != C) {
-            S.value = C;
-            UpdateSwatch(S);
-            UpdateColors(S);
-        }
-    }
-
-    function Init() {
-        $('#settings_design_color').append('<div id="color-picker"></div>');
-        $('#color-picker').hide();
-
-        f = $.farbtastic('#color-picker', SynchColors);
-        swatches = $('#settings_design_color .swatch');
-
-        swatches
-            .each(SynchColors)
-            .blur(function() {
-                $(this).val($(this).val().toUpperCase());
-             })
-            .focus(function() {
-                $('#color-picker').show();
-                UpdateFarbtastic(this);
-            })
-            .change(function() {
-                UpdateFarbtastic(this);
-                UpdateSwatch(this);
-                UpdateColors(this);
-            }).change();
-    }
-
-    var f, swatches;
-    Init();
-    $('#form_settings_design').bind('reset', function(){
-        setTimeout(function(){
-            swatches.each(function(){UpdateColors(this);});
-            $('#color-picker').remove();
-            swatches.unbind();
-            Init();
-        },10);
-    });
-});
index 24a377803c42008f36b7e455a369b4dcd15bea51..d8b5ad9cdf077a2460c6cfab11cef2eb53f0c229 100644 (file)
@@ -1,5 +1,21 @@
-// $Id: farbtastic.js,v 1.2 2007/01/08 22:53:01 unconed Exp $
-// Farbtastic 1.2
+/**
+ * Farbtastic Color Picker 1.2
+ * Â© 2008 Steven Wittens
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
 
 jQuery.fn.farbtastic = function (callback) {
   $.farbtastic(this, callback);
diff --git a/js/userdesign.go.js b/js/userdesign.go.js
new file mode 100644 (file)
index 0000000..b54b492
--- /dev/null
@@ -0,0 +1,99 @@
+/** Init for Farbtastic library and page setup
+ *
+ * @package   Laconica
+ * @author Sarven Capadisli <csarven@controlyourself.ca>
+ * @copyright 2009 Control Yourself, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://laconi.ca/
+ */
+$(document).ready(function() {
+    function UpdateColors(S) {
+        C = $(S).val();
+        switch (parseInt(S.id.slice(-1))) {
+            case 1: default:
+                $('html, body').css({'background-color':C});
+                break;
+            case 2:
+                $('#content, #site_nav_local_views .current a').css({'background-color':C});
+                break;
+            case 3:
+                $('#aside_primary').css({'background-color':C});
+                break;
+            case 4:
+                $('html body').css({'color':C});
+                break;
+            case 5:
+                $('a').css({'color':C});
+                break;
+        }
+    }
+
+    function UpdateFarbtastic(e) {
+        f.linked = e;
+        f.setColor(e.value);
+    }
+
+    function UpdateSwatch(e) {
+        $(e).css({"background-color": e.value,
+                  "color": f.hsl[2] > 0.5 ? "#000": "#fff"});
+    }
+
+    function SynchColors(e) {
+        var S = f.linked;
+        var C = f.color;
+
+        if (S && S.value && S.value != C) {
+            S.value = C;
+            UpdateSwatch(S);
+            UpdateColors(S);
+        }
+    }
+
+    function InitFarbtastic() {
+        $('#settings_design_color').append('<div id="color-picker"></div>');
+        $('#color-picker').hide();
+
+        f = $.farbtastic('#color-picker', SynchColors);
+        swatches = $('#settings_design_color .swatch');
+
+        swatches
+            .each(SynchColors)
+            .blur(function() {
+                tv = $(this).val();
+                $(this).val(tv.toUpperCase());
+                (tv.length == 4) ? ((tv[0] == '#') ? $(this).val('#'+tv[1]+tv[1]+tv[2]+tv[2]+tv[3]+tv[3]) : '') : '';
+             })
+            .focus(function() {
+                $('#color-picker').show();
+                UpdateFarbtastic(this);
+            })
+            .change(function() {
+                UpdateFarbtastic(this);
+                UpdateSwatch(this);
+                UpdateColors(this);
+            }).change();
+    }
+
+    var f, swatches;
+    InitFarbtastic();
+    $('#form_settings_design').bind('reset', function(){
+        setTimeout(function(){
+            swatches.each(function(){UpdateColors(this);});
+            $('#color-picker').remove();
+            swatches.unbind();
+            InitFarbtastic();
+        },10);
+    });
+
+    $('#design_background-image_off').focus(function() {
+        $('body').css({'background-image':'none'});
+    });
+    $('#design_background-image_on').focus(function() {
+        var bis = $('#design_background-image_onoff img')[0].src;
+        $('body').css({'background-image':'url('+bis+')'});
+    });
+
+    $('#design_background-image_repeat').click(function() {
+        ($(this)[0].checked) ? $('body').css({'background-repeat':'repeat'}) : $('body').css({'background-repeat':'no-repeat'});
+    });
+});
\ No newline at end of file
index ce0c20d31ba05b5a243dc777ee0a6e834e09f7ce..17ae4c071926829234a28b741d91a21076e68784 100644 (file)
@@ -235,7 +235,7 @@ $(document).ready(function(){
 });
 
 function NoticeReply() {
-    if ($('#notice_data-text').length > 0) {
+    if ($('#notice_data-text').length > 0 && $('#content .notice_reply').length > 0) {
         $('#content .notice').each(function() {
             var notice = $(this)[0];
             $($('.notice_reply', notice)[0]).click(function() {
@@ -308,4 +308,4 @@ function NoticeAttachments() {
             $(this).closest(".entry-title").removeClass('ov');
         }
     );
-}
\ No newline at end of file
+}
index 86800d2a36b1e3630d61cb399dda7523be7f2d4e..4ab50abcef1134a50282bbdfb64a45a4696fa972 100644 (file)
@@ -115,7 +115,7 @@ class AccountSettingsNav extends Widget
                 'openidsettings' =>
                 array(_('OpenID'),
                       _('Add or remove OpenIDs')),
-                'designsettings' =>
+                'userdesignsettings' =>
                 array(_('Design'),
                       _('Design your profile')),
                 'othersettings' =>
index a781c3092c9fb7215f68610bc5756fb7f2fbf478..c80c0c418f91417433c93deb448f419296ad68dc 100644 (file)
@@ -46,7 +46,6 @@ if (!defined('LACONICA')) {
  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  * @link     http://laconi.ca/
  * @see      Notice
- * @see      StreamAction
  * @see      NoticeListItem
  * @see      ProfileNoticeList
  */
index bbd9f78c3e1df660ff6c37fc9302a3da7a57b317..eb8a2b8736cab98b153d217c73bcf075d390e9f1 100644 (file)
@@ -71,6 +71,15 @@ $config =
         array('name' => 'Just another Laconica microblog',
               'server' => $_server,
               'theme' => 'default',
+              'skin' => 'default',
+              'design' =>
+              array('backgroundcolor' => '#F0F2F5',
+                    'contentcolor' => '#FFFFFF',
+                    'sidebarcolor' => '#CEE1E9',
+                    'textcolor' => '#000000',
+                    'linkcolor' => '#002E6E',
+                    'backgroundimage' => null,
+                    'disposition' => 1),
               'path' => $_path,
               'logfile' => null,
               'logo' => null,
@@ -111,6 +120,10 @@ $config =
         array('server' => null,
               'dir' => INSTALLDIR . '/avatar/',
               'path' => $_path . '/avatar/'),
+        'background' =>
+        array('server' => null,
+              'dir' => INSTALLDIR . '/background/',
+              'path' => $_path . '/background/'),
         'public' =>
         array('localonly' => true,
               'blacklist' => array(),
diff --git a/lib/currentuserdesignaction.php b/lib/currentuserdesignaction.php
new file mode 100644 (file)
index 0000000..7c2520c
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Base class for actions that use the current user's design
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Action
+ * @package   Laconica
+ * @author    Evan Prodromou <evan@controlyourself.ca>
+ * @copyright 2009 Control Yourself, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://laconi.ca/
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * Base class for actions that use the current user's design
+ *
+ * Some pages (settings in particular) use the current user's chosen
+ * design. This superclass returns that design.
+ *
+ * @category Action
+ * @package  Laconica
+ * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://laconi.ca/
+ *
+ */
+
+class CurrentUserDesignAction extends Action
+{
+
+    /**
+      * Show the user's design stylesheet
+      *
+      * @return nothing
+      */
+     function showStylesheets()
+     {
+         parent::showStylesheets();
+
+         $design = $this->getDesign();
+
+         if (!empty($design)) {
+             $design->showCSS($this);
+         }
+     }
+
+    /**
+     * A design for this action
+     *
+     * if the user attribute has been set, returns that user's
+     * design.
+     *
+     * @return Design a design object to use
+     */
+
+    function getDesign()
+    {
+        $cur = common_current_user();
+
+        if (empty($cur)) {
+            return null;
+        }
+
+        return $cur->getDesign();
+    }
+
+
+}
diff --git a/lib/designsettings.php b/lib/designsettings.php
new file mode 100644 (file)
index 0000000..6aa6bb2
--- /dev/null
@@ -0,0 +1,402 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Change user password
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Settings
+ * @package   Laconica
+ * @author    Sarven Capadisli <csarven@controlyourself.ca>
+ * @author    Zach Copley <zach@controlyourself.ca>
+ * @copyright 2008-2009 Control Yourself, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://laconi.ca/
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/lib/accountsettingsaction.php';
+require_once INSTALLDIR . '/lib/webcolor.php';
+
+class DesignSettingsAction extends AccountSettingsAction
+{
+
+    var $submitaction = null;
+
+    /**
+     * Title of the page
+     *
+     * @return string Title of the page
+     */
+
+    function title()
+    {
+        return _('Profile design');
+    }
+
+    /**
+     * Instructions for use
+     *
+     * @return instructions for use
+     */
+
+    function getInstructions()
+    {
+        return _('Customize the way your profile looks ' .
+        'with a background image and a colour palette of your choice.');
+    }
+
+    function showDesignForm($design)
+    {
+
+        $this->elementStart('form', array('method' => 'post',
+                                          'enctype' => 'multipart/form-data',
+                                          'id' => 'form_settings_design',
+                                          'class' => 'form_settings',
+                                          'action' => $this->submitaction));
+        $this->elementStart('fieldset');
+        $this->hidden('token', common_session_token());
+
+        $this->elementStart('fieldset', array('id' =>
+            'settings_design_background-image'));
+        $this->element('legend', null, _('Change background image'));
+        $this->elementStart('ul', 'form_data');
+        $this->elementStart('li');
+        $this->element('label', array('for' => 'design_background-image_file'),
+                                _('Upload file'));
+        $this->element('input', array('name' => 'design_background-image_file',
+                                      'type' => 'file',
+                                      'id' => 'design_background-image_file'));
+        $this->element('p', 'form_guide', _('You can upload your personal ' .
+            'background image. The maximum file size is 2Mb.'));
+        $this->element('input', array('name' => 'MAX_FILE_SIZE',
+                                      'type' => 'hidden',
+                                      'id' => 'MAX_FILE_SIZE',
+                                      'value' => ImageFile::maxFileSizeInt()));
+        $this->elementEnd('li');
+
+        if (!empty($design->backgroundimage)) {
+
+            $this->elementStart('li', array('id' => 'design_background-image_onoff'));
+
+            $this->element('img', array('src' =>
+                Design::url($design->backgroundimage)));
+
+            $attrs = array('name' => 'design_background-image_onoff',
+                           'type' => 'radio',
+                           'id' => 'design_background-image_on',
+                           'class' => 'radio',
+                           'value' => 'on');
+
+            if ($design->disposition & BACKGROUND_ON) {
+                $attrs['checked'] = 'checked';
+            }
+
+            $this->element('input', $attrs);
+
+            $this->element('label', array('for' => 'design_background-image_on',
+                                          'class' => 'radio'),
+                                          _('On'));
+
+            $attrs = array('name' => 'design_background-image_onoff',
+                           'type' => 'radio',
+                           'id' => 'design_background-image_off',
+                           'class' => 'radio',
+                           'value' => 'off');
+
+            if ($design->disposition & BACKGROUND_OFF) {
+                $attrs['checked'] = 'checked';
+            }
+
+            $this->element('input', $attrs);
+
+            $this->element('label', array('for' => 'design_background-image_off',
+                                          'class' => 'radio'),
+                                          _('Off'));
+            $this->element('p', 'form_guide', _('Turn background image on or off.'));
+            $this->elementEnd('li');
+        }
+
+        $this->elementStart('li');
+        $this->checkbox('design_background-image_repeat',
+                        _('Tile background image'),
+                        ($design->disposition & BACKGROUND_TILE) ? true : false );
+        $this->elementEnd('li');
+
+        $this->elementEnd('ul');
+        $this->elementEnd('fieldset');
+
+        $this->elementStart('fieldset', array('id' => 'settings_design_color'));
+        $this->element('legend', null, _('Change colours'));
+        $this->elementStart('ul', 'form_data');
+
+        try {
+
+            $bgcolor = new WebColor($design->backgroundcolor);
+
+            $this->elementStart('li');
+            $this->element('label', array('for' => 'swatch-1'), _('Background'));
+            $this->element('input', array('name' => 'design_background',
+                                          'type' => 'text',
+                                          'id' => 'swatch-1',
+                                          'class' => 'swatch',
+                                          'maxlength' => '7',
+                                          'size' => '7',
+                                          'value' => '#' . $bgcolor->hexValue()));
+            $this->elementEnd('li');
+
+            $ccolor = new WebColor($design->contentcolor);
+
+            $this->elementStart('li');
+            $this->element('label', array('for' => 'swatch-2'), _('Content'));
+            $this->element('input', array('name' => 'design_content',
+                                          'type' => 'text',
+                                          'id' => 'swatch-2',
+                                          'class' => 'swatch',
+                                          'maxlength' => '7',
+                                          'size' => '7',
+                                          'value' => '#' . $ccolor->hexValue()));
+            $this->elementEnd('li');
+
+            $sbcolor = new WebColor($design->sidebarcolor);
+
+            $this->elementStart('li');
+            $this->element('label', array('for' => 'swatch-3'), _('Sidebar'));
+            $this->element('input', array('name' => 'design_sidebar',
+                                        'type' => 'text',
+                                        'id' => 'swatch-3',
+                                        'class' => 'swatch',
+                                        'maxlength' => '7',
+                                        'size' => '7',
+                                        'value' => '#' . $sbcolor->hexValue()));
+            $this->elementEnd('li');
+
+            $tcolor = new WebColor($design->textcolor);
+
+            $this->elementStart('li');
+            $this->element('label', array('for' => 'swatch-4'), _('Text'));
+            $this->element('input', array('name' => 'design_text',
+                                        'type' => 'text',
+                                        'id' => 'swatch-4',
+                                        'class' => 'swatch',
+                                        'maxlength' => '7',
+                                        'size' => '7',
+                                        'value' => '#' . $tcolor->hexValue()));
+            $this->elementEnd('li');
+
+            $lcolor = new WebColor($design->linkcolor);
+
+            $this->elementStart('li');
+            $this->element('label', array('for' => 'swatch-5'), _('Links'));
+            $this->element('input', array('name' => 'design_links',
+                                         'type' => 'text',
+                                         'id' => 'swatch-5',
+                                         'class' => 'swatch',
+                                         'maxlength' => '7',
+                                         'size' => '7',
+                                         'value' => '#' . $lcolor->hexValue()));
+
+           $this->elementEnd('li');
+
+       } catch (WebColorException $e) {
+           common_log(LOG_ERR, 'Bad color values in design ID: ' .
+               $design->id);
+       }
+
+       $this->elementEnd('ul');
+       $this->elementEnd('fieldset');
+
+       $this->element('input', array('id' => 'settings_design_reset',
+                                     'type' => 'reset',
+                                     'value' => 'Reset',
+                                     'class' => 'submit form_action-primary',
+                                     'title' => _('Reset back to default')));
+
+        $this->submit('save', _('Save'), 'submit form_action-secondary',
+            'save', _('Save design'));
+
+        $this->elementEnd('fieldset');
+        $this->elementEnd('form');
+    }
+
+    /**
+     * Handle a post
+     *
+     * Validate input and save changes. Reload the form with a success
+     * or error message.
+     *
+     * @return void
+     */
+
+    function handlePost()
+    {
+        // XXX: Robin's workaround for a bug in PHP where $_POST
+        // and $_FILE are empty in the case that the uploaded
+        // file is bigger than PHP is configured to handle.
+
+        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+            if (empty($_POST) && $_SERVER['CONTENT_LENGTH']) {
+
+                $msg = _('The server was unable to handle that much POST ' .
+                    'data (%s bytes) due to its current configuration.');
+
+                $this->showForm(sprintf($msg, $_SERVER['CONTENT_LENGTH']));
+            }
+        }
+
+        // CSRF protection
+        $token = $this->trimmed('token');
+        if (!$token || $token != common_session_token()) {
+            $this->showForm(_('There was a problem with your session token. '.
+                              'Try again, please.'));
+            return;
+        }
+
+        if ($this->arg('save')) {
+            $this->saveDesign();
+        } else if ($this->arg('reset')) {
+            $this->resetDesign();
+        } else {
+            $this->showForm(_('Unexpected form submission.'));
+        }
+    }
+
+    /**
+     * Add the Farbtastic stylesheet
+     *
+     * @return void
+     */
+
+    function showStylesheets()
+    {
+        parent::showStylesheets();
+        $farbtasticStyle =
+          common_path('theme/base/css/farbtastic.css?version='.LACONICA_VERSION);
+
+        $this->element('link', array('rel' => 'stylesheet',
+                                     'type' => 'text/css',
+                                     'href' => $farbtasticStyle,
+                                     'media' => 'screen, projection, tv'));
+    }
+
+    /**
+     * Add the Farbtastic scripts
+     *
+     * @return void
+     */
+
+    function showScripts()
+    {
+        parent::showScripts();
+
+        $farbtasticPack = common_path('js/farbtastic/farbtastic.js');
+        $userDesignGo   = common_path('js/userdesign.go.js');
+
+        $this->element('script', array('type' => 'text/javascript',
+                                       'src' => $farbtasticPack));
+        $this->element('script', array('type' => 'text/javascript',
+                                       'src' => $userDesignGo));
+    }
+
+    /**
+     * Get a default user design
+     *
+     * @return Design design
+     */
+
+    function defaultDesign()
+    {
+        $defaults = common_config('site', 'design');
+
+        $design = new Design();
+
+        try {
+
+            $color = new WebColor();
+
+            $color->parseColor($defaults['backgroundcolor']);
+            $design->backgroundcolor = $color->intValue();
+
+            $color->parseColor($defaults['contentcolor']);
+            $design->contentcolor = $color->intValue();
+
+            $color->parseColor($defaults['sidebarcolor']);
+            $design->sidebarcolor = $color->intValue();
+
+            $color->parseColor($defaults['textcolor']);
+            $design->textcolor = $color->intValue();
+
+            $color->parseColor($defaults['linkcolor']);
+            $design->linkcolor = $color->intValue();
+
+            $design->backgroundimage = $defaults['backgroundimage'];
+
+            $design->disposition = $defaults['disposition'];
+
+        } catch (WebColorException $e) {
+            common_log(LOG_ERR, _('Bad default color settings: ' .
+                $e->getMessage()));
+        }
+
+        return $design;
+    }
+
+    function saveBackgroundImage($design) {
+
+        // Now that we have a Design ID we can add a file to the design.
+        // XXX: This is an additional DB hit, but figured having the image
+        // associated with the Design rather than the User was worth
+        // it. -- Zach
+
+        if ($_FILES['design_background-image_file']['error'] ==
+            UPLOAD_ERR_OK) {
+
+            $filepath = null;
+
+            try {
+                    $imagefile =
+                        ImageFile::fromUpload('design_background-image_file');
+                } catch (Exception $e) {
+                    $this->showForm($e->getMessage());
+                    return;
+                }
+
+            $filename = Design::filename($design->id,
+                image_type_to_extension($imagefile->type),
+                    common_timestamp());
+
+            $filepath = Design::path($filename);
+
+            move_uploaded_file($imagefile->filepath, $filepath);
+
+            $original = clone($design);
+            $design->backgroundimage = $filename;
+            $design->setDisposition(true, false, false);
+            $result = $design->update($original);
+
+            if ($result === false) {
+                common_log_db_error($design, 'UPDATE', __FILE__);
+                $this->showForm(_('Couldn\'t update your design.'));
+                return;
+            }
+        }
+    }
+
+}
index 8fa11a7562752f217edb65bb206f797b5732c40a..498c828514b5f30b57b90ce4817a2d1984e1b587 100644 (file)
@@ -27,10 +27,9 @@ require_once INSTALLDIR.'/lib/profilelist.php';
 
 define('AVATARS_PER_PAGE', 80);
 
-class GalleryAction extends Action
+class GalleryAction extends OwnerDesignAction
 {
     var $profile = null;
-    var $user = null;
     var $page = null;
     var $tag = null;
 
diff --git a/lib/groupdesignaction.php b/lib/groupdesignaction.php
new file mode 100644 (file)
index 0000000..bc95921
--- /dev/null
@@ -0,0 +1,87 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Base class for actions that use the current user's design
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Action
+ * @package   Laconica
+ * @author    Zach Copley <zach@controlyourself.ca>
+ * @copyright 2009 Control Yourself, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://laconi.ca/
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * Base class for actions that use a group's design
+ *
+ * Pages related to groups can be themed with a design. 
+ * This superclass returns that design.
+ *
+ * @category Action
+ * @package  Laconica
+ * @author   Zach Copley <zach@controlyourself.ca>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://laconi.ca/
+ *
+ */
+class GroupDesignAction extends Action {
+
+    /** The group in question */
+    var $group = null;
+    
+    /**
+      * Show the groups's design stylesheet
+      *
+      * @return nothing
+      */
+     function showStylesheets()
+     {
+         parent::showStylesheets();
+
+         $design = $this->getDesign();
+
+         if (!empty($design)) {
+             $design->showCSS($this);
+         }
+     }
+
+    /**
+     * A design for this action
+     *
+     * if the group attribute has been set, returns that group's
+     * design.
+     *
+     * @return Design a design object to use
+     */
+
+    function getDesign()
+    {
+
+        if (empty($this->group)) {
+            return null;
+        }
+
+        return $this->group->getDesign();
+    }
+
+}
index 194247982cc6ae7e6b25c71fd816125902d9d66e..9e530c447ce511bca2eabb1b3d5371a10c92dc03 100644 (file)
@@ -113,6 +113,12 @@ class GroupNav extends Widget
                                  sprintf(_('Add or edit %s logo'), $nickname),
                                  $action_name == 'grouplogo',
                                  'nav_group_logo');
+            $this->out->menuItem(common_local_url('groupdesignsettings', array('nickname' =>
+                                                                  $nickname)),
+                                 _('Design'),
+                                 sprintf(_('Add or edit %s design'), $nickname),
+                                 $action_name == 'groupdesignsettings',
+                                 'nav_group_design');
         }
         $this->out->elementEnd('ul');
     }
index 01bbf5721a4450426f2b53a0d731979fa76c1b02..f1f6e98c195343d9d36a4becba131b701b30531a 100644 (file)
@@ -31,8 +31,6 @@ if (!defined('LACONICA')) {
     exit(1);
 }
 
-require_once INSTALLDIR.'/lib/personal.php';
-
 define('MESSAGES_PER_PAGE', 20);
 
 /**
@@ -47,11 +45,11 @@ define('MESSAGES_PER_PAGE', 20);
  * @see      OutboxAction
  */
 
-class MailboxAction extends PersonalAction
+class MailboxAction extends CurrentUserDesignAction
 {
     var $page = null;
 
-    function prepare($args) 
+    function prepare($args)
     {
         parent::prepare($args);
 
@@ -265,12 +263,12 @@ class MailboxAction extends PersonalAction
      * Returns either the name (and link) of the API client that posted the notice,
      * or one of other other channels.
      *
-     * @param string $source the source of the message 
+     * @param string $source the source of the message
      *
      * @return void
      */
 
-    function showSource($source) 
+    function showSource($source)
     {
         $source_name = _($source);
         switch ($source) {
@@ -297,4 +295,17 @@ class MailboxAction extends PersonalAction
         return;
     }
 
+    /**
+     * Mailbox actions are read only
+     *
+     * @param array $args other arguments
+     *
+     * @return boolean
+     */
+
+    function isReadOnly($args)
+    {
+         return true;
+    }
+
 }
index c312292abacc1999a09b28ca90cede5a7296dbfb..ad792441a3794765df430698c0721a65b0737e37 100644 (file)
@@ -50,7 +50,6 @@ require_once INSTALLDIR.'/lib/attachmentlist.php';
  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
  * @link     http://laconi.ca/
  * @see      Notice
- * @see      StreamAction
  * @see      NoticeListItem
  * @see      ProfileNoticeList
  */
diff --git a/lib/ownerdesignaction.php b/lib/ownerdesignaction.php
new file mode 100644 (file)
index 0000000..424474f
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Base class for actions that use the page owner's design
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Action
+ * @package   Laconica
+ * @author    Evan Prodromou <evan@controlyourself.ca>
+ * @copyright 2009 Control Yourself, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://laconi.ca/
+ */
+
+if (!defined('LACONICA')) {
+    exit(1);
+}
+
+/**
+ * Base class for actions that use the page owner's design
+ *
+ * Some pages have a clear "owner" -- like the profile page, subscriptions
+ * pages, etc. This superclass uses that owner's chosen design for the page
+ * design.
+ *
+ * @category Action
+ * @package  Laconica
+ * @author   Evan Prodromou <evan@controlyourself.ca>
+ * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link     http://laconi.ca/
+ *
+ */
+
+class OwnerDesignAction extends Action {
+
+    /** The user for this page. */
+
+    var $user = null;
+
+    /**
+      * Show the owner's design stylesheet
+      *
+      * @return nothing
+      */
+     function showStylesheets()
+     {
+         parent::showStylesheets();
+
+         $design = $this->getDesign();
+
+         if (!empty($design)) {
+             $design->showCSS($this);
+        }
+     }
+
+    /**
+     * A design for this action
+     *
+     * if the user attribute has been set, returns that user's
+     * design.
+     *
+     * @return Design a design object to use
+     */
+
+    function getDesign()
+    {
+        if (empty($this->user)) {
+            return null;
+        }
+
+        return $this->user->getDesign();
+    }
+}
diff --git a/lib/personal.php b/lib/personal.php
deleted file mode 100644 (file)
index f927323..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-<?php
-/**
- * Laconica, the distributed open-source microblogging tool
- *
- * User profile page
- *
- * PHP version 5
- *
- * LICENCE: This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * @category  Personal
- * @package   Laconica
- * @author    Evan Prodromou <evan@controlyourself.ca>
- * @author    Sarven Capadisli <csarven@controlyourself.ca>
- * @copyright 2008-2009 Control Yourself, Inc.
- * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link      http://laconi.ca/
- */
-
-if (!defined('LACONICA')) {
-    exit(1);
-}
-
-/**
- * Base class for user profile page
- *
- * @category Personal
- * @package  Laconica
- * @author   Evan Prodromou <evan@controlyourself.ca>
- * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
- * @link     http://laconi.ca/
- */
-
-class PersonalAction extends Action
-{
-
-    var $user = null;
-
-    function isReadOnly($args)
-    {
-         return true;
-    }
-
-    function handle($args)
-    {
-        parent::handle($args);
-    }
-
-}
index 298f34b2212f3773c540dc92c485d2f713d51dcd..2519922b2b28e8292e9a4a51e270b8e6af3e8416 100644 (file)
@@ -47,9 +47,8 @@ require_once INSTALLDIR.'/lib/groupminilist.php';
  * @link     http://laconi.ca/
  */
 
-class ProfileAction extends Action
+class ProfileAction extends OwnerDesignAction
 {
-    var $user    = null;
     var $page    = null;
     var $profile = null;
     var $tag     = null;
index 8b6f636182d7e5fc16168e306ad8dd71976ad2f8..1f39c60dc50207449c6ddba1a196560d0a1a390c 100644 (file)
@@ -132,7 +132,7 @@ class Router
         // settings
 
         foreach (array('profile', 'avatar', 'password', 'openid', 'im',
-                       'email', 'sms', 'twitter', 'design', 'other') as $s) {
+                       'email', 'sms', 'twitter', 'userdesign', 'other') as $s) {
             $m->connect('settings/'.$s, array('action' => $s.'settings'));
         }
 
@@ -223,7 +223,7 @@ class Router
                         array('nickname' => '[a-zA-Z0-9]+'));
         }
 
-        foreach (array('members', 'logo', 'rss') as $n) {
+        foreach (array('members', 'logo', 'rss', 'designsettings') as $n) {
             $m->connect('group/:nickname/'.$n,
                         array('action' => 'group'.$n),
                         array('nickname' => '[a-zA-Z0-9]+'));
index db20c580436b46e0ae4bc361d65013d2234d43d2..17d3a2f64dcb1853190ee0064b6a224d01342be0 100644 (file)
@@ -43,7 +43,7 @@ if (!defined('LACONICA')) {
  * @see      Widget
  */
 
-class SettingsAction extends Action
+class SettingsAction extends CurrentUserDesignAction
 {
     /**
      * A message for the user.
diff --git a/lib/stream.php b/lib/stream.php
deleted file mode 100644 (file)
index 0cb9e0b..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-/*
- * Laconica - a distributed open-source microblogging tool
- * Copyright (C) 2008, Controlez-Vous, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.     See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program.     If not, see <http://www.gnu.org/licenses/>.
- */
-
-if (!defined('LACONICA')) { exit(1); }
-
-require_once(INSTALLDIR.'/lib/personal.php');
-require_once(INSTALLDIR.'/lib/noticelist.php');
-
-class StreamAction extends PersonalAction
-{
-    function show_notice_list($notice)
-    {
-        $nl = new NoticeList($notice);
-        return $nl->show();
-    }
-}
diff --git a/lib/webcolor.php b/lib/webcolor.php
new file mode 100644 (file)
index 0000000..f3ca6e9
--- /dev/null
@@ -0,0 +1,192 @@
+<?php
+/**
+ * Laconica, the distributed open-source microblogging tool
+ *
+ * Base class for deleting things
+ *
+ * PHP version 5
+ *
+ * LICENCE: This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Personal
+ * @package   Laconica
+ * @author    Zach Copley <zach@controlyourself.ca>
+ * @copyright 2009 Control Yourself, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link      http://laconi.ca/
+ */
+
+if (!defined('LACONICA')) {
+     exit(1);
+}
+
+class WebColor {
+
+    // XXX: Maybe make getters and setters for r,g,b values and tuples,
+    // e.g.: to support this kinda CSS representation: rgb(255,0,0)
+    // http://www.w3.org/TR/CSS21/syndata.html#color-units
+
+    var $red   = 0;
+    var $green = 0;
+    var $blue  = 0;
+
+    /**
+     * Constructor
+     *
+     * @return nothing
+     */
+
+    function __construct($color = null)
+    {
+        if (isset($color)) {
+            $this->parseColor($color);
+        }
+    }
+
+    /**
+     * Parses input to and tries to determine whether the color
+     * is being specified via an integer or hex tuple and sets
+     * the RGB instance variables accordingly.
+     *
+     * XXX: Maybe support (r,g,b) style, and array?
+     *
+     * @param mixed $color
+     *
+     * @return nothing
+     */
+
+    function parseColor($color) {
+
+        if (is_numeric($color)) {
+            $this->setIntColor($color);
+        } else {
+
+            // XXX named colors
+
+            // XXX: probably should do even more validation
+
+            if (preg_match('/(#([0-9A-Fa-f]{3,6})\b)/u', $color) > 0) {
+                $this->setHexColor($color);
+            } else {
+                $errmsg = _('%s is not a valid color!');
+                throw new WebColorException(sprintf($errmsg, $color));
+            }
+        }
+    }
+
+    /**
+     * @param string $name
+     *
+     * @return nothing
+     */
+
+    function setNamedColor($name)
+    {
+        // XXX Implement this
+    }
+
+
+    /**
+     * Sets the RGB color values from a a hex tuple
+     *
+     * @param string $hexcolor
+     *
+     * @return nothing
+     */
+
+    function setHexColor($hexcolor) {
+
+        if ($hexcolor[0] == '#') {
+            $hexcolor = substr($hexcolor, 1);
+        }
+
+        if (strlen($hexcolor) == 6) {
+            list($r, $g, $b) = array($hexcolor[0].$hexcolor[1],
+                                     $hexcolor[2].$hexcolor[3],
+                                     $hexcolor[4].$hexcolor[5]);
+        } elseif (strlen($hexcolor) == 3) {
+            list($r, $g, $b) = array($hexcolor[0].$hexcolor[0],
+                                     $hexcolor[1].$hexcolor[1],
+                                     $hexcolor[2].$hexcolor[2]);
+        } else {
+            $errmsg = _('%s is not a valid color! Use 3 or 6 hex chars.');
+            throw new WebColorException(sprintf($errmsg, $hexcolor));
+        }
+
+        $this->red   = hexdec($r);
+        $this->green = hexdec($g);
+        $this->blue  = hexdec($b);
+
+    }
+
+    /**
+     * Sets the RGB color values from a 24-bit integer
+     *
+     * @param int $intcolor
+     *
+     * @return nothing
+     */
+
+    function setIntColor($intcolor)
+    {
+        // We could do 32 bit and have an alpha channel because
+        // Sarven wants one real bad, but nah.
+
+        $this->red   = $intcolor >> 16;
+        $this->green = $intcolor >> 8 & 0xFF;
+        $this->blue  = $intcolor & 0xFF;
+
+    }
+
+    /**
+     * Returns a hex tuple of the RGB color useful for output in HTML
+     *
+     * @return string
+     */
+
+    function hexValue() {
+
+        $hexcolor  = (strlen(dechex($this->red)) < 2 ? '0' : '' ) .
+            dechex($this->red);
+        $hexcolor .= (strlen(dechex($this->green)) < 2 ? '0' : '') .
+            dechex($this->green);
+        $hexcolor .= (strlen(dechex($this->blue)) < 2 ? '0' : '') .
+            dechex($this->blue);
+
+        return strtoupper($hexcolor);
+
+    }
+
+    /**
+     * Returns a 24-bit packed integer representation of the RGB color
+     * for convenient storage in the DB
+     *
+     * XXX: probably could just use hexdec() instead
+     *
+     * @return int
+     */
+
+    function intValue()
+    {
+        $intcolor = 256 * 256 * $this->red + 256 * $this->green + $this->blue;
+        return $intcolor;
+    }
+
+}
+
+class WebColorException extends Exception
+{
+}
+
+?>
\ No newline at end of file
index dd787f5e2919dee7f87b783e5c1888fa1d95c943..daf5ada1c5a4702b966a6554bed584b5aced5b70 100644 (file)
@@ -12,9 +12,9 @@ img { display:block; border:0; }
 a abbr { cursor: pointer; border-bottom:0; }
 table { border-collapse:collapse; }
 ol { list-style-position:inside; }
-html { font-size: 87.5%; background-color:#fff; height:100%; }
+html { font-size: 87.5%; }
 body {
-background-color:#fff;
+background-color:#FFFFFF;
 color:#000;
 font-family:sans-serif;
 font-size:1em;
@@ -77,7 +77,8 @@ margin:0 0 18px 0;
 form label {
 font-weight:bold;
 }
-input.checkbox {
+input.checkbox,
+input.radio {
 position:relative;
 top:2px;
 left:0;
@@ -169,7 +170,8 @@ margin-bottom:0;
 margin-bottom:11px;
 }
 
-.form_settings input.checkbox {
+.form_settings input.checkbox,
+.form_settings input.radio {
 margin-top:3px;
 margin-left:0;
 }
@@ -181,13 +183,19 @@ margin-left:11px;
 float:left;
 width:90%;
 }
-
+.form_settings label.radio {
+margin-top:0;
+margin-right:47px;
+margin-left:11px;
+width:auto;
+}
 
 #form_login p.form_guide,
 #form_register #settings_rememberme p.form_guide,
 #form_openid_login #settings_rememberme p.form_guide,
 #settings_twitter_remove p.form_guide,
-#form_search ul.form_data #q {
+#form_search ul.form_data #q,
+#design_background-image_onoff p.form_guide {
 margin-left:0;
 }
 
index d1b0558ec80344016b04b260c88f3c115a1776c5..da200388eb57d8c704005f86123c0e7aab5f8458 100644 (file)
@@ -1,8 +1,6 @@
 /* IE specific styles */
-legend {
-margin-left:-7px;
-}
-input.checkbox {
+input.checkbox,
+input.radio {
 top:0;
 }
 #form_notice textarea {
index 935116a74fdea834b2fb934c882447d1cbda8048..7e8b84b4cc231c6b62b5585b1695bdc3373573b5 100644 (file)
@@ -9,7 +9,6 @@
 
 @import url(../../base/css/display.css);
 
-html,
 body,
 a:active {
 background-color:#C3D6DF;
@@ -245,7 +244,7 @@ div.notice-options a,
 div.notice-options input {
 font-family:sans-serif;
 }
-.notices li:hover {
+#content .notices li:hover {
 background-color:#FCFCFC;
 }
 #conversation .notices li:hover {
index 6845fda5f94d474ec27f6bb66edf912f1bf1fbd6..09ad4c9724a2f072dc540ce18cc6f686f6062f04 100644 (file)
@@ -9,7 +9,6 @@
 
 @import url(../../base/css/display.css);
 
-html,
 body,
 a:active {
 background-color:#F0F2F5;
@@ -245,7 +244,7 @@ div.notice-options a,
 div.notice-options input {
 font-family:sans-serif;
 }
-.notices li:hover {
+#content .notices li:hover {
 background-color:#FCFCFC;
 }
 #conversation .notices li:hover {
index 08427d3c847f9b61f0dfa37683d60811d7490ea2..9866e2d2cca4ae5b9e78cf01ba4ab4f5d838fbf2 100644 (file)
@@ -12,9 +12,9 @@ img { display:block; border:0; }
 a abbr { cursor: pointer; border-bottom:0; }
 table { border-collapse:collapse; }
 ol { list-style-position:inside; }
-html { font-size: 87.5%; background-color:#fff; }
+html { font-size: 87.5%; }
 body {
-background-color:#fff;
+background-color:#FFFFFF;
 color:#000;
 font-family:sans-serif;
 font-size:1em;
@@ -78,7 +78,8 @@ margin:0 0 18px 0;
 form label {
 font-weight:bold;
 }
-input.checkbox {
+input.checkbox,
+input.radio {
 position:relative;
 top:2px;
 left:0;
@@ -155,7 +156,8 @@ font-weight:bold;
 #form_invite legend,
 #form_notice_delete legend,
 #form_password_recover legend,
-#form_password_change legend {
+#form_password_change legend,
+.form_entity_block legend {
 display:none;
 }
 
@@ -181,13 +183,19 @@ margin-left:11px;
 float:left;
 width:90%;
 }
-
+.form_settings label.radio {
+margin-top:0;
+margin-right:47px;
+margin-left:11px;
+width:auto;
+}
 
 #form_login p.form_guide,
 #form_register #settings_rememberme p.form_guide,
 #form_openid_login #settings_rememberme p.form_guide,
 #settings_twitter_remove p.form_guide,
-#form_search ul.form_data #q {
+#form_search ul.form_data #q,
+#design_background-image_onoff p.form_guide {
 margin-left:0;
 }
 
@@ -375,10 +383,10 @@ margin-bottom:1em;
 }
 
 #content {
-width:50.009%;
+width:49.009%;
 min-height:259px;
 float:left;
-margin-left:18px;
+padding:0 18px;
 }
 #shownotice #content {
 min-height:0;
@@ -421,6 +429,8 @@ width:80.789%;
 height:46px;
 line-height:1.5;
 padding:7px 7px 16px 7px;
+position:relative;
+z-index:2;
 }
 #form_notice label {
 display:block;
@@ -428,8 +438,22 @@ float:left;
 font-size:1.3em;
 margin-bottom:7px;
 }
-#form_notice #notice_submit label {
-display:none;
+#form_notice label[for=notice_data-attach],
+#form_notice #notice_data-attach {
+position:absolute;
+top:25px;
+cursor:pointer;
+}
+#form_notice label[for=notice_data-attach] {
+text-indent:-9999px;
+left:394px;
+width:16px;
+height:16px;
+}
+#form_notice #notice_data-attach {
+left:183px;
+padding:0;
+height:16px;
 }
 #form_notice .form_note {
 position:absolute;
@@ -509,12 +533,15 @@ margin-bottom:4px;
 .entity_profile .entity_nickname {
 margin-left:11px;
 display:inline;
-font-weight:bold;
 }
 .entity_profile .entity_nickname {
 margin-left:0;
 }
-
+.entity_profile .fn,
+.entity_profile .nickname {
+font-size:1.1em;
+font-weight:bold;
+}
 .entity_profile .entity_fn dd:before {
 content: "(";
 font-weight:normal;
@@ -574,10 +601,13 @@ display:block;
 
 .form_user_block input.submit,
 .form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit,
 .entity_send-a-message a,
 .entity_edit a,
 .form_user_nudge input.submit,
-.entity_nudge p {
+.entity_nudge p,
+.form_make_admin input.submit {
 border:0;
 padding-left:20px;
 }
@@ -640,6 +670,7 @@ list-style-type:none;
 float:left;
 margin-right:7px;
 margin-bottom:7px;
+display:inline;
 }
 .section .entities li .photo {
 margin-right:0;
@@ -712,12 +743,17 @@ float:left;
 width:96.41%;
 border-width:1px;
 border-style:solid;
-padding:1.795%;
 margin-bottom:11px;
 }
 .notices li {
 list-style-type:none;
 }
+.notices .notices {
+margin-top:7px;
+margin-left:5%;
+width:95%;
+float:left;
+}
 
 #aside_primary .notice,
 #aside_primary .profile {
@@ -773,6 +809,9 @@ float:left;
 width:100%;
 overflow:hidden;
 }
+.notice .entry-title.ov {
+overflow:visible;
+}
 #shownotice .notice .entry-title {
 font-size:2.2em;
 }
@@ -797,7 +836,7 @@ clear:left;
 float:left;
 font-size:0.95em;
 margin-left:59px;
-width:65%;
+width:60%;
 }
 #showstream .notice div.entry-content,
 #shownotice .notice div.entry-content {
@@ -827,15 +866,12 @@ display:inline-block;
 text-transform:lowercase;
 }
 
-
 .notice-options {
-padding-left:2%;
-float:left;
-width:50%;
 position:relative;
 font-size:0.95em;
-width:12.5%;
+width:90px;
 float:right;
+margin-right:11px;
 }
 
 .notice-options a {
@@ -896,6 +932,74 @@ border:0;
 padding:0;
 }
 
+.notice .attachment {
+position:relative;
+padding-left:16px;
+}
+#attachments .attachment {
+padding-left:0;
+}
+.notice .attachment img {
+position:absolute;
+top:18px;
+left:0;
+z-index:99;
+}
+#shownotice .notice .attachment img {
+position:static;
+}
+
+#attachments {
+clear:both;
+float:left;
+width:100%;
+margin-top:18px;
+}
+#attachments dt {
+font-weight:bold;
+font-size:1.3em;
+margin-bottom:4px;
+}
+
+#attachments ol li {
+margin-bottom:18px;
+list-style-type:decimal;
+float:left;
+clear:both;
+}
+
+#jOverlayContent,
+#jOverlayContent #content,
+#jOverlayContent #content_inner {
+width: auto !important;
+margin-bottom:0;
+}
+#jOverlayContent #content {
+padding:11px;
+min-height:auto;
+}
+#jOverlayContent .external span {
+display:block;
+margin-bottom:11px;
+}
+#jOverlayContent button {
+position:absolute;
+top:0;
+right:0;
+width:29px;
+height:29px;
+text-align:center;
+font-weight:bold;
+padding:0;
+}
+#jOverlayContent h1 {
+max-width:475px;
+}
+#jOverlayContent #content {
+border-radius:7px;
+-moz-border-radius:7px;
+-webkit-border-radius:7px;
+}
 
 #usergroups #new_group {
 float: left;
@@ -1019,8 +1123,6 @@ margin-left:18px;
 }
 
 
-
-
 /* TOP_POSTERS */
 .section tbody td {
 padding-right:11px;
@@ -1140,6 +1242,18 @@ width:400px;
 margin-right:28px;
 }
 
+#settings_design_color .form_data li {
+width:33%;
+}
+#settings_design_color .form_data label {
+float:none;
+display:block;
+}
+#settings_design_color .form_data .swatch {
+padding:11px;
+margin-left:0;
+}
+
 .instructions ul {
 list-style-position:inside;
 }
index af31cf78d65c15f86eade643057722a9438ecd2c..01af500bfcb3c693bd234d5346a0dc89fc1546bd 100644 (file)
@@ -10,7 +10,7 @@
 @import url(base.css);
 
 html {
-background:#fff url(../images/illustrations/illu_pigeons-01.png) no-repeat 0 100%;
+background:url(../images/illustrations/illu_pigeons-01.png) no-repeat 0 100%;
 }
 
 body,
@@ -30,10 +30,10 @@ font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;
 }
 input, textarea, select,
 .entity_remote_subscribe {
-border-color:#aaa;
+border-color:#AAAAAA;
 }
 #filter_tags ul li {
-border-color:#ddd;
+border-color:#DDDDDD;
 }
 
 .form_settings input.form_action-primary {
@@ -50,35 +50,41 @@ background-color:#8F0000;
 input:focus, textarea:focus, select:focus,
 #form_notice.warning #notice_data-text {
 border-color:#8F0000;
+box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
+-moz-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
+-webkit-box-shadow:3px 3px 3px rgba(194, 194, 194, 0.3);
 }
 input.submit,
 .entity_remote_subscribe {
-color:#fff;
+color:#FFFFFF;
 }
 
 a,
 div.notice-options input,
 .form_user_block input.submit,
 .form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit,
 .entity_send-a-message a,
 .form_user_nudge input.submit,
 .entity_nudge p,
-.form_settings input.form_action-primary {
-color:#000;
+.form_settings input.form_action-primary,
+.form_make_admin input.submit {
+color:#000000;
 }
 
 .notice,
 .profile {
-border-color:#000;
+border-color:#000000;
 }
 .notice a,
 .profile a {
-color:#fff;
+color:#FFFFFF;
 }
 
 .notice:nth-child(3n-1),
 .profile:nth-child(3n-1) {
-border-color:#fff;
+border-color:#FFFFFF;
 }
 .notice:nth-child(3n-1) a,
 .profile:nth-child(3n-1) a {
@@ -90,7 +96,7 @@ border-color:#7F1114;
 }
 .notice:nth-child(3n) a,
 .profile:nth-child(3n) a {
-color:#000;
+color:#000000;
 }
 
 .aside .section .notice,
@@ -100,30 +106,30 @@ color:#000;
 .aside .section .notice:nth-child(3n),
 .aside .section .profile:nth-child(3n) {
 background-color:transparent;
-color:#000;
+color:#000000;
 }
 
 
 .aside .section {
-border-color:#fff;
-background-color:#fff;
-color:#000;
+border-color:#FFFFFF;
+background-color:#FFFFFF;
+color:#000000;
 }
 
 .aside .section:nth-child(n) {
-border-color:#000;
-background-color:#000;
-color:#fff;
+border-color:#000000;
+background-color:#000000;
+color:#FFFFFF;
 }
 .aside .section:nth-child(3n-1) {
-border-color:#fff;
-background-color:#fff;
-color:#000;
+border-color:#FFFFFF;
+background-color:#FFFFFF;
+color:#000000;
 }
 .aside .section:nth-child(3n) {
 background-color:#7F1114;
 border-color:#7F1114;
-color:#000;
+color:#000000;
 }
 .aside .section a {
 color:#7F1114;
@@ -132,7 +138,7 @@ color:#7F1114;
 color:#7F1114;
 }
 .aside .section:nth-child(3n) a {
-color:#fff;
+color:#FFFFFF;
 }
 
 
@@ -145,33 +151,43 @@ background:url(../images/illustrations/illu_pigeons-02.png) no-repeat 10% 100%;
 }
 
 #notice_text-count {
-color:#333;
+color:#333333;
 }
 #form_notice.warning #notice_text-count {
-color:#000;
+color:#000000;
+}
+#form_notice label[for=notice_data-attach] {
+background:transparent url(../../base/images/icons/twotone/green/clip-01.gif) no-repeat 0 45%;
+}
+#form_notice #notice_data-attach {
+opacity:0;
 }
+
 #form_notice.processing #notice_action-submit {
-background:#fff url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
+background:#FFFFFF url(../../base/images/icons/icon_processing.gif) no-repeat 47% 47%;
 cursor:wait;
 text-indent:-9999px;
 }
 
 #content,
 #site_nav_local_views a {
-border-color:#fff;
+border-color:#FFFFFF;
 }
 #site_nav_local_views .current a {
 background-color:rgba(143, 0, 0, 0.8);
-color:#fff;
+color:#FFFFFF;
 }
 
 #site_nav_local_views a {
-background-color:rgba(255, 255, 255, 0.3);
+background-color:rgba(255, 255, 255, 0.5);
 }
 #site_nav_local_views a:hover {
-background-color:#fff;
+background-color:rgba(255, 255, 255, 0.9);
 color:#8F0000;
 }
+#site_nav_local_views .current a {
+text-shadow: rgba(194,194,194,0.5) 1px 1px 1px;
+}
 
 .error {
 background-color:#F7E8E8;
@@ -181,7 +197,7 @@ background-color:#EFF3DC;
 }
 
 #anon_notice {
-color:#000;
+color:#000000;
 }
 
 
@@ -204,7 +220,10 @@ background-image:url(../../base/images/icons/icon_foaf.gif);
 .form_user_nudge input.submit,
 .form_user_block input.submit,
 .form_user_unblock input.submit,
-.entity_nudge p {
+.form_group_block input.submit,
+.form_group_unblock input.submit,
+.entity_nudge p,
+.form_make_admin input.submit {
 background-position: 0 40%;
 background-repeat: no-repeat;
 background-color:transparent;
@@ -214,7 +233,7 @@ background-color:transparent;
 .form_user_subscribe input.submit,
 .form_user_unsubscribe input.submit {
 background-color:#8F0000;
-color:#fff;
+color:#FFFFFF;
 }
 .form_user_unsubscribe input.submit,
 .form_group_leave input.submit,
@@ -233,15 +252,22 @@ background-image:url(../../base/images/icons/twotone/green/quote.gif);
 background-image:url(../../base/images/icons/twotone/green/mail.gif);
 }
 .form_user_block input.submit,
-.form_user_unblock input.submit {
+.form_user_unblock input.submit,
+.form_group_block input.submit,
+.form_group_unblock input.submit {
 background-image:url(../../base/images/icons/twotone/green/shield.gif);
 }
+.form_make_admin input.submit {
+background-image:url(../../base/images/icons/twotone/green/admin.gif);
+}
 
 /* NOTICES */
-.notices li.over {
-background-color:#fcfcfc;
+.notice .attachment {
+background:transparent url(../../base/images/icons/twotone/green/clip-02.gif) no-repeat 0 45%;
+}
+#attachments .attachment {
+background:none;
 }
-
 .notice-options .notice_reply a,
 .notice-options form input.submit {
 background-color:transparent;
@@ -263,17 +289,36 @@ background:transparent url(../../base/images/icons/twotone/green/trash.gif) no-r
 .notices div.notice-options {
 opacity:0.4;
 }
-.notices li.hover div.entry-content,
-.notices li.hover div.notice-options {
+.notices li:hover div.entry-content,
+.notices li:hover div.notice-options {
 opacity:1;
 }
 div.entry-content {
-color:#333;
+color:#333333;
 }
 div.notice-options a,
 div.notice-options input {
 font-family:sans-serif;
 }
+#content .notices li:hover {
+background-color:transparent;
+}
+#conversation .notices li:hover {
+background-color:transparent;
+}
+
+.notices .notices {
+background-color:rgba(200, 200, 200, 0.050);
+}
+.notices .notices .notices {
+background-color:rgba(200, 200, 200, 0.100);
+}
+.notices .notices .notices .notices {
+background-color:rgba(200, 200, 200, 0.150);
+}
+.notices .notices .notices .notices .notices {
+background-color:rgba(200, 200, 200, 0.300);
+}
 /*END: NOTICES */
 
 #new_group a {
@@ -283,7 +328,7 @@ background:transparent url(../../base/images/icons/twotone/green/news.gif) no-re
 .pagination .nav_prev a,
 .pagination .nav_next a {
 background-repeat:no-repeat;
-border-color:#000;
+border-color:#000000;
 }
 .pagination .nav_prev a {
 background-image:url(../../base/images/icons/twotone/green/arrow-left.gif);