diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | README | 17 | ||||
-rw-r--r-- | actions/api.php | 1 | ||||
-rw-r--r-- | actions/conversation.php | 2 | ||||
-rw-r--r-- | actions/public.php | 2 | ||||
-rw-r--r-- | actions/twitapifavorites.php | 26 | ||||
-rw-r--r-- | actions/twitapigroups.php | 31 | ||||
-rw-r--r-- | actions/twitapitags.php | 5 | ||||
-rw-r--r-- | classes/Design.php | 92 | ||||
-rw-r--r-- | classes/File.php | 5 | ||||
-rw-r--r-- | classes/Notice.php | 158 | ||||
-rw-r--r-- | classes/Session.php | 17 | ||||
-rw-r--r-- | config.php.sample | 16 | ||||
-rw-r--r-- | index.php | 2 | ||||
-rw-r--r-- | js/userdesign.go.js | 31 | ||||
-rw-r--r-- | lib/action.php | 28 | ||||
-rw-r--r-- | lib/common.php | 20 | ||||
-rw-r--r-- | lib/currentuserdesignaction.php | 37 | ||||
-rw-r--r-- | lib/designsettings.php | 10 | ||||
-rw-r--r-- | lib/groupdesignaction.php | 30 | ||||
-rw-r--r-- | lib/ownerdesignaction.php | 31 | ||||
-rw-r--r-- | lib/router.php | 15 | ||||
-rw-r--r-- | lib/twitterapi.php | 49 | ||||
-rw-r--r-- | lib/util.php | 11 | ||||
-rw-r--r-- | scripts/sessiongc.php | 36 |
25 files changed, 466 insertions, 208 deletions
diff --git a/.gitignore b/.gitignore index f4c2bba5f..5394f5eac 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,4 @@ config-*.php good-config.php lac08.log php.log -config.php.* + @@ -964,9 +964,6 @@ sslserver: use an alternate server name for SSL URLs, like shorturllength: Length of URL at which URLs in a message exceeding 140 characters will be sent to the user's chosen shortening service. -design: a default design (colors and background) for the site. - Sub-items are: backgroundcolor, contentcolor, sidebarcolor, - textcolor, linkcolor, backgroundimage, disposition. dupelimit: minimum time allowed for one person to say the same thing twice. Default 60s. Anything lower is considered a user or UI error. @@ -1432,6 +1429,20 @@ notify third-party servers of updates. notify: an array of URLs for ping endpoints. Default is the empty array (no notification). +design +------ + +Default design (colors and background) for the site. Actual appearance +depends on the theme. Null values mean to use the theme defaults. + +backgroundcolor: Hex color of the site background. +contentcolor: Hex color of the content area background. +sidebarcolor: Hex color of the sidebar background. +textcolor: Hex color of all non-link text. +linkcolor: Hex color of all links. +backgroundimage: Image to use for the background. +disposition: Flags for whether or not to tile the background image. + Plugins ======= diff --git a/actions/api.php b/actions/api.php index 8b92889f8..99ab262ad 100644 --- a/actions/api.php +++ b/actions/api.php @@ -130,6 +130,7 @@ class ApiAction extends Action 'laconica/wadl', 'tags/timeline', 'oembed/oembed', + 'groups/show', 'groups/timeline'); static $bareauth = array('statuses/user_timeline', diff --git a/actions/conversation.php b/actions/conversation.php index c8755ba6e..6b5d8d54d 100644 --- a/actions/conversation.php +++ b/actions/conversation.php @@ -167,6 +167,8 @@ class ConversationTree extends NoticeList function _buildTree() { + $cnt = 0; + $this->tree = array(); $this->table = array(); diff --git a/actions/public.php b/actions/public.php index ef9ef0d1a..d0317ac70 100644 --- a/actions/public.php +++ b/actions/public.php @@ -229,7 +229,7 @@ class PublicAction extends Action // $top->show(); $pop = new PopularNoticeSection($this); $pop->show(); - $gbp = new GroupsByPostsSection($this); + $gbp = new GroupsByMembersSection($this); $gbp->show(); $feat = new FeaturedUsersSection($this); $feat->show(); diff --git a/actions/twitapifavorites.php b/actions/twitapifavorites.php index 8256668f3..6f9361065 100644 --- a/actions/twitapifavorites.php +++ b/actions/twitapifavorites.php @@ -207,32 +207,10 @@ class TwitapifavoritesAction extends TwitterapiAction $other = User::staticGet('id', $notice->profile_id); if ($other && $other->id != $user->id) { if ($other->email && $other->emailnotifyfav) { - $this->notify_mail($other, $user, $notice); + mail_notify_fave($other, $user, $notice); } # XXX: notify by IM # XXX: notify by SMS } } - - function notify_mail($other, $user, $notice) - { - $profile = $user->getProfile(); - $bestname = $profile->getBestName(); - $subject = sprintf(_('%s added your notice as a favorite'), $bestname); - $body = sprintf(_("%1\$s just added your notice from %2\$s as one of their favorites.\n\n" . - "In case you forgot, you can see the text of your notice here:\n\n" . - "%3\$s\n\n" . - "You can see the list of %1\$s's favorites here:\n\n" . - "%4\$s\n\n" . - "Faithfully yours,\n" . - "%5\$s\n"), - $bestname, - common_exact_date($notice->created), - common_local_url('shownotice', array('notice' => $notice->id)), - common_local_url('showfavorites', array('nickname' => $user->nickname)), - common_config('site', 'name')); - - mail_to_user($other, $subject, $body); - } - -}
\ No newline at end of file +} diff --git a/actions/twitapigroups.php b/actions/twitapigroups.php index 71a0776f4..82604ebff 100644 --- a/actions/twitapigroups.php +++ b/actions/twitapigroups.php @@ -51,6 +51,32 @@ require_once INSTALLDIR.'/lib/twitterapi.php'; class TwitapigroupsAction extends TwitterapiAction { + function show($args, $apidata) + { + parent::handle($args); + + common_debug("in groups api action"); + + $this->auth_user = $apidata['user']; + $group = $this->get_group($apidata['api_arg'], $apidata); + + if (empty($group)) { + $this->clientError('Not Found', 404, $apidata['content-type']); + return; + } + + switch($apidata['content-type']) { + case 'xml': + $this->show_single_xml_group($group); + break; + case 'json': + $this->show_single_json_group($group); + break; + default: + $this->clientError(_('API method not found!'), $code = 404); + } + } + function timeline($args, $apidata) { parent::handle($args); @@ -88,8 +114,7 @@ require_once INSTALLDIR.'/lib/twitterapi.php'; $this->show_xml_timeline($notice); break; case 'rss': - $this->show_rss_timeline($notice, $title, $link, - $subtitle, $suplink); + $this->show_rss_timeline($notice, $title, $link, $subtitle); break; case 'atom': if (isset($apidata['api_arg'])) { @@ -101,7 +126,7 @@ require_once INSTALLDIR.'/lib/twitterapi.php'; 'api/laconica/groups/timeline.atom'; } $this->show_atom_timeline($notice, $title, $id, $link, - $subtitle, $suplink, $selfuri); + $subtitle, null, $selfuri); break; case 'json': $this->show_json_timeline($notice); diff --git a/actions/twitapitags.php b/actions/twitapitags.php index 5c8527530..e19e1b1ed 100644 --- a/actions/twitapitags.php +++ b/actions/twitapitags.php @@ -88,8 +88,7 @@ require_once INSTALLDIR.'/lib/twitterapi.php'; $this->show_xml_timeline($notice); break; case 'rss': - $this->show_rss_timeline($notice, $title, $link, - $subtitle, $suplink); + $this->show_rss_timeline($notice, $title, $link, $subtitle); break; case 'atom': if (isset($apidata['api_arg'])) { @@ -101,7 +100,7 @@ require_once INSTALLDIR.'/lib/twitterapi.php'; 'api/laconica/tags/timeline.atom'; } $this->show_atom_timeline($notice, $title, $id, $link, - $subtitle, $suplink, $selfuri); + $subtitle, null, $selfuri); break; case 'json': $this->show_json_timeline($notice); diff --git a/classes/Design.php b/classes/Design.php index 0927fcda7..43544f1c9 100644 --- a/classes/Design.php +++ b/classes/Design.php @@ -55,26 +55,38 @@ class Design extends Memcached_DataObject function showCSS($out) { - try { + $css = ''; - $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); + $bgcolor = Design::toWebColor($this->backgroundcolor); - } catch (WebColorException $e) { - // This shouldn't happen - common_log(LOG_ERR, "Unable to create color for design $id.", - __FILE__); + if (!empty($bgcolor)) { + $css .= 'body { background-color: #' . $bgcolor->hexValue() . ' }' . "\n"; + } + + $ccolor = Design::toWebColor($this->contentcolor); + + if (!empty($ccolor)) { + $css .= '#content, #site_nav_local_views .current a { background-color: #'; + $css .= $ccolor->hexValue() . '} '."\n"; + } + + $sbcolor = Design::toWebColor($this->sidebarcolor); + + if (!empty($sbcolor)) { + $css .= '#aside_primary { background-color: #'. $sbcolor->hexValue() . ' }' . "\n"; + } + + $tcolor = Design::toWebColor($this->textcolor); + + if (!empty($tcolor)) { + $css .= 'html body { color: #'. $tcolor->hexValue() . ' }'. "\n"; } - $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"; + $lcolor = Design::toWebColor($this->linkcolor); + + if (!empty($lcolor)) { + $css .= 'a { color: #' . $lcolor->hexValue() . ' }' . "\n"; + } if (!empty($this->backgroundimage) && $this->disposition & BACKGROUND_ON) { @@ -88,8 +100,25 @@ class Design extends Memcached_DataObject '); ' . $repeat . ' background-attachment:fixed; }' . "\n"; } - $out->element('style', array('type' => 'text/css'), $css); + if (0 != mb_strlen($css)) { + $out->element('style', array('type' => 'text/css'), $css); + } + } + + static function toWebColor($color) + { + if (is_null($color)) { + return null; + } + try { + return new WebColor($color); + } catch (WebColorException $e) { + // This shouldn't happen + common_log(LOG_ERR, "Unable to create color for design $id.", + __FILE__); + return null; + } } static function filename($id, $extension, $extra=null) @@ -152,4 +181,33 @@ class Design extends Memcached_DataObject } } + /** + * Return a design object based on the configured site design. + * + * @return Design a singleton design object for the site. + */ + + static function siteDesign() + { + static $siteDesign = null; + + if (empty($siteDesign)) { + + $siteDesign = new Design(); + + $attrs = array('backgroundcolor', + 'contentcolor', + 'sidebarcolor', + 'textcolor', + 'linkcolor', + 'backgroundimage', + 'disposition'); + + foreach ($attrs as $attr) { + $siteDesign->$attr = common_config('design', $attr); + } + } + + return $siteDesign; + } } diff --git a/classes/File.php b/classes/File.php index 0c4fbf7e6..959301eda 100644 --- a/classes/File.php +++ b/classes/File.php @@ -93,7 +93,6 @@ class File extends Memcached_DataObject if (empty($file)) { $file_redir = File_redirection::staticGet('url', $given_url); if (empty($file_redir)) { - common_debug("processNew() '$given_url' not a known redirect.\n"); $redir_data = File_redirection::where($given_url); $redir_url = $redir_data['url']; if ($redir_url === $given_url) { @@ -114,7 +113,9 @@ class File extends Memcached_DataObject if (empty($x)) { $x = File::staticGet($file_id); - if (empty($x)) die('Impossible!'); + if (empty($x)) { + throw new ServerException("Robin thinks something is impossible."); + } } File_to_post::processNew($file_id, $notice_id); diff --git a/classes/Notice.php b/classes/Notice.php index c2770edbe..ebd5e1efd 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -102,15 +102,14 @@ class Notice extends Memcached_DataObject if (!$count) { return true; } - + //turn each into their canonical tag //this is needed to remove dupes before saving e.g. #hash.tag = #hashtag $hashtags = array(); for($i=0; $i<count($match[1]); $i++) { - $hashtags[] = common_canonical_tag($match[1][$i]); + $hashtags[] = common_canonical_tag($match[1][$i]); } - /* Add them to the database */ foreach(array_unique($hashtags) as $hashtag) { /* elide characters we don't want in the tag */ @@ -183,29 +182,30 @@ class Notice extends Memcached_DataObject $notice->is_local = $is_local; } - $notice->query('BEGIN'); - - $notice->reply_to = $reply_to; if (!empty($created)) { $notice->created = $created; } else { $notice->created = common_sql_now(); } + $notice->content = $final; $notice->rendered = common_render_content($final, $notice); $notice->source = $source; $notice->uri = $uri; - if (!empty($reply_to)) { - $reply_notice = Notice::staticGet('id', $reply_to); - if (!empty($reply_notice)) { - $notice->reply_to = $reply_to; - $notice->conversation = $reply_notice->conversation; - } + $notice->reply_to = self::getReplyTo($reply_to, $profile_id, $source, $final); + + if (!empty($notice->reply_to)) { + $reply = Notice::staticGet('id', $notice->reply_to); + $notice->conversation = $reply->conversation; } if (Event::handle('StartNoticeSave', array(&$notice))) { + // XXX: some of these functions write to the DB + + $notice->query('BEGIN'); + $id = $notice->insert(); if (!$id) { @@ -213,18 +213,33 @@ class Notice extends Memcached_DataObject return _('Problem saving notice.'); } - # Update the URI after the notice is in the database - if (!$uri) { - $orig = clone($notice); + // Update ID-dependent columns: URI, conversation + + $orig = clone($notice); + + $changed = false; + + if (empty($uri)) { $notice->uri = common_notice_uri($notice); + $changed = true; + } + + // If it's not part of a conversation, it's + // the beginning of a new conversation. + if (empty($notice->conversation)) { + $notice->conversation = $notice->id; + $changed = true; + } + + if ($changed) { if (!$notice->update($orig)) { common_log_db_error($notice, 'UPDATE', __FILE__); return _('Problem saving notice.'); } } - # XXX: do we need to change this for remote users? + // XXX: do we need to change this for remote users? $notice->saveReplies(); $notice->saveTags(); @@ -232,8 +247,13 @@ class Notice extends Memcached_DataObject $notice->addToInboxes(); $notice->saveUrls(); + + // FIXME: why do we have to re-render the content? + // Remove this if it's not necessary. + $orig2 = clone($notice); - $notice->rendered = common_render_content($final, $notice); + + $notice->rendered = common_render_content($final, $notice); if (!$notice->update($orig2)) { common_log_db_error($notice, 'UPDATE', __FILE__); return _('Problem saving notice.'); @@ -290,9 +310,9 @@ class Notice extends Memcached_DataObject $notice->profile_id = $profile_id; $notice->content = $content; if (common_config('db','type') == 'pgsql') - $notice->whereAdd('extract(epoch from now() - created) < ' . common_config('site', 'dupelimit')); + $notice->whereAdd('extract(epoch from now() - created) < ' . common_config('site', 'dupelimit')); else - $notice->whereAdd('now() - created < ' . common_config('site', 'dupelimit')); + $notice->whereAdd('now() - created < ' . common_config('site', 'dupelimit')); $cnt = $notice->count(); return ($cnt == 0); @@ -906,14 +926,14 @@ class Notice extends Memcached_DataObject { $user = new User(); - if(common_config('db','quote_identifiers')) - $user_table = '"user"'; - else $user_table = 'user'; + if(common_config('db','quote_identifiers')) + $user_table = '"user"'; + else $user_table = 'user'; $qry = 'SELECT id ' . - 'FROM '. $user_table .' JOIN subscription '. - 'ON '. $user_table .'.id = subscription.subscriber ' . + 'FROM '. $user_table .' JOIN subscription '. + 'ON '. $user_table .'.id = subscription.subscriber ' . 'WHERE subscription.subscribed = %d '; $user->query(sprintf($qry, $this->profile_id)); @@ -1031,16 +1051,6 @@ class Notice extends Memcached_DataObject if (!$recipient) { continue; } - if ($i == 0 && ($recipient->id != $sender->id) && !$this->reply_to) { // Don't save reply to self - $reply_for = $recipient; - $recipient_notice = $reply_for->getCurrentNotice(); - if ($recipient_notice) { - $orig = clone($this); - $this->reply_to = $recipient_notice->id; - $this->conversation = $recipient_notice->conversation; - $this->update($orig); - } - } // Don't save replies from blocked profile to local user $recipient_user = User::staticGet('id', $recipient->id); if ($recipient_user && $recipient_user->hasBlocked($sender)) { @@ -1087,14 +1097,6 @@ class Notice extends Memcached_DataObject } } - // If it's not a reply, make it the root of a new conversation - - if (empty($this->conversation)) { - $orig = clone($this); - $this->conversation = $this->id; - $this->update($orig); - } - foreach (array_keys($replied) as $recipient) { $user = User::staticGet('id', $recipient); if ($user) { @@ -1266,4 +1268,76 @@ class Notice extends Memcached_DataObject return $ids; } + + /** + * Determine which notice, if any, a new notice is in reply to. + * + * For conversation tracking, we try to see where this notice fits + * in the tree. Rough algorithm is: + * + * if (reply_to is set and valid) { + * return reply_to; + * } else if ((source not API or Web) and (content starts with "T NAME" or "@name ")) { + * return ID of last notice by initial @name in content; + * } + * + * Note that all @nickname instances will still be used to save "reply" records, + * so the notice shows up in the mentioned users' "replies" tab. + * + * @param integer $reply_to ID passed in by Web or API + * @param integer $profile_id ID of author + * @param string $source Source tag, like 'web' or 'gwibber' + * @param string $content Final notice content + * + * @return integer ID of replied-to notice, or null for not a reply. + */ + + static function getReplyTo($reply_to, $profile_id, $source, $content) + { + static $lb = array('xmpp', 'mail', 'sms', 'omb'); + + // If $reply_to is specified, we check that it exists, and then + // return it if it does + + if (!empty($reply_to)) { + $reply_notice = Notice::staticGet('id', $reply_to); + if (!empty($reply_notice)) { + return $reply_to; + } + } + + // If it's not a "low bandwidth" source (one where you can't set + // a reply_to argument), we return. This is mostly web and API + // clients. + + if (!in_array($source, $lb)) { + return null; + } + + // Is there an initial @ or T? + + if (preg_match('/^T ([A-Z0-9]{1,64}) /', $content, $match) || + preg_match('/^@([a-z0-9]{1,64})\s+/', $content, $match)) { + $nickname = common_canonical_nickname($match[1]); + } else { + return null; + } + + // Figure out who that is. + + $sender = Profile::staticGet('id', $profile_id); + $recipient = common_relative_profile($sender, $nickname, common_sql_now()); + + if (empty($recipient)) { + return null; + } + + // Get their last notice + + $last = $recipient->getCurrentNotice(); + + if (!empty($last)) { + return $last->id; + } + } } diff --git a/classes/Session.php b/classes/Session.php index ac80279c5..5ec509f5f 100644 --- a/classes/Session.php +++ b/classes/Session.php @@ -108,11 +108,24 @@ class Session extends Memcached_DataObject $epoch = common_sql_date(time() - $maxlifetime); + $ids = array(); + $session = new Session(); $session->whereAdd('modified < "'.$epoch.'"'); - $result = $session->delete(DB_DATAOBJECT_WHEREADD_ONLY); + $session->selectAdd(); + $session->selectAdd('id'); + + $session->find(); + + while ($session->fetch()) { + $ids[] = $session->id; + } + + $session->free(); - self::logdeb("garbage collection result = $result"); + foreach ($ids as $id) { + self::destroy($id); + } } static function setSaveHandler() diff --git a/config.php.sample b/config.php.sample index 36e62f70f..c27645ff8 100644 --- a/config.php.sample +++ b/config.php.sample @@ -18,14 +18,14 @@ $config['site']['server'] = 'localhost'; $config['site']['path'] = 'laconica'; // $config['site']['fancy'] = false; // $config['site']['theme'] = 'default'; -// Sets the site's default design values (match it with the values in the theme) -// $config['site']['design']['backgroundcolor'] = '#F0F2F5'; -// $config['site']['design']['contentcolor'] = '#FFFFFF'; -// $config['site']['design']['sidebarcolor'] = '#CEE1E9'; -// $config['site']['design']['textcolor'] = '#000000'; -// $config['site']['design']['linkcolor'] = '#002E6E'; -// $config['site']['design']['backgroundimage'] = null; -// $config['site']['design']['disposition'] = 1; +// Sets the site's default design values +// $config['design']['backgroundcolor'] = '#F0F2F5'; +// $config['design']['contentcolor'] = '#FFFFFF'; +// $config['design']['sidebarcolor'] = '#CEE1E9'; +// $config['design']['textcolor'] = '#000000'; +// $config['design']['linkcolor'] = '#002E6E'; +// $config['design']['backgroundimage'] = null; +// $config['design']['disposition'] = 1; // To enable the built-in mobile style sheet, defaults to false. // $config['site']['mobile'] = true; // For contact email, defaults to $_SERVER["SERVER_ADMIN"] @@ -108,7 +108,7 @@ function checkMirror($action_obj) function main() { // quick check for fancy URL auto-detection support in installer. - if (isset($_SERVER['REDIRECT_URL']) && ((dirname($_SERVER['REQUEST_URI']) . '/check-fancy') === $_SERVER['REDIRECT_URL'])) { + if (isset($_SERVER['REDIRECT_URL']) && (preg_replace("/^\/$/","",(dirname($_SERVER['REQUEST_URI']))) . '/check-fancy') === $_SERVER['REDIRECT_URL']) { die("Fancy URL support detection succeeded. We suggest you enable this to get fancy (pretty) URLs."); } global $user, $action; diff --git a/js/userdesign.go.js b/js/userdesign.go.js index dda86294e..70dd9c7de 100644 --- a/js/userdesign.go.js +++ b/js/userdesign.go.js @@ -7,6 +7,35 @@ * @link http://laconi.ca/ */ $(document).ready(function() { + function InitColors(i, E) { + switch (parseInt(E.id.slice(-1))) { + case 1: default: + $(E).val(rgb2hex($('body').css('background-color'))); + break; + case 2: + $(E).val(rgb2hex($('#content').css('background-color'))); + break; + case 3: + $(E).val(rgb2hex($('#aside_primary').css('background-color'))); + break; + case 4: + $(E).val(rgb2hex($('html body').css('color'))); + break; + case 5: + $(E).val(rgb2hex($('a').css('color'))); + break; + } + } + + function rgb2hex(rgb) { + rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/); + function hex(x) { + hexDigits = new Array("0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F"); + return isNaN(x) ? "00" : hexDigits[(x - x % 16) / 16] + hexDigits[x % 16]; + } + return "#" + hex(rgb[1]) + hex(rgb[2]) + hex(rgb[3]); + } + function UpdateColors(S) { C = $(S).val(); switch (parseInt(S.id.slice(-1))) { @@ -55,7 +84,7 @@ $(document).ready(function() { f = $.farbtastic('#color-picker', SynchColors); swatches = $('#settings_design_color .swatch'); - + swatches.each(InitColors); swatches .each(SynchColors) .blur(function() { diff --git a/lib/action.php b/lib/action.php index 95ee10c64..a5244371a 100644 --- a/lib/action.php +++ b/lib/action.php @@ -191,6 +191,7 @@ class Action extends HTMLOutputter // lawsuit function showStylesheets() { if (Event::handle('StartShowStyles', array($this))) { + if (Event::handle('StartShowLaconicaStyles', array($this))) { $this->element('link', array('rel' => 'stylesheet', 'type' => 'text/css', @@ -209,6 +210,7 @@ class Action extends HTMLOutputter // lawsuit 'media' => 'print')); Event::handle('EndShowLaconicaStyles', array($this)); } + if (Event::handle('StartShowUAStyles', array($this))) { $this->comment('[if IE]><link rel="stylesheet" type="text/css" '. 'href="'.theme_path('css/ie.css', 'base').'?version='.LACONICA_VERSION.'" /><![endif]'); @@ -223,6 +225,21 @@ class Action extends HTMLOutputter // lawsuit 'href="'.theme_path('css/ie.css', null).'?version='.LACONICA_VERSION.'" /><![endif]'); Event::handle('EndShowUAStyles', array($this)); } + + if (Event::handle('StartShowDesign', array($this))) { + + $user = common_current_user(); + + if (empty($user) || $user->viewdesigns) { + $design = $this->getDesign(); + + if (!empty($design)) { + $design->showCSS($this); + } + } + + Event::handle('EndShowDesign', array($this)); + } Event::handle('EndShowStyles', array($this)); } } @@ -1074,4 +1091,15 @@ class Action extends HTMLOutputter // lawsuit { return null; } + + /** + * A design for this action + * + * @return Design a design object to use + */ + + function getDesign() + { + return Design::siteDesign(); + } } diff --git a/lib/common.php b/lib/common.php index 9d7954fa9..b3d301862 100644 --- a/lib/common.php +++ b/lib/common.php @@ -94,14 +94,6 @@ $config = array('name' => 'Just another Laconica microblog', 'server' => $_server, 'theme' => 'default', - 'design' => - array('backgroundcolor' => '#CEE1E9', - 'contentcolor' => '#FFFFFF', - 'sidebarcolor' => '#C8D1D5', - 'textcolor' => '#000000', - 'linkcolor' => '#002E6E', - 'backgroundimage' => null, - 'disposition' => 1), 'path' => $_path, 'logfile' => null, 'logo' => null, @@ -261,6 +253,14 @@ $config = 'sessions' => array('handle' => false, // whether to handle sessions ourselves 'debug' => false), // debugging output for sessions + 'design' => + array('backgroundcolor' => null, // null -> 'use theme default' + 'contentcolor' => null, + 'sidebarcolor' => null, + 'textcolor' => null, + 'linkcolor' => null, + 'backgroundimage' => null, + 'disposition' => null), ); $config['db'] = &PEAR::getStaticProperty('DB_DataObject','options'); @@ -277,6 +277,10 @@ $config['db'] = 'quote_identifiers' => false, 'type' => 'mysql' ); +// Backward compatibility + +$config['site']['design'] =& $config['design']; + if (function_exists('date_default_timezone_set')) { /* Work internally in UTC */ date_default_timezone_set('UTC'); diff --git a/lib/currentuserdesignaction.php b/lib/currentuserdesignaction.php index 4c7e15a8b..52516b624 100644 --- a/lib/currentuserdesignaction.php +++ b/lib/currentuserdesignaction.php @@ -47,33 +47,10 @@ if (!defined('LACONICA')) { class CurrentUserDesignAction extends Action { - - /** - * Show the user's design stylesheet - * - * @return nothing - */ - - function showStylesheets() - { - parent::showStylesheets(); - - $user = common_current_user(); - - if (empty($user) || $user->viewdesigns) { - $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. + * Returns the design preferences for the current user. * * @return Design a design object to use */ @@ -82,11 +59,15 @@ class CurrentUserDesignAction extends Action { $cur = common_current_user(); - if (empty($cur)) { - return null; + if (!empty($cur)) { + + $design = $cur->getDesign(); + + if (!empty($design)) { + return $design; + } } - return $cur->getDesign(); + return parent::getDesign(); } - } diff --git a/lib/designsettings.php b/lib/designsettings.php index fbffdb208..1b0e62166 100644 --- a/lib/designsettings.php +++ b/lib/designsettings.php @@ -182,7 +182,7 @@ class DesignSettingsAction extends AccountSettingsAction 'class' => 'swatch', 'maxlength' => '7', 'size' => '7', - 'value' => '#' . $bgcolor->hexValue())); + 'value' => '')); $this->elementEnd('li'); $ccolor = new WebColor($design->contentcolor); @@ -195,7 +195,7 @@ class DesignSettingsAction extends AccountSettingsAction 'class' => 'swatch', 'maxlength' => '7', 'size' => '7', - 'value' => '#' . $ccolor->hexValue())); + 'value' => '')); $this->elementEnd('li'); $sbcolor = new WebColor($design->sidebarcolor); @@ -208,7 +208,7 @@ class DesignSettingsAction extends AccountSettingsAction 'class' => 'swatch', 'maxlength' => '7', 'size' => '7', - 'value' => '#' . $sbcolor->hexValue())); + 'value' => '')); $this->elementEnd('li'); $tcolor = new WebColor($design->textcolor); @@ -221,7 +221,7 @@ class DesignSettingsAction extends AccountSettingsAction 'class' => 'swatch', 'maxlength' => '7', 'size' => '7', - 'value' => '#' . $tcolor->hexValue())); + 'value' => '')); $this->elementEnd('li'); $lcolor = new WebColor($design->linkcolor); @@ -234,7 +234,7 @@ class DesignSettingsAction extends AccountSettingsAction 'class' => 'swatch', 'maxlength' => '7', 'size' => '7', - 'value' => '#' . $lcolor->hexValue())); + 'value' => '')); $this->elementEnd('li'); } catch (WebColorException $e) { diff --git a/lib/groupdesignaction.php b/lib/groupdesignaction.php index 58777c283..c7cdff1fe 100644 --- a/lib/groupdesignaction.php +++ b/lib/groupdesignaction.php @@ -50,26 +50,6 @@ class GroupDesignAction extends Action { var $group = null; /** - * Show the groups's design stylesheet - * - * @return nothing - */ - function showStylesheets() - { - parent::showStylesheets(); - - $user = common_current_user(); - - if (empty($user) || $user->viewdesigns) { - $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 @@ -80,10 +60,12 @@ class GroupDesignAction extends Action { function getDesign() { - if (empty($this->group)) { - return null; + if (!empty($this->group)) { + $design = $this->group->getDesign(); + if (!empty($design)) { + return $design; + } } - - return $this->group->getDesign(); + return parent::getDesign(); } } diff --git a/lib/ownerdesignaction.php b/lib/ownerdesignaction.php index 785b8a93d..b42df926d 100644 --- a/lib/ownerdesignaction.php +++ b/lib/ownerdesignaction.php @@ -53,26 +53,6 @@ class OwnerDesignAction extends Action { var $user = null; /** - * Show the owner's design stylesheet - * - * @return nothing - */ - function showStylesheets() - { - parent::showStylesheets(); - - $user = common_current_user(); - - if (empty($user) || $user->viewdesigns) { - $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 @@ -83,10 +63,15 @@ class OwnerDesignAction extends Action { function getDesign() { - if (empty($this->user)) { - return null; + if (!empty($this->user)) { + + $design = $this->user->getDesign(); + + if (!empty($design)) { + return $design; + } } - return $this->user->getDesign(); + return parent::getDesign(); } } diff --git a/lib/router.php b/lib/router.php index e10d484f4..19839b997 100644 --- a/lib/router.php +++ b/lib/router.php @@ -113,6 +113,16 @@ class Router $m->connect('main/tagother/:id', array('action' => 'tagother')); + $m->connect('main/oembed.xml', + array('action' => 'api', + 'method' => 'oembed.xml', + 'apiaction' => 'oembed')); + + $m->connect('main/oembed.json', + array('action' => 'api', + 'method' => 'oembed.json', + 'apiaction' => 'oembed')); + // these take a code foreach (array('register', 'confirmaddress', 'recoverpassword') as $c) { @@ -475,11 +485,6 @@ class Router Event::handle('RouterInitialized', array($m)); - $m->connect('main/:method', - array('action' => 'api', - 'method' => 'oembed(.xml|.json)?', - 'apiaction' => 'oembed')); - return $m; } diff --git a/lib/twitterapi.php b/lib/twitterapi.php index b2602e77c..4115d9dcb 100644 --- a/lib/twitterapi.php +++ b/lib/twitterapi.php @@ -213,6 +213,26 @@ class TwitterapiAction extends Action return $twitter_status; } + function twitter_group_array($group) + { + $twitter_group=array(); + $twitter_group['id']=$group->id; + $twitter_group['url']=$group->permalink(); + $twitter_group['nickname']=$group->nickname; + $twitter_group['fullname']=$group->fullname; + $twitter_group['homepage_url']=$group->homepage_url; + $twitter_group['original_logo']=$group->original_logo; + $twitter_group['homepage_logo']=$group->homepage_logo; + $twitter_group['stream_logo']=$group->stream_logo; + $twitter_group['mini_logo']=$group->mini_logo; + $twitter_group['homepage']=$group->homepage; + $twitter_group['description']=$group->description; + $twitter_group['location']=$group->location; + $twitter_group['created']=$this->date_twitter($group->created); + $twitter_group['modified']=$this->date_twitter($group->modified); + return $twitter_group; + } + function twitter_rss_entry_array($notice) { $profile = $notice->getProfile(); @@ -413,6 +433,15 @@ class TwitterapiAction extends Action $this->elementEnd('status'); } + function show_twitter_xml_group($twitter_group) + { + $this->elementStart('group'); + foreach($twitter_group as $element => $value) { + $this->element($element, null, $value); + } + $this->elementEnd('group'); + } + function show_twitter_xml_user($twitter_user, $role='user') { $this->elementStart($role); @@ -450,12 +479,12 @@ class TwitterapiAction extends Action $this->element('link', null, $entry['link']); # RSS only supports 1 enclosure per item - if($entry['enclosures']){ + if(array_key_exists('enclosures', $entry) and !empty($entry['enclosures'])){ $enclosure = $entry['enclosures'][0]; $this->element('enclosure', array('url'=>$enclosure['url'],'type'=>$enclosure['mimetype'],'length'=>$enclosure['size']), null); } - if($entry['tags']){ + if(array_key_exists('tags', $entry)){ foreach($entry['tags'] as $tag){ $this->element('category', null,$tag); } @@ -639,6 +668,22 @@ class TwitterapiAction extends Action $this->end_document('json'); } + function show_single_json_group($group) + { + $this->init_document('json'); + $twitter_group = $this->twitter_group_array($group); + $this->show_json_objects($twitter_group); + $this->end_document('json'); + } + + function show_single_xml_group($group) + { + $this->init_document('xml'); + $twitter_group = $this->twitter_group_array($group); + $this->show_twitter_xml_group($twitter_group); + $this->end_document('xml'); + } + // Anyone know what date format this is? // Twitter's dates look like this: "Mon Jul 14 23:52:38 +0000 2008" -- Zach function date_twitter($dt) diff --git a/lib/util.php b/lib/util.php index d784bb793..c8e318efe 100644 --- a/lib/util.php +++ b/lib/util.php @@ -140,7 +140,7 @@ function common_have_session() function common_ensure_session() { $c = null; - if (array_key_exists(session_name, $_COOKIE)) { + if (array_key_exists(session_name(), $_COOKIE)) { $c = $_COOKIE[session_name()]; } if (!common_have_session()) { @@ -1410,20 +1410,21 @@ function common_client_ip() return null; } - if ($_SERVER['HTTP_X_FORWARDED_FOR']) { - if ($_SERVER['HTTP_CLIENT_IP']) { + if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)) { + if (array_key_exists('HTTP_CLIENT_IP', $_SERVER)) { $proxy = $_SERVER['HTTP_CLIENT_IP']; } else { $proxy = $_SERVER['REMOTE_ADDR']; } $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; } else { - if ($_SERVER['HTTP_CLIENT_IP']) { + $proxy = null; + if (array_key_exists('HTTP_CLIENT_IP', $_SERVER)) { $ip = $_SERVER['HTTP_CLIENT_IP']; } else { $ip = $_SERVER['REMOTE_ADDR']; } } - return array($ip, $proxy); + return array($proxy, $ip); } diff --git a/scripts/sessiongc.php b/scripts/sessiongc.php new file mode 100644 index 000000000..314b641eb --- /dev/null +++ b/scripts/sessiongc.php @@ -0,0 +1,36 @@ +#!/usr/bin/env php +<?php +/* + * Laconica - a distributed open-source microblogging tool + * Copyright (C) 2008, 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/>. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); + +$helptext = <<<END_OF_GC_HELP +sessiongc.php + +Delete old sessions from the server + +END_OF_GC_HELP; + +require_once INSTALLDIR.'/scripts/commandline.inc'; + +$maxlifetime = ini_get('session.gc_maxlifetime'); + +print "Deleting sessions older than $maxlifetime seconds.\n"; + +Session::gc($maxlifetime); |