diff options
author | Zach Copley <zach@status.net> | 2009-09-24 18:18:26 -0700 |
---|---|---|
committer | Zach Copley <zach@status.net> | 2009-09-24 18:18:26 -0700 |
commit | 150cf8c045e8e091a70440d1b833fa19aaf31419 (patch) | |
tree | 26aa6f09e89cf1176785f6bcb9b38b4c61d4a7cd | |
parent | ed9ba9d945e4f50812022a7489fc8135f4e49846 (diff) | |
parent | b617c608ea0d66451eb2dcd75e1e1c58c179d8e6 (diff) |
Merge branch '0.9.x' into refactor-api
* 0.9.x: (88 commits)
Left a couple debugging statements in (removed)
Output If-Modified-Since header for all RSS 1.0 feeds (again)
Revert "move scripts to just before </body>, add event for scripts that need to be in <head>"
Implemented join and leave groups api methods
implemented etag and last modified
Fixed broken Piwik plugin - was not using the supplied site code
move scripts to just before </body>, add event for scripts that need to be in <head>
some UI fixes
Using timeline string instead of title for WindowName because IE doesn't
Added JavaScript to initialize the poped Window
Some layout and rendering adjustment for Realtime plugin
Created addPop() for Realtime plugin and added param to include iconurl
move some stuff around for realtime
hack around address hack in util.js
Add some more realtime feeds
Do realtime popup with PHP instead of Javascript
JavaScript fixes for IE
Revert "Added realtime streams for all and showstream timelines"
Revert "Fixed indenting"
Revert "Made it slighly more compact with less jQuery selection"
...
46 files changed, 785 insertions, 219 deletions
diff --git a/EVENTS.txt b/EVENTS.txt index 2b16d43c0..fa25aabcd 100644 --- a/EVENTS.txt +++ b/EVENTS.txt @@ -170,12 +170,6 @@ StartShowBody: called before showing the <body> element and children EndShowBody: called after showing the <body> element (and </body>) - $action: action object being shown -StartHeadChildren: called before showing the children of <head> element (after <head> tag) -- $action: action object being shown - -EndHeadChildren: called after showing the children of <head> element (before </head>) -- $action: action object being shown - StartPersonalGroupNav: beginning of personal group nav menu - $action: action object being shown @@ -271,3 +265,9 @@ GetValidDaemons: Just before determining which daemons to run HandleQueuedNotice: Handle a queued notice at queue time (or immediately if no queue) - &$notice: notice to handle + +StartShowHeadElements: Right after the <head> tag +- $action: the current action + +EndShowHeadElements: Right before the </head> tag; put <script>s here if you need them in <head> +- $action: the current action diff --git a/actions/allrss.php b/actions/allrss.php index 57efb73f0..28b1be27d 100644 --- a/actions/allrss.php +++ b/actions/allrss.php @@ -68,6 +68,7 @@ class AllrssAction extends Rss10Action $this->clientError(_('No such user.')); return false; } else { + $this->notices = $this->getNotices($this->limit); return true; } } diff --git a/actions/avatarsettings.php b/actions/avatarsettings.php index 02a684b38..ded419dd7 100644 --- a/actions/avatarsettings.php +++ b/actions/avatarsettings.php @@ -362,13 +362,13 @@ class AvatarsettingsAction extends AccountSettingsAction $profile = $user->getProfile(); $avatar = $profile->getOriginalAvatar(); - $avatar->delete(); + if($avatar) $avatar->delete(); $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); - $avatar->delete(); + if($avatar) $avatar->delete(); $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE); - $avatar->delete(); + if($avatar) $avatar->delete(); $avatar = $profile->getAvatar(AVATAR_MINI_SIZE); - $avatar->delete(); + if($avatar) $avatar->delete(); $this->showForm(_('Avatar deleted.'), true); } diff --git a/actions/favoritesrss.php b/actions/favoritesrss.php index 2d5ce9854..62f06e841 100644 --- a/actions/favoritesrss.php +++ b/actions/favoritesrss.php @@ -50,11 +50,11 @@ require_once INSTALLDIR.'/lib/rssaction.php'; */ class FavoritesrssAction extends Rss10Action { - + /** The user whose favorites to display */ - + var $user = null; - + /** * Find the user to display by supplied nickname * @@ -66,7 +66,7 @@ class FavoritesrssAction extends Rss10Action function prepare($args) { parent::prepare($args); - + $nickname = $this->trimmed('nickname'); $this->user = User::staticGet('nickname', $nickname); @@ -74,10 +74,11 @@ class FavoritesrssAction extends Rss10Action $this->clientError(_('No such user.')); return false; } else { + $this->notices = $this->getNotices($this->limit); return true; } } - + /** * Get notices * diff --git a/actions/grouprss.php b/actions/grouprss.php index 70c1ded48..6a6b55e78 100644 --- a/actions/grouprss.php +++ b/actions/grouprss.php @@ -104,6 +104,7 @@ class groupRssAction extends Rss10Action return false; } + $this->notices = $this->getNotices($this->limit); return true; } diff --git a/actions/newnotice.php b/actions/newnotice.php index 4c6372c2b..23ec2a1b5 100644 --- a/actions/newnotice.php +++ b/actions/newnotice.php @@ -433,13 +433,14 @@ class NewnoticeAction extends Action $content = $this->trimmed('status_textarea'); if (!$content) { $replyto = $this->trimmed('replyto'); + $inreplyto = $this->trimmed('inreplyto'); $profile = Profile::staticGet('nickname', $replyto); if ($profile) { $content = '@' . $profile->nickname . ' '; } } - $notice_form = new NoticeForm($this, '', $content); + $notice_form = new NoticeForm($this, '', $content, null, $inreplyto); $notice_form->show(); } diff --git a/actions/publicrss.php b/actions/publicrss.php index 593888b9f..0c5d061cb 100644 --- a/actions/publicrss.php +++ b/actions/publicrss.php @@ -50,8 +50,22 @@ require_once INSTALLDIR.'/lib/rssaction.php'; class PublicrssAction extends Rss10Action { /** + * Read arguments and initialize members + * + * @param array $args Arguments from $_REQUEST + * @return boolean success + */ + + function prepare($args) + { + parent::prepare($args); + $this->notices = $this->getNotices($this->limit); + return true; + } + + /** * Initialization. - * + * * @return boolean true */ function init() @@ -73,7 +87,7 @@ class PublicrssAction extends Rss10Action while ($notice->fetch()) { $notices[] = clone($notice); } - + return $notices; } diff --git a/actions/repliesrss.php b/actions/repliesrss.php index c71c9226f..76aae21ad 100644 --- a/actions/repliesrss.php +++ b/actions/repliesrss.php @@ -38,6 +38,7 @@ class RepliesrssAction extends Rss10Action $this->clientError(_('No such user.')); return false; } else { + $this->notices = $this->getNotices($this->limit); return true; } } diff --git a/actions/showstream.php b/actions/showstream.php index 2e9679fae..cdac4f47b 100644 --- a/actions/showstream.php +++ b/actions/showstream.php @@ -378,8 +378,13 @@ class ShowstreamAction extends ProfileAction $this->showEmptyListMessage(); } + $args = array('nickname' => $this->user->nickname); + if (!empty($this->tag)) + { + $args['tag'] = $this->tag; + } $this->pagination($this->page>1, $cnt>NOTICES_PER_PAGE, $this->page, - 'showstream', array('nickname' => $this->user->nickname)); + 'showstream', $args); } function showAnonymousMessage() diff --git a/actions/twitapigroups.php b/actions/twitapigroups.php index 4deb1b764..493144e77 100644 --- a/actions/twitapigroups.php +++ b/actions/twitapigroups.php @@ -293,6 +293,105 @@ require_once INSTALLDIR.'/lib/twitterapi.php'; } } + function join($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 false; + } + + if($this->auth_user->isMember($group)){ + $this->clientError(_('You are already a member of that group'), $code = 403); + return false; + } + + if (Group_block::isBlocked($group, $this->auth_user->getProfile())) { + $this->clientError(_('You have been blocked from that group by the admin.'), 403); + return false; + } + + $member = new Group_member(); + + $member->group_id = $group->id; + $member->profile_id = $this->auth_user->id; + $member->created = common_sql_now(); + + $result = $member->insert(); + + if (!$result) { + common_log_db_error($member, 'INSERT', __FILE__); + $this->serverError(sprintf(_('Could not join user %s to group %s'), + $this->auth_user->nickname, $group->nickname)); + } + + 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 leave($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 false; + } + + if(! $this->auth_user->isMember($group)){ + $this->clientError(_('You are not a member of that group'), $code = 403); + return false; + } + + $member = new Group_member(); + + $member->group_id = $group->id; + $member->profile_id = $this->auth_user->id; + + if (!$member->find(true)) { + $this->serverError(_('Could not find membership record.')); + return; + } + + $result = $member->delete(); + + if (!$result) { + common_log_db_error($member, 'INSERT', __FILE__); + $this->serverError(sprintf(_('Could not remove user %s to group %s'), + $this->auth_user->nickname, $group->nickname)); + } + + 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 is_member($args, $apidata) { parent::handle($args); @@ -326,4 +425,29 @@ require_once INSTALLDIR.'/lib/twitterapi.php'; $this->clientError(_('API method not found!'), $code = 404); } } + + function create($args, $apidata) + { + die("todo"); + } + + function update($args, $apidata) + { + die("todo"); + } + + function update_group_logo($args, $apidata) + { + die("todo"); + } + + function destroy($args, $apidata) + { + die("todo"); + } + + function tag($args, $apidata) + { + die("todo"); + } } diff --git a/actions/userrss.php b/actions/userrss.php index fa6d588cd..19e610551 100644 --- a/actions/userrss.php +++ b/actions/userrss.php @@ -25,7 +25,6 @@ require_once(INSTALLDIR.'/lib/rssaction.php'); class UserrssAction extends Rss10Action { - var $user = null; var $tag = null; function prepare($args) @@ -39,6 +38,7 @@ class UserrssAction extends Rss10Action $this->clientError(_('No such user.')); return false; } else { + $this->notices = $this->getNotices($this->limit); return true; } } @@ -64,9 +64,8 @@ class UserrssAction extends Rss10Action function getNotices($limit=0) { - $user = $this->user; - + if (is_null($user)) { return null; } diff --git a/classes/Notice.php b/classes/Notice.php index 2138e0561..f3fa9af78 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -909,7 +909,8 @@ class Notice extends Memcached_DataObject $qry .= '('.$id.', '.$this->id.', '.$source.", '".$this->created. "') "; $cnt++; if (rand() % NOTICE_INBOX_SOFT_LIMIT == 0) { - Notice_inbox::gc($id); + // FIXME: Causes lag in replicated servers + // Notice_inbox::gc($id); } if ($cnt >= MAX_BOXCARS) { $inbox = new Notice_inbox(); diff --git a/classes/User.php b/classes/User.php index 11cb4f08b..5e74c7fde 100644 --- a/classes/User.php +++ b/classes/User.php @@ -117,11 +117,15 @@ class User extends Memcached_DataObject function allowed_nickname($nickname) { // XXX: should already be validated for size, content, etc. - static $blacklist = array('rss', 'xrds', 'doc', 'main', - 'settings', 'notice', 'user', - 'search', 'avatar', 'tag', 'tags', - 'api', 'message', 'group', 'groups', - 'local'); + + $blacklist = array(); + + //all directory and file names should be blacklisted + $d = dir(INSTALLDIR); + while (false !== ($entry = $d->read())) { + $blacklist[]=$entry; + } + $d->close(); $merged = array_merge($blacklist, common_config('nickname', 'blacklist')); return !in_array($nickname, $merged); } diff --git a/db/sms_carrier.sql b/db/sms_carrier.sql index 055606f58..0e94df296 100644 --- a/db/sms_carrier.sql +++ b/db/sms_carrier.sql @@ -61,4 +61,5 @@ VALUES (100113, 'T-Mobile Germany', '%s@t-mobile-sms.de', now()), (100114, 'Vodafone Germany', '%s@vodafone-sms.de', now()), (100115, 'E-Plus', '%s@smsmail.eplus.de', now()), - (100116, 'Cellular South', '%s@csouth1.com', now()); + (100116, 'Cellular South', '%s@csouth1.com', now()), + (100117, 'ChinaMobile (139)', '%s@139.com', now()); diff --git a/doc-src/bookmarklet b/doc-src/bookmarklet index 6cd2c08f9..e5ded7702 100644 --- a/doc-src/bookmarklet +++ b/doc-src/bookmarklet @@ -2,6 +2,4 @@ A bookmarklet is a small piece of javascript code used as a bookmark. This one w Drag-and-drop the following link to your bookmarks bar or right-click it and add it to your browser favorites to keep it handy. -<MTMarkdownOptions output='raw'> -<a href="javascript:var%20d=document,w=window,e=w.getSelection,k=d.getSelection,x=d.selection,s=(e?e():(k)?k():(x?x.createRange().text:0)),f='http://%%site.server%%/%%site.path%%/index.php?action=newnotice',l=d.location,e=encodeURIComponent,g=f+'&status_textarea=%22'+((e(s))?e(s):e(document.title))+'%22 from '+l.href;function%20a(){if(!w.open(g,'t','toolbar=0,resizable=0,scrollbars=1,status=1,width=800,height=570')){l.href=g;}}a();void(0);">Post to %%site.name%%</a> -</MTMarkdownOptions> +<a href="javascript:var%20d=document,w=window,e=w.getSelection,k=d.getSelection,x=d.selection,s=(e?e():(k)?k():(x?x.createRange().text:0)),f='http://%%site.server%%/%%site.path%%/index.php?action=newnotice',l=d.location,e=encodeURIComponent,g=f+'&status_textarea=%22'+((e(s))?e(s):e(document.title))+'%22 from '+l.href;function%20a(){if(!w.open(g,'t','toolbar=0,resizable=0,scrollbars=1,status=1,width=800,height=570')){l.href=g;}}a();void(0);">Post to %%site.name%%</a> diff --git a/doc-src/im b/doc-src/im index c722a4e2c..631f6d9bb 100644 --- a/doc-src/im +++ b/doc-src/im @@ -37,10 +37,10 @@ currently-implemented commands: * **help**: Show this help. List available Jabber/XMPP commands * **follow <nickname>**: Subscribe to <nickname> * **sub <nickname>**: Same as follow -* **leave <nickname>**: Subscribe to <nickname> +* **leave <nickname>**: Unsubscribe from <nickname> * **unsub <nickname>**: Same as leave * **d <nickname> <text>**: Send direct message to <nickname> with message body <text> * **get <nickname>**: Get last notice from <nickname> * **last <nickname>**: Same as 'get' * **whois <nickname>**: Get Profile info on <nickname> -* **fav <nickname>**: Add user's last notice as a favorite
\ No newline at end of file +* **fav <nickname>**: Add user's last notice as a favorite diff --git a/extlib/Auth/OpenID/BigMath.php b/extlib/Auth/OpenID/BigMath.php index 45104947d..b5fc627a0 100644 --- a/extlib/Auth/OpenID/BigMath.php +++ b/extlib/Auth/OpenID/BigMath.php @@ -376,7 +376,7 @@ function Auth_OpenID_detectMathLibrary($exts) // Try to load dynamic modules. if (!$loaded) { foreach ($extension['modules'] as $module) { - if (@dl($module . "." . PHP_SHLIB_SUFFIX)) { + if (function_exists('dl') && ini_get('enable_dl') && !ini_get('safe_mode') && @dl($module . "." . PHP_SHLIB_SUFFIX)) { $loaded = true; break; } diff --git a/extlib/Auth/Yadis/XML.php b/extlib/Auth/Yadis/XML.php index 4854f12bb..7232d6cbd 100644 --- a/extlib/Auth/Yadis/XML.php +++ b/extlib/Auth/Yadis/XML.php @@ -349,7 +349,7 @@ function &Auth_Yadis_getXMLParser() foreach ($extensions as $name => $params) { if (!extension_loaded($name)) { foreach ($params['libname'] as $libname) { - if (@dl($libname)) { + if (function_exists('dl') && ini_get('enable_dl') && !ini_get('safe_mode') && @dl($libname)) { $classname = $params['classname']; } } diff --git a/extlib/OAuth.php b/extlib/OAuth.php index fd4853554..648627b57 100644 --- a/extlib/OAuth.php +++ b/extlib/OAuth.php @@ -327,7 +327,7 @@ class OAuthRequest {/*{{{*/ public function get_normalized_http_url() {/*{{{*/ $parts = parse_url($this->http_url); - $port = @$parts['port']; + $port = isset($parts['port']) ? $parts['port'] : null; $scheme = $parts['scheme']; $host = $parts['host']; $path = @$parts['path']; diff --git a/extlib/PEAR.php b/extlib/PEAR.php index 4c24c6006..fcefa964a 100644 --- a/extlib/PEAR.php +++ b/extlib/PEAR.php @@ -746,7 +746,7 @@ class PEAR { if (!extension_loaded($ext)) { // if either returns true dl() will produce a FATAL error, stop that - if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) { + if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1) || !function_exists('dl')) { return false; } if (OS_WINDOWS) { @@ -49,7 +49,13 @@ function getPath($req) ) { return $req['p']; } else if (array_key_exists('PATH_INFO', $_SERVER)) { - return $_SERVER['PATH_INFO']; + $path = $_SERVER['PATH_INFO']; + $script = $_SERVER['SCRIPT_NAME']; + if (substr($path, 0, mb_strlen($script)) == $script) { + return substr($path, mb_strlen($script)); + } else { + return $path; + } } else { return null; } diff --git a/install.php b/install.php index 46248c789..c2a5bb29e 100644 --- a/install.php +++ b/install.php @@ -244,7 +244,7 @@ function main() */ function haveExternalLibrary($external_library) { - if (isset($external_library['include']) && ! @include_once $external_library['include'] ) { + if (isset($external_library['include']) && !haveIncludeFile($external_library['include'])) { return false; } if (isset($external_library['check_function']) && ! function_exists($external_library['check_function'])) { @@ -256,6 +256,15 @@ function haveExternalLibrary($external_library) return true; } +// Attempt to include a PHP file and report if it worked, while +// suppressing the annoying warning messages on failure. +function haveIncludeFile($filename) { + $old = error_reporting(error_reporting() & ~E_WARNING); + $ok = include_once($filename); + error_reporting($old); + return $ok; +} + /** * Check if all is ready for installation * @@ -328,12 +337,19 @@ function checkPrereqs() */ function checkExtension($name) { - if (!extension_loaded($name)) { - if (!@dl($name.'.so')) { - return false; - } + if (extension_loaded($name)) { + return true; + } elseif (function_exists('dl') && ini_get('enable_dl') && !ini_get('safe_mode')) { + // dl will throw a fatal error if it's disabled or we're in safe mode. + // More fun, it may not even exist under some SAPIs in 5.3.0 or later... + $soname = $name . '.' . PHP_SHLIB_SUFFIX; + if (PHP_SHLIB_SUFFIX == 'dll') { + $soname = "php_" . $soname; + } + return @dl($soname); + } else { + return false; } - return true; } /** @@ -390,7 +406,7 @@ E_O_T; E_O_T; foreach ($present_libraries as $library) { echo '<li>'; - if ($library['url']) { + if (isset($library['url'])) { echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>'; } else { echo htmlentities($library['name']); diff --git a/lib/action.php b/lib/action.php index 8056cb9ec..02793f069 100644 --- a/lib/action.php +++ b/lib/action.php @@ -120,17 +120,14 @@ class Action extends HTMLOutputter // lawsuit { // XXX: attributes (profile?) $this->elementStart('head'); - if (Event::handle('StartHeadChildren', array($this))) { - $this->showTitle(); - $this->showShortcutIcon(); - $this->showStylesheets(); - $this->showScripts(); - $this->showOpenSearch(); - $this->showFeeds(); - $this->showDescription(); - $this->extraHead(); - Event::handle('EndHeadChildren', array($this)); - } + $this->showTitle(); + $this->showShortcutIcon(); + $this->showStylesheets(); + $this->showScripts(); + $this->showOpenSearch(); + $this->showFeeds(); + $this->showDescription(); + $this->extraHead(); $this->elementEnd('head'); } @@ -879,6 +876,7 @@ class Action extends HTMLOutputter // lawsuit */ function handle($argarray=null) { + header('Vary: Accept-Encoding,Cookie'); $lm = $this->lastModified(); $etag = $this->etag(); if ($etag) { diff --git a/lib/designsettings.php b/lib/designsettings.php index fdc05562e..820d534f2 100644 --- a/lib/designsettings.php +++ b/lib/designsettings.php @@ -325,7 +325,6 @@ class DesignSettingsAction extends AccountSettingsAction parent::showScripts(); $this->script('js/farbtastic/farbtastic.js'); - $this->script('js/farbtastic/farbtastic.go.js'); $this->script('js/userdesign.go.js'); $this->autofocus('design_background-image_file'); diff --git a/lib/mail.php b/lib/mail.php index df585406c..5bf4d7425 100644 --- a/lib/mail.php +++ b/lib/mail.php @@ -551,9 +551,9 @@ function mail_notify_fave($other, $user, $notice) common_init_locale($other->language); - $subject = sprintf(_('%s added your notice as a favorite'), $bestname); + $subject = sprintf(_('%s (@%s) added your notice as a favorite'), $bestname, $user->nickname); - $body = sprintf(_("%1\$s just added your notice from %2\$s". + $body = sprintf(_("%1\$s (@%7\$s) just added your notice from %2\$s". " as one of their favorites.\n\n" . "The URL of your notice is:\n\n" . "%3\$s\n\n" . @@ -570,7 +570,8 @@ function mail_notify_fave($other, $user, $notice) $notice->content, common_local_url('showfavorites', array('nickname' => $user->nickname)), - common_config('site', 'name')); + common_config('site', 'name'), + $user->nickname); common_init_locale(); mail_to_user($other, $subject, $body); @@ -607,9 +608,9 @@ function mail_notify_attn($user, $notice) $conversationUrl = null; } - $subject = sprintf(_('%s sent a notice to your attention'), $bestname); + $subject = sprintf(_('%s (@%s) sent a notice to your attention'), $bestname, $sender->nickname); - $body = sprintf(_("%1\$s just sent a notice to your attention (an '@-reply') on %2\$s.\n\n". + $body = sprintf(_("%1\$s (@%9\$s) just sent a notice to your attention (an '@-reply') on %2\$s.\n\n". "The notice is here:\n\n". "\t%3\$s\n\n" . "It reads:\n\n". @@ -629,10 +630,11 @@ function mail_notify_attn($user, $notice) $notice->content,//%4 $conversationUrl,//%5 common_local_url('newnotice', - array('replyto' => $sender->nickname)),//%6 + array('replyto' => $sender->nickname, 'inreplyto' => $notice->id)),//%6 common_local_url('replies', array('nickname' => $user->nickname)),//%7 - common_local_url('emailsettings'));//%8 + common_local_url('emailsettings'), //%8 + $sender->nickname); //%9 common_init_locale(); mail_to_user($user, $subject, $body); diff --git a/lib/noticeform.php b/lib/noticeform.php index cee46709e..186330bf1 100644 --- a/lib/noticeform.php +++ b/lib/noticeform.php @@ -70,6 +70,12 @@ class NoticeForm extends Form var $user = null; /** + * The notice being replied to + */ + + var $inreplyto = null; + + /** * Constructor * * @param HTMLOutputter $out output channel @@ -77,12 +83,13 @@ class NoticeForm extends Form * @param string $content content to pre-fill */ - function __construct($out=null, $action=null, $content=null, $user=null) + function __construct($out=null, $action=null, $content=null, $user=null, $inreplyto=null) { parent::__construct($out); $this->action = $action; $this->content = $content; + $this->inreplyto = $inreplyto; if ($user) { $this->user = $user; @@ -168,7 +175,7 @@ class NoticeForm extends Form if ($this->action) { $this->out->hidden('notice_return-to', $this->action, 'returnto'); } - $this->out->hidden('notice_in-reply-to', $this->action, 'inreplyto'); + $this->out->hidden('notice_in-reply-to', $this->inreplyto, 'inreplyto'); } /** diff --git a/lib/noticelist.php b/lib/noticelist.php index ec85e4a5c..d4cd3ff6e 100644 --- a/lib/noticelist.php +++ b/lib/noticelist.php @@ -261,7 +261,7 @@ class NoticeListItem extends Widget $attrs = array('href' => $this->profile->profileurl, 'class' => 'url'); if (!empty($this->profile->fullname)) { - $attrs['title'] = $this->profile->fullname . ' (' . $this->profile->nickname . ') '; + $attrs['title'] = $this->profile->fullname . ' (' . $this->profile->nickname . ')'; } $this->out->elementStart('a', $attrs); $this->showAvatar(); @@ -418,9 +418,17 @@ class NoticeListItem extends Widget function showContext() { - // XXX: also show context if there are replies to this notice - if (!empty($this->notice->conversation) - && $this->notice->conversation != $this->notice->id) { + $hasConversation = false; + if( !empty($this->notice->conversation) + && $this->notice->conversation != $this->notice->id){ + $hasConversation = true; + }else{ + $conversation = Notice::conversationStream($this->notice->id, 1, 1); + if($conversation->N > 0){ + $hasConversation = true; + } + } + if ($hasConversation){ $convurl = common_local_url('conversation', array('id' => $this->notice->conversation)); $this->out->element('a', array('href' => $convurl.'#notice-'.$this->notice->id, @@ -442,7 +450,7 @@ class NoticeListItem extends Widget { if (common_logged_in()) { $reply_url = common_local_url('newnotice', - array('replyto' => $this->profile->nickname)); + array('replyto' => $this->profile->nickname, 'inreplyto' => $this->notice->id)); $this->out->elementStart('a', array('href' => $reply_url, 'class' => 'notice_reply', 'title' => _('Reply to this notice'))); diff --git a/lib/router.php b/lib/router.php index 0e5fe3a54..33b098473 100644 --- a/lib/router.php +++ b/lib/router.php @@ -172,6 +172,10 @@ class Router $m->connect('notice/new?replyto=:replyto', array('action' => 'newnotice'), array('replyto' => '[A-Za-z0-9_-]+')); + $m->connect('notice/new?replyto=:replyto&inreplyto=:inreplyto', + array('action' => 'newnotice'), + array('replyto' => '[A-Za-z0-9_-]+'), + array('inreplyto' => '[0-9]+')); $m->connect('notice/:notice/file', array('action' => 'file'), diff --git a/lib/rssaction.php b/lib/rssaction.php index dd0f1005a..faf6bec7d 100644 --- a/lib/rssaction.php +++ b/lib/rssaction.php @@ -78,25 +78,12 @@ class Rss10Action extends Action function prepare($args) { parent::prepare($args); + $this->limit = (int) $this->trimmed('limit'); + if ($this->limit == 0) { $this->limit = DEFAULT_RSS_LIMIT; } - return true; - } - - /** - * Handle a request - * - * @param array $args Arguments from $_REQUEST - * - * @return void - */ - - function handle($args) - { - // Parent handling, including cache check - parent::handle($args); if (common_config('site', 'private')) { if (!isset($_SERVER['PHP_AUTH_USER'])) { @@ -122,8 +109,21 @@ class Rss10Action extends Action } } - // Get the list of notices - $this->notices = $this->getNotices($this->limit); + return true; + } + + /** + * Handle a request + * + * @param array $args Arguments from $_REQUEST + * + * @return void + */ + + function handle($args) + { + // Parent handling, including cache check + parent::handle($args); $this->showRss(); } @@ -140,7 +140,7 @@ class Rss10Action extends Action } /** - * Get the notices to output in this stream + * Get the notices to output in this stream. * * @return array an array of Notice objects sorted in reverse chron */ diff --git a/lib/util.php b/lib/util.php index bd15a5622..56753debe 100644 --- a/lib/util.php +++ b/lib/util.php @@ -391,10 +391,10 @@ function common_render_content($text, $notice) { $r = common_render_text($text); $id = $notice->profile_id; - $r = preg_replace('/(^|\s+)@([A-Za-z0-9]{1,64})/e', "'\\1@'.common_at_link($id, '\\2')", $r); + $r = preg_replace('/(^|[\s\.\,\:\;]+)@([A-Za-z0-9]{1,64})/e', "'\\1@'.common_at_link($id, '\\2')", $r); $r = preg_replace('/^T ([A-Z0-9]{1,64}) /e', "'T '.common_at_link($id, '\\1').' '", $r); - $r = preg_replace('/(^|\s+)@#([A-Za-z0-9]{1,64})/e', "'\\1@#'.common_at_hash_link($id, '\\2')", $r); - $r = preg_replace('/(^|\s)!([A-Za-z0-9]{1,64})/e', "'\\1!'.common_group_link($id, '\\2')", $r); + $r = preg_replace('/(^|[\s\.\,\:\;]+)@#([A-Za-z0-9]{1,64})/e', "'\\1@#'.common_at_hash_link($id, '\\2')", $r); + $r = preg_replace('/(^|[\s\.\,\:\;]+)!([A-Za-z0-9]{1,64})/e', "'\\1!'.common_group_link($id, '\\2')", $r); return $r; } @@ -442,9 +442,9 @@ function common_replace_urls_callback($text, $callback, $notice_id = null) { ')'. '(?:'. '(?:\:\d+)?'. //:port - '(?:/[\pN\pL$\[\]\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\*\$\+\'\"]*)?'. // /path - '(?:\?[\pN\pL\$\[\]\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\*\$\+\'\"\/]*)?'. // ?query string - '(?:\#[\pN\pL$\[\]\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\*\$\+\'\"\/\?\#]*)?'. // #fragment + '(?:/[\pN\pL$\[\]\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\*\$\+\'\"@]*)?'. // /path + '(?:\?[\pN\pL\$\[\]\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\*\$\+\'\"@\/]*)?'. // ?query string + '(?:\#[\pN\pL$\[\]\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\*\$\+\'\"\@/\?\#]*)?'. // #fragment ')(?<![\?\.\,\#\,])'. ')'. '#ixu'; @@ -552,12 +552,13 @@ function common_linkify($url) { } if (!empty($f)) { - if (isset($f->filename)) { + if ($f->isEnclosure()) { $is_attachment = true; $attachment_id = $f->id; - } else { // if it has OEmbed info, it's an attachment, too + } else { $foe = File_oembed::staticGet('file_id', $f->id); if (!empty($foe)) { + // if it has OEmbed info, it's an attachment, too $is_attachment = true; $attachment_id = $f->id; @@ -1393,9 +1394,6 @@ function common_shorten_url($long_url) $short_url_service = $reflectionObj->newInstanceArgs($_shorteners[$svc]['callInfo'][1]); $short_url = $short_url_service->shorten($long_url); - if(substr($short_url,0,7)=='http://'){ - $short_url = substr($short_url,7); - } return $short_url; } diff --git a/plugins/Autocomplete/Autocomplete.js b/plugins/Autocomplete/Autocomplete.js index dfadea004..3eff685a8 100644 --- a/plugins/Autocomplete/Autocomplete.js +++ b/plugins/Autocomplete/Autocomplete.js @@ -1,38 +1,37 @@ $(document).ready(function(){ - $.getJSON($('address .url')[0].href+'/api/statuses/friends.json?user_id=' + current_user['id'] + '&lite=true&callback=?', - function(friends){ - $('#notice_data-text').autocomplete(friends, { + $('#notice_data-text').autocomplete($('address .url')[0].href+'/plugins/Autocomplete/autocomplete.json', { multiple: true, multipleSeparator: " ", minChars: 1, formatItem: function(row, i, max){ - return '@' + row.screen_name + ' (' + row.name + ')'; + row = eval("(" + row + ")"); + switch(row.type) + { + case 'user': + return row.nickname + ' (' + row.fullname + ')'; + case 'group': + return row.nickname + ' (' + row.fullname + ')'; + } }, formatMatch: function(row, i, max){ - return '@' + row.screen_name; + row = eval("(" + row + ")"); + switch(row.type) + { + case 'user': + return row.nickname; + case 'group': + return row.nickname; + } }, formatResult: function(row){ - return '@' + row.screen_name; + row = eval("(" + row + ")"); + switch(row.type) + { + case 'user': + return '@' + row.nickname; + case 'group': + return '!' + row.nickname; + } } }); - } - ); - $.getJSON($('address .url')[0].href+'/api/statusnet/groups/list.json?user_id=' + current_user['id'] + '&callback=?', - function(groups){ - $('#notice_data-text').autocomplete(groups, { - multiple: true, - multipleSeparator: " ", - minChars: 1, - formatItem: function(row, i, max){ - return '!' + row.nickname + ' (' + row.fullname + ')'; - }, - formatMatch: function(row, i, max){ - return '!' + row.nickname; - }, - formatResult: function(row){ - return '!' + row.nickname; - } - }); - } - ); }); diff --git a/plugins/Autocomplete/AutocompletePlugin.php b/plugins/Autocomplete/AutocompletePlugin.php index b75397270..baaec73c1 100644 --- a/plugins/Autocomplete/AutocompletePlugin.php +++ b/plugins/Autocomplete/AutocompletePlugin.php @@ -31,6 +31,8 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } +require_once(INSTALLDIR.'/plugins/Autocomplete/autocomplete.php'); + class AutocompletePlugin extends Plugin { function __construct() @@ -40,13 +42,6 @@ class AutocompletePlugin extends Plugin function onEndShowScripts($action){ if (common_logged_in()) { - $current_user = common_current_user(); - $js_string = <<<EOT -<script type="text/javascript"> -var current_user = { id: '$current_user->id' }; -</script> -EOT; - $action->raw($js_string); $action->script('plugins/Autocomplete/jquery-autocomplete/jquery.autocomplete.pack.js'); $action->script('plugins/Autocomplete/Autocomplete.js'); } @@ -59,5 +54,12 @@ EOT; } } + function onRouterInitialized($m) + { + if (common_logged_in()) { + $m->connect('plugins/Autocomplete/autocomplete.json', array('action'=>'autocomplete')); + } + } + } ?> diff --git a/plugins/Autocomplete/autocomplete.php b/plugins/Autocomplete/autocomplete.php new file mode 100644 index 000000000..aa57b3915 --- /dev/null +++ b/plugins/Autocomplete/autocomplete.php @@ -0,0 +1,136 @@ +<?php +/** + * StatusNet, the distributed open-source microblogging tool + * + * List users for autocompletion + * + * 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 Plugin + * @package StatusNet + * @author Craig Andrews <candrews@integralblue.com> + * @copyright 2008-2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET') && !defined('LACONICA')) { + exit(1); +} + +/** + * List users for autocompletion + * + * This is the form for adding a new g + * + * @category Plugin + * @package StatusNet + * @author Craig Andrews <candrews@integralblue.com> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class AutocompleteAction extends Action +{ + private $result; + + /** + * Last-modified date for page + * + * When was the content of this page last modified? Based on notice, + * profile, avatar. + * + * @return int last-modified date as unix timestamp + */ + function lastModified() + { + $max=0; + foreach($this->users as $user){ + $max = max($max,strtotime($user->modified),strtotime($user->profile->modified)); + } + foreach($this->groups as $group){ + $max = max($max,strtotime($group->modified)); + } + return $max; + } + + /** + * An entity tag for this page + * + * Shows the ETag for the page, based on the notice ID and timestamps + * for the notice, profile, and avatar. It's weak, since we change + * the date text "one hour ago", etc. + * + * @return string etag + */ + function etag() + { + return '"' . implode(':', array($this->arg('action'), + crc32($this->arg('q')), //the actual string can have funny characters in we don't want showing up in the etag + $this->arg('limit'), + $this->lastModified())) . '"'; + } + + function prepare($args) + { + parent::prepare($args); + $this->groups=array(); + $this->users=array(); + $q = $this->arg('q'); + $limit = $this->arg('limit'); + if($limit > 200) $limit=200; //prevent DOS attacks + if(substr($q,0,1)=='@'){ + //user search + $q=substr($q,1); + $user = new User(); + $user->limit($limit); + $user->whereAdd('nickname like \'' . trim($user->escape($q), '\'') . '%\''); + $user->find(); + while($user->fetch()) { + $profile = Profile::staticGet($user->id); + $user->profile=$profile; + $this->users[]=$user; + } + } + if(substr($q,0,1)=='!'){ + //group search + $q=substr($q,1); + $group = new User_group(); + $group->limit($limit); + $group->whereAdd('nickname like \'' . trim($group->escape($q), '\'') . '%\''); + $group->find(); + while($group->fetch()) { + $this->groups[]=$group; + } + } + return true; + } + + function handle($args) + { + parent::handle($args); + $results = array(); + foreach($this->users as $user){ + $results[]=array('nickname' => $user->nickname, 'fullname'=> $user->profile->fullname, 'type'=>'user'); + } + foreach($this->groups as $group){ + $results[]=array('nickname' => $group->nickname, 'fullname'=> $group->fullname, 'type'=>'group'); + } + foreach($results as $result) { + print json_encode($result) . "\n"; + } + } +} diff --git a/plugins/Autocomplete/readme.txt b/plugins/Autocomplete/readme.txt index 3272aa1ee..1db4c6565 100644 --- a/plugins/Autocomplete/readme.txt +++ b/plugins/Autocomplete/readme.txt @@ -1,5 +1,7 @@ Autocomplete allows users to autocomplete screen names in @ replies. When an "@" is typed into the notice text area, an autocomplete box is displayed populated with the user's friends' screen names. +Note: This plugin doesn't work if the site is in Private mode, i.e. when $config['site']['private'] is set to true. + Installation ============ Add "addPlugin('Autocomplete');" to the bottom of your config.php diff --git a/plugins/InfiniteScroll/InfiniteScrollPlugin.php b/plugins/InfiniteScroll/InfiniteScrollPlugin.php index c955298cb..5928c007f 100644 --- a/plugins/InfiniteScroll/InfiniteScrollPlugin.php +++ b/plugins/InfiniteScroll/InfiniteScrollPlugin.php @@ -40,7 +40,7 @@ class InfiniteScrollPlugin extends Plugin function onEndShowScripts($action) { - $action->script('plugins/InfiniteScroll/jquery.infinitescroll.min.js'); + $action->script('plugins/InfiniteScroll/jquery.infinitescroll.js'); $action->script('plugins/InfiniteScroll/infinitescroll.js'); } } diff --git a/plugins/InfiniteScroll/infinitescroll.js b/plugins/InfiniteScroll/infinitescroll.js index 6513072d0..ae4d53d09 100644 --- a/plugins/InfiniteScroll/infinitescroll.js +++ b/plugins/InfiniteScroll/infinitescroll.js @@ -1,6 +1,7 @@ jQuery(document).ready(function($){ $('notices_primary').infinitescroll({ debug: true, + infiniteScroll : false, nextSelector : "li.nav_next a", loadingImg : $('address .url')[0].href+'plugins/InfiniteScroll/ajax-loader.gif', text : "<em>Loading the next set of posts...</em>", @@ -12,4 +13,3 @@ jQuery(document).ready(function($){ NoticeAttachments(); }); }); - diff --git a/plugins/InfiniteScroll/jquery.infinitescroll.js b/plugins/InfiniteScroll/jquery.infinitescroll.js index 670686b0e..ec31bb086 100644 --- a/plugins/InfiniteScroll/jquery.infinitescroll.js +++ b/plugins/InfiniteScroll/jquery.infinitescroll.js @@ -92,14 +92,14 @@ if (props.isDuringAjax || props.isInvalidPage || props.isDone) return; - if ( !isNearBottom(opts,props) ) return; + if ( opts.infiniteScroll && !isNearBottom(opts,props) ) return; // we dont want to fire the ajax multiple times props.isDuringAjax = true; // show the loading message and hide the previous/next links props.loadingMsg.appendTo( opts.contentSelector ).show(); - $( opts.navSelector ).hide(); + if(opts.infiniteScroll) $( opts.navSelector ).hide(); // increment the URL bit. e.g. /page/3/ props.currPage++; @@ -205,10 +205,19 @@ } }); - // bind scroll handler to element (if its a local scroll) or window - $(opts.localMode ? this : window) - .bind('scroll.infscr', function(){ infscrSetup(path,opts,props,callback); } ) - .trigger('scroll.infscr'); // trigger the event, in case it's a short page + if(opts.infiniteScroll){ + // bind scroll handler to element (if its a local scroll) or window + $(opts.localMode ? this : window) + .bind('scroll.infscr', function(){ infscrSetup(path,opts,props,callback); } ) + .trigger('scroll.infscr'); // trigger the event, in case it's a short page + }else{ + $(opts.nextSelector).click( + function(){ + infscrSetup(path,opts,props,callback); + return false; + } + ); + } return this; @@ -222,6 +231,7 @@ $.infinitescroll = { defaults : { debug : false, + infiniteScroll : true, preload : false, nextSelector : "div.navigation a:first", loadingImg : "http://www.infinite-scroll.com/loading.gif", diff --git a/plugins/Meteor/meteorupdater.js b/plugins/Meteor/meteorupdater.js index 2e688336f..9ce68775b 100644 --- a/plugins/Meteor/meteorupdater.js +++ b/plugins/Meteor/meteorupdater.js @@ -1,5 +1,6 @@ -// update the local timeline from a Meteor server -// +// Update the local timeline from a Meteor server +// XXX: If @a is subscribed to @b, @a should get @b's notices in @a's Personal timeline. +// Do Replies timeline. var MeteorUpdater = function() { diff --git a/plugins/PiwikAnalyticsPlugin.php b/plugins/PiwikAnalyticsPlugin.php index 85a24c132..8191f5181 100644 --- a/plugins/PiwikAnalyticsPlugin.php +++ b/plugins/PiwikAnalyticsPlugin.php @@ -59,9 +59,9 @@ if (!defined('STATUSNET')) { class PiwikAnalyticsPlugin extends Plugin { /** the base of your Piwik installation */ - var $piwikroot = null; + public $piwikroot = null; /** the Piwik Id of your statusnet installation */ - var $piwikId = null; + public $piwikId = null; /** * constructor @@ -73,7 +73,7 @@ class PiwikAnalyticsPlugin extends Plugin function __construct($root=null, $id=null) { $this->piwikroot = $root; - $this->piwikid = $id; + $this->piwikId = $id; parent::__construct(); } @@ -96,7 +96,7 @@ document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/ja </script> <script type="text/javascript"> try { - var piwikTracker = Piwik.getTracker(pkBaseURL + "piwik.php", 4); + var piwikTracker = Piwik.getTracker(pkBaseURL + "piwik.php", {$this->piwikId}); piwikTracker.trackPageView(); piwikTracker.enableLinkTracking(); } catch( err ) {} @@ -108,4 +108,4 @@ ENDOFPIWIK; $action->raw($piwikCode); return true; } -}
\ No newline at end of file +} diff --git a/plugins/Realtime/RealtimePlugin.php b/plugins/Realtime/RealtimePlugin.php index 82eca3d08..e30c41156 100644 --- a/plugins/Realtime/RealtimePlugin.php +++ b/plugins/Realtime/RealtimePlugin.php @@ -50,6 +50,11 @@ class RealtimePlugin extends Plugin protected $favorurl = null; protected $deleteurl = null; + /** + * When it's time to initialize the plugin, calculate and + * pass the URLs we need. + */ + function onInitializePlugin() { $this->replyurl = common_local_url('newnotice'); @@ -57,29 +62,26 @@ class RealtimePlugin extends Plugin // FIXME: need to find a better way to pass this pattern in $this->deleteurl = common_local_url('deletenotice', array('notice' => '0000000000')); + return true; } function onEndShowScripts($action) { - $path = null; + $timeline = $this->_getTimeline($action); - switch ($action->trimmed('action')) { - case 'public': - $path = array('public'); - break; - case 'tag': - $tag = $action->trimmed('tag'); - if (!empty($tag)) { - $path = array('tag', $tag); - } else { - return true; - } - break; - default: + // If there's not a timeline on this page, + // just return true + + if (empty($timeline)) { return true; } - $timeline = $this->_pathToChannel($path); + $base = $action->selfUrl(); + if (mb_strstr($base, '?')) { + $url = $base . '&realtime=1'; + } else { + $url = $base . '?realtime=1'; + } $scripts = $this->_getScripts(); @@ -95,10 +97,22 @@ class RealtimePlugin extends Plugin $user_id = 0; } + if ($action->boolean('realtime')) { + $realtimeUI = ' RealtimeUpdate.initPopupWindow();'; + } + else { + $iconurl = common_path('plugins/Realtime/icon_external.gif'); + $realtimeUI = ' RealtimeUpdate.addPopup("'.$url.'", "'.$timeline.'", "'. $iconurl .'");'; + } + $action->elementStart('script', array('type' => 'text/javascript')); - $action->raw("$(document).ready(function() { "); - $action->raw($this->_updateInitialize($timeline, $user_id)); - $action->raw(" });"); + + $script = ' $(document).ready(function() { '. + $realtimeUI. + $this->_updateInitialize($timeline, $user_id). + '}); '; + $action->raw($script); + $action->elementEnd('script'); return true; @@ -108,13 +122,23 @@ class RealtimePlugin extends Plugin { $paths = array(); - // XXX: Add other timelines; this is just for the public one + // Add to the author's timeline + + $user = User::staticGet('id', $notice->profile_id); + + if (!empty($user)) { + $paths[] = array('showstream', $user->nickname); + } + + // Add to the public timeline if ($notice->is_local || ($notice->is_local == 0 && !common_config('public', 'localonly'))) { $paths[] = array('public'); } + // Add to the tags timeline + $tags = $this->getNoticeTags($notice); if (!empty($tags)) { @@ -123,6 +147,46 @@ class RealtimePlugin extends Plugin } } + // Add to inbox timelines + // XXX: do a join + + $inbox = new Notice_inbox(); + $inbox->notice_id = $notice->id; + + if ($inbox->find()) { + while ($inbox->fetch()) { + $user = User::staticGet('id', $inbox->user_id); + $paths[] = array('all', $user->nickname); + } + } + + // Add to the replies timeline + + $reply = new Reply(); + $reply->notice_id = $notice->id; + + if ($reply->find()) { + while ($reply->fetch()) { + $user = User::staticGet('id', $reply->profile_id); + if (!empty($user)) { + $paths[] = array('replies', $user->nickname); + } + } + } + + // Add to the group timeline + // XXX: join + + $gi = new Group_inbox(); + $gi->notice_id = $notice->id; + + if ($gi->find()) { + while ($gi->fetch()) { + $ug = User_group::staticGet('id', $gi->group_id); + $paths[] = array('showgroup', $ug->nickname); + } + } + if (count($paths) > 0) { $json = $this->noticeAsJson($notice); @@ -140,6 +204,39 @@ class RealtimePlugin extends Plugin return true; } + function onStartShowBody($action) + { + $realtime = $action->boolean('realtime'); + if (!$realtime) { + return true; + } + + $action->elementStart('body', + (common_current_user()) ? array('id' => $action->trimmed('action'), + 'class' => 'user_in') + : array('id' => $action->trimmed('action'))); + + $action->elementStart('div', array('id' => 'header')); + + // XXX hack to deal with JS that tries to get the + // root url from page output + + $action->elementStart('address'); + $action->element('a', array('class' => 'url', + 'href' => common_local_url('public')), + ''); + $action->elementEnd('address'); + + if (common_logged_in()) { + $action->showNoticeForm(); + } + $action->elementEnd('div'); + + $action->showContentBlock(); + $action->elementEnd('body'); + return false; // No default processing + } + function noticeAsJson($notice) { // FIXME: this code should be abstracted to a neutral third @@ -224,4 +321,41 @@ class RealtimePlugin extends Plugin { return ''; } + + function _getTimeline($action) + { + $path = null; + $timeline = null; + + $action_name = $action->trimmed('action'); + + switch ($action_name) { + case 'public': + $path = array('public'); + break; + case 'tag': + $tag = $action->trimmed('tag'); + if (!empty($tag)) { + $path = array('tag', $tag); + } + break; + case 'showstream': + case 'all': + case 'replies': + case 'showgroup': + $nickname = common_canonical_nickname($action->trimmed('nickname')); + if (!empty($nickname)) { + $path = array($action_name, $nickname); + } + break; + default: + break; + } + + if (!empty($path)) { + $timeline = $this->_pathToChannel($path); + } + + return $timeline; + } } diff --git a/plugins/Realtime/icon_external.gif b/plugins/Realtime/icon_external.gif Binary files differnew file mode 100644 index 000000000..c4118d53b --- /dev/null +++ b/plugins/Realtime/icon_external.gif diff --git a/plugins/Realtime/jquery.getUrlParam.js b/plugins/Realtime/jquery.getUrlParam.js new file mode 100644 index 000000000..e8f73eb47 --- /dev/null +++ b/plugins/Realtime/jquery.getUrlParam.js @@ -0,0 +1,72 @@ +/* Copyright (c) 2006-2007 Mathias Bank (http://www.mathias-bank.de) + * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) + * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. + * + * Version 2.1 + * + * Thanks to + * Hinnerk Ruemenapf - http://hinnerk.ruemenapf.de/ for bug reporting and fixing. + * Tom Leonard for some improvements + * + */ +jQuery.fn.extend({ +/** +* Returns get parameters. +* +* If the desired param does not exist, null will be returned +* +* To get the document params: +* @example value = $(document).getUrlParam("paramName"); +* +* To get the params of a html-attribut (uses src attribute) +* @example value = $('#imgLink').getUrlParam("paramName"); +*/ + getUrlParam: function(strParamName){ + strParamName = escape(unescape(strParamName)); + + var returnVal = new Array(); + var qString = null; + + if ($(this).attr("nodeName")=="#document") { + //document-handler + + if (window.location.search.search(strParamName) > -1 ){ + + qString = window.location.search.substr(1,window.location.search.length).split("&"); + } + + } else if ($(this).attr("src")!="undefined") { + + var strHref = $(this).attr("src") + if ( strHref.indexOf("?") > -1 ){ + var strQueryString = strHref.substr(strHref.indexOf("?")+1); + qString = strQueryString.split("&"); + } + } else if ($(this).attr("href")!="undefined") { + + var strHref = $(this).attr("href") + if ( strHref.indexOf("?") > -1 ){ + var strQueryString = strHref.substr(strHref.indexOf("?")+1); + qString = strQueryString.split("&"); + } + } else { + return null; + } + + + if (qString==null) return null; + + + for (var i=0;i<qString.length; i++){ + if (escape(unescape(qString[i].split("=")[0])) == strParamName){ + returnVal.push(qString[i].split("=")[1]); + } + + } + + + if (returnVal.length==0) return null; + else if (returnVal.length==1) return returnVal[0]; + else return returnVal; + } +});
\ No newline at end of file diff --git a/plugins/Realtime/realtimeupdate.js b/plugins/Realtime/realtimeupdate.js index d55db5859..57fe0a843 100644 --- a/plugins/Realtime/realtimeupdate.js +++ b/plugins/Realtime/realtimeupdate.js @@ -1,8 +1,8 @@ // add a notice encoded as JSON into the current timeline // +// TODO: i18n RealtimeUpdate = { - _userid: 0, _replyurl: '', _favorurl: '', @@ -10,10 +10,10 @@ RealtimeUpdate = { init: function(userid, replyurl, favorurl, deleteurl) { - RealtimeUpdate._userid = userid; - RealtimeUpdate._replyurl = replyurl; - RealtimeUpdate._favorurl = favorurl; - RealtimeUpdate._deleteurl = deleteurl; + RealtimeUpdate._userid = userid; + RealtimeUpdate._replyurl = replyurl; + RealtimeUpdate._favorurl = favorurl; + RealtimeUpdate._deleteurl = deleteurl; }, receive: function(data) @@ -21,7 +21,7 @@ RealtimeUpdate = { id = data.id; // Don't add it if it already exists - + // if ($("#notice-"+id).length > 0) { return; } @@ -50,30 +50,19 @@ RealtimeUpdate = { "<p class=\"entry-content\">"+html+"</p>"+ "</div>"+ "<div class=\"entry-content\">"+ - "<dl class=\"timestamp\">"+ - "<dt>Published</dt>"+ - "<dd>"+ - "<a rel=\"bookmark\" href=\""+data['url']+"\" >"+ + "<a class=\"timestamp\" rel=\"bookmark\" href=\""+data['url']+"\" >"+ "<abbr class=\"published\" title=\""+data['created_at']+"\">a few seconds ago</abbr>"+ "</a> "+ - "</dd>"+ - "</dl>"+ - "<dl class=\"device\">"+ - "<dt>From</dt> "+ - "<dd>"+source+"</dd>"+ // may have a link, I think - "</dl>"; - + "<span class=\"source\">"+ + "from "+ + "<span class=\"device\">"+source+"</span>"+ // may have a link + "</span>"; if (data['in_reply_to_status_id']) { - ni = ni+" <dl class=\"response\">"+ - "<dt>To</dt>"+ - "<dd>"+ - "<a href=\""+data['in_reply_to_status_url']+"\" rel=\"in-reply-to\">in reply to</a>"+ - "</dd>"+ - "</dl>"; + ni = ni+" <a class=\"response\" href=\""+data['in_reply_to_status_url']+"\">in context</a>"; } ni = ni+"</div>"+ - "<div class=\"notice-options\">"; + "<div class=\"notice-options\">"; if (RealtimeUpdate._userid != 0) { var input = $("form#form_notice fieldset input#token"); @@ -95,12 +84,12 @@ RealtimeUpdate = { var ff; ff = "<form id=\"favor-"+id+"\" class=\"form_favor\" method=\"post\" action=\""+RealtimeUpdate._favorurl+"\">"+ - "<fieldset>"+ - "<legend>Favor this notice</legend>"+ // XXX: i18n + "<fieldset>"+ + "<legend>Favor this notice</legend>"+ "<input name=\"token-"+id+"\" type=\"hidden\" id=\"token-"+id+"\" value=\""+session_key+"\"/>"+ "<input name=\"notice\" type=\"hidden\" id=\"notice-n"+id+"\" value=\""+id+"\"/>"+ "<input type=\"submit\" id=\"favor-submit-"+id+"\" name=\"favor-submit-"+id+"\" class=\"submit\" value=\"Favor\" title=\"Favor this notice\"/>"+ - "</fieldset>"+ + "</fieldset>"+ "</form>"; return ff; }, @@ -108,28 +97,51 @@ RealtimeUpdate = { makeReplyLink: function(id, nickname) { var rl; - rl = "<dl class=\"notice_reply\">"+ - "<dt>Reply to this notice</dt>"+ - "<dd>"+ - "<a href=\""+RealtimeUpdate._replyurl+"?replyto="+nickname+"\" title=\"Reply to this notice\">Reply <span class=\"notice_id\">"+id+"</span>"+ - "</a>"+ - "</dd>"+ - "</dl>"; + rl = "<a class=\"notice_reply\" href=\""+RealtimeUpdate._replyurl+"?replyto="+nickname+"\" title=\"Reply to this notice\">Reply <span class=\"notice_id\">"+id+"</span></a>"; return rl; - }, + }, makeDeleteLink: function(id) { var dl, delurl; delurl = RealtimeUpdate._deleteurl.replace("0000000000", id); - dl = "<dl class=\"notice_delete\">"+ - "<dt>Delete this notice</dt>"+ - "<dd>"+ - "<a href=\""+delurl+"\" title=\"Delete this notice\">Delete</a>"+ - "</dd>"+ - "</dl>"; + dl = "<a class=\"notice_delete\" href=\""+delurl+"\" title=\"Delete this notice\">Delete</a>"; return dl; }, + + addPopup: function(url, timeline, iconurl) + { + $('#site_nav_local_views .current a').append('<button id="realtime_timeline" title="Real-time pop window">↗</button>'); + + $('#realtime_timeline').css({ + 'margin':'2px 0 0 11px', + 'background':'transparent url('+ iconurl + ') no-repeat 45% 45%', + 'text-indent':'-9999px', + 'width':'16px', + 'height':'16px', + 'padding':'0', + 'display':'block', + 'float':'right', + 'border':'none', + 'cursor':'pointer' + }); + + $('#realtime_timeline').click(function() { + window.open(url, + timeline, + 'toolbar=no,resizable=yes,scrollbars=yes,status=yes'); + + return false; + }); + }, + + initPopupWindow: function() + { + window.resizeTo(575, 640); + $('address').hide(); + $('#content').css({'width':'92%'}); + } } + diff --git a/plugins/recaptcha/README b/plugins/recaptcha/README index ce23a2695..b996f96cc 100644 --- a/plugins/recaptcha/README +++ b/plugins/recaptcha/README @@ -6,7 +6,7 @@ Use: 1. Get an API key from http://recaptcha.net 2. In config.php add: -include_once('plugins/recaptcha.php'); +include_once('plugins/recaptcha/recaptcha.php'); $captcha = new recaptcha(publickey, privatekey, showErrors); Changelog diff --git a/tests/URLDetectionTest.php b/tests/URLDetectionTest.php index 87b537646..1c3f7cd96 100644 --- a/tests/URLDetectionTest.php +++ b/tests/URLDetectionTest.php @@ -27,6 +27,8 @@ class URLDetectionTest extends PHPUnit_Framework_TestCase return array( array('not a link :: no way', 'not a link :: no way'), + array('link http://www.somesite.com/xyz/35637563@N00/52803365/ link', + 'link <a href="http://www.somesite.com/xyz/35637563@N00/52803365/" rel="external">http://www.somesite.com/xyz/35637563@N00/52803365/</a> link'), array('http://127.0.0.1', '<a href="http://127.0.0.1/" rel="external">http://127.0.0.1</a>'), array('127.0.0.1', diff --git a/theme/cloudy/css/display.css b/theme/cloudy/css/display.css index 3851bc057..3fe05eb3d 100644 --- a/theme/cloudy/css/display.css +++ b/theme/cloudy/css/display.css @@ -120,6 +120,10 @@ float:left; margin-left:11px; float:left; } +.form_settings .form_data textarea { +width:325px; +} + .form_settings .form_data input.submit { margin-left:0; } @@ -968,9 +972,6 @@ right:7px; top:47px; right:7px; } -.notice-options .notice_reply dt { -display:none; -} .notice-options input, .notice-options a { @@ -978,13 +979,13 @@ text-indent:-9999px; outline:none; } -.notice-options .notice_reply a, +.notice-options .notice_reply, .notice-options input.submit { display:block; border:0; } -.notice-options .notice_reply a, -.notice-options .notice_delete a { +.notice-options .notice_reply, +.notice-options .notice_delete { text-decoration:none; padding-left:16px; } @@ -1375,6 +1376,12 @@ padding-top:160px; #smssettings #form_notice, #twittersettings #form_notice, #imsettings #form_notice, +#userdesignsettings #form_notice, +#groupdesignsettings #form_notice, +#grouplogo #form_notice, +#editgroup #form_notice, +#blockedfromgroup #form_notice, +#groupmembers #form_notice, #doc #form_notice, #usergroups #form_notice, #invite #form_notice, @@ -1584,11 +1591,11 @@ background:transparent url(../../base/images/icons/twotone/green/clip-02.gif) no background:none; } -.notice-options .notice_reply a, +.notice-options .notice_reply, .notice-options form input.submit { background-color:transparent; } -.notice-options .notice_reply a { +.notice-options .notice_reply { background:transparent url(../images/icons/icon_reply.gif) no-repeat 0 45%; } .notice-options form.form_favor input.submit { @@ -1597,7 +1604,7 @@ background:transparent url(../images/icons/icon_favourite.gif) no-repeat 0 45%; .notice-options form.form_disfavor input.submit { background:transparent url(../images/icons/icon_disfavourite.gif) no-repeat 0 45%; } -.notice-options .notice_delete a { +.notice-options .notice_delete { background:transparent url(../images/icons/icon_trash.gif) no-repeat 0 45%; } |