From 1536d3ef29486fc1c5387e73106b86873fdb7cf9 Mon Sep 17 00:00:00 2001 From: Diogo Cordeiro Date: Mon, 6 May 2019 23:58:45 +0100 Subject: [PATCH] [XML/HTML Outputter] General improvements and refactoring as well as some bug fixes --- actions/apiaccountratelimitstatus.php | 118 +- actions/networkpublic.php | 47 +- actions/public.php | 2 +- lib/action.php | 1652 +++++++++-------- lib/activityobject.php | 559 +++--- lib/apiaction.php | 199 +- lib/atom10feed.php | 2 +- lib/atomgroupnoticefeed.php | 2 +- lib/erroraction.php | 13 +- lib/htmloutputter.php | 436 ++--- lib/infoaction.php | 20 +- lib/menu.php | 4 +- lib/servererroraction.php | 29 +- lib/serverexception.php | 2 +- lib/xmloutputter.php | 96 +- lib/xmlstringer.php | 21 +- plugins/Directory/lib/sortablegrouplist.php | 2 +- .../lib/sortablesubscriptionlist.php | 2 +- plugins/Oembed/OembedPlugin.php | 8 +- plugins/OpenID/actions/openidlogin.php | 4 +- 20 files changed, 1651 insertions(+), 1567 deletions(-) diff --git a/actions/apiaccountratelimitstatus.php b/actions/apiaccountratelimitstatus.php index 592cf908fc..23c2eb5d6d 100644 --- a/actions/apiaccountratelimitstatus.php +++ b/actions/apiaccountratelimitstatus.php @@ -31,7 +31,9 @@ * @link http://status.net/ */ -if (!defined('GNUSOCIAL')) { exit(1); } +if (!defined('GNUSOCIAL')) { + exit(1); +} /** * We don't have a rate limit, but some clients check this method. @@ -47,62 +49,6 @@ if (!defined('GNUSOCIAL')) { exit(1); } */ class ApiAccountRateLimitStatusAction extends ApiBareAuthAction { - /** - * Handle the request - * - * Return some Twitter-ish data about API limits - * - * @param array $args $_REQUEST data (unused) - * - * @return void - */ - protected function handle() - { - parent::handle(); - - if (!in_array($this->format, array('xml', 'json'))) { - $this->clientError( - // TRANS: Client error displayed when coming across a non-supported API method. - _('API method not found.'), - 404, - $this->format - ); - } - - $reset = new DateTime(); - $reset->modify('+1 hour'); - - $this->initDocument($this->format); - - if ($this->format == 'xml') { - $this->elementStart('hash'); - $this->element('remaining-hits', array('type' => 'integer'), 150); - $this->element('hourly-limit', array('type' => 'integer'), 150); - $this->element( - 'reset-time', array('type' => 'datetime'), - common_date_iso8601($reset->format('r')) - ); - $this->element( - 'reset_time_in_seconds', - array('type' => 'integer'), - strtotime('+1 hour') - ); - $this->elementEnd('hash'); - } elseif ($this->format == 'json') { - $out = array( - 'reset_time_in_seconds' => strtotime('+1 hour'), - 'remaining_hits' => 150, - 'hourly_limit' => 150, - 'reset_time' => common_date_rfc2822( - $reset->format('r') - ) - ); - print json_encode($out); - } - - $this->endDocument($this->format); - } - /** * Return true if read only. * @@ -112,8 +58,64 @@ class ApiAccountRateLimitStatusAction extends ApiBareAuthAction * * @return boolean is read only action? */ - function isReadOnly($args) + public function isReadOnly($args) { return true; } + + /** + * Handle the request + * + * Return some Twitter-ish data about API limits + * + * @return void + * @throws ClientException + */ + protected function handle() + { + parent::handle(); + + if (!in_array($this->format, ['xml', 'json'])) { + $this->clientError( + // TRANS: Client error displayed when coming across a non-supported API method. + _('API method not found.'), + 404, + $this->format + ); + } + + $reset = new DateTime(); + $reset->modify('+1 hour'); + + $this->initDocument($this->format); + + if ($this->format == 'xml') { + $this->elementStart('hash'); + $this->element('remaining-hits', ['type' => 'integer'], "150"); + $this->element('hourly-limit', ['type' => 'integer'], "150"); + $this->element( + 'reset-time', + ['type' => 'datetime'], + common_date_iso8601($reset->format('r')) + ); + $this->element( + 'reset_time_in_seconds', + ['type' => 'integer'], + strtotime('+1 hour') + ); + $this->elementEnd('hash'); + } elseif ($this->format == 'json') { + $out = [ + 'reset_time_in_seconds' => strtotime('+1 hour'), + 'remaining_hits' => 150, + 'hourly_limit' => 150, + 'reset_time' => common_date_rfc2822( + $reset->format('r') + ) + ]; + print json_encode($out); + } + + $this->endDocument($this->format); + } } diff --git a/actions/networkpublic.php b/actions/networkpublic.php index 41c4e37e3c..4967a85eb9 100644 --- a/actions/networkpublic.php +++ b/actions/networkpublic.php @@ -49,25 +49,36 @@ class NetworkpublicAction extends SitestreamAction // Network public tag cloud? } + /** + * Output elements for RSS and Atom feeds + * + * @return array + */ function getFeeds() { - return array(new Feed(Feed::JSON, - common_local_url('ApiTimelineNetworkPublic', - array('format' => 'as')), - // TRANS: Link description for the _global_ network public timeline feed. - _('Network Public Timeline Feed (Activity Streams JSON)')), - new Feed(Feed::RSS1, common_local_url('publicrss'), - // TRANS: Link description for the _global_ network public timeline feed. - _('Network Public Timeline Feed (RSS 1.0)')), - new Feed(Feed::RSS2, - common_local_url('ApiTimelineNetworkPublic', - array('format' => 'rss')), - // TRANS: Link description for the _global_ network public timeline feed. - _('Network Public Timeline Feed (RSS 2.0)')), - new Feed(Feed::ATOM, - common_local_url('ApiTimelineNetworkPublic', - array('format' => 'atom')), - // TRANS: Link description for the _global_ network public timeline feed. - _('Network Public Timeline Feed (Atom)'))); + return [ + new Feed(Feed::ATOM, + common_local_url('ApiTimelinePublic', + array('format' => 'atom')), + // TRANS: Link description for public timeline feed. + _('Public Timeline Feed (Atom)') + ), + new Feed(Feed::JSON, + common_local_url('ApiTimelinePublic', + array('format' => 'as')), + // TRANS: Link description for public timeline feed. + _('Public Timeline Feed (Activity Streams JSON)') + ), + new Feed(Feed::RSS1, common_local_url('publicrss'), + // TRANS: Link description for public timeline feed. + _('Public Timeline Feed (RSS 1.0)') + ), + new Feed(Feed::RSS2, + common_local_url('ApiTimelinePublic', + array('format' => 'rss')), + // TRANS: Link description for public timeline feed. + _('Public Timeline Feed (RSS 2.0)') + ), + ]; } } diff --git a/actions/public.php b/actions/public.php index dd69f7ef24..71239eaf99 100644 --- a/actions/public.php +++ b/actions/public.php @@ -93,7 +93,7 @@ class PublicAction extends SitestreamAction /** * Output elements for RSS and Atom feeds * - * @return void + * @return array */ function getFeeds() { diff --git a/lib/action.php b/lib/action.php index bab8a6dbcc..32b49e4e11 100644 --- a/lib/action.php +++ b/lib/action.php @@ -28,7 +28,9 @@ * @link http://status.net/ */ -if (!defined('GNUSOCIAL')) { exit(1); } +if (!defined('GNUSOCIAL')) { + exit(1); +} /** * Base class for all actions @@ -51,12 +53,12 @@ if (!defined('GNUSOCIAL')) { exit(1); } class Action extends HTMLOutputter // lawsuit { // This should be protected/private in the future - public $args = array(); + public $args = []; // Action properties, set per-class protected $action = false; - protected $ajax = false; - protected $menus = true; + protected $ajax = false; + protected $menus = true; protected $needLogin = false; protected $needPost = false; // implies canPost if true protected $canPost = false; // can this action handle POST method? @@ -66,31 +68,35 @@ class Action extends HTMLOutputter // lawsuit // Related to front-end user representation protected $format = null; - protected $error = null; - protected $msg = null; + protected $error = null; + protected $msg = null; /** * Constructor * * Just wraps the HTMLOutputter constructor. * - * @param string $output URI to output to, default = stdout + * @param string $output URI to output to, default = stdout * @param boolean $indent Whether to indent output, default true * * @see XMLOutputter::__construct * @see HTMLOutputter::__construct */ - function __construct($output='php://output', $indent=null) + public function __construct($output = 'php://output', $indent = null) { parent::__construct($output, $indent); } - function getError() + public static function run(array $args = [], $output = 'php://output', $indent = null) { - return $this->error; + $class = get_called_class(); + $action = new $class($output, $indent); + set_exception_handler(array($action, 'handleError')); + $action->execute($args); + return $action; } - function getInfo() + public function getInfo() { return $this->msg; } @@ -107,15 +113,65 @@ class Action extends HTMLOutputter // lawsuit } } - static public function run(array $args=array(), $output='php://output', $indent=null) { - $class = get_called_class(); - $action = new $class($output, $indent); - set_exception_handler(array($action, 'handleError')); - $action->execute($args); - return $action; + /** + * Client error + * + * @param string $msg error message to display + * @param integer $code http error code, 400 by default + * @param string $format error format (json, xml, text) for ApiAction + * + * @return void + * @throws ClientException always + */ + public function clientError($msg, $code = 400, $format = null) + { + // $format is currently only relevant for an ApiAction anyway + if ($format === null) { + $format = $this->format; + } + + common_debug("User error '{$code}' on '{$this->action}': {$msg}", __FILE__); + + if (!array_key_exists($code, ClientErrorAction::$status)) { + $code = 400; + } + + $status_string = ClientErrorAction::$status[$code]; + + switch ($format) { + case 'xml': + header("HTTP/1.1 {$code} {$status_string}"); + $this->initDocument('xml'); + $this->elementStart('hash'); + $this->element('error', null, $msg); + $this->element('request', null, $_SERVER['REQUEST_URI']); + $this->elementEnd('hash'); + $this->endDocument('xml'); + break; + case 'json': + if (!isset($this->callback)) { + header("HTTP/1.1 {$code} {$status_string}"); + } + $this->initDocument('json'); + $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']); + print(json_encode($error_array)); + $this->endDocument('json'); + break; + case 'text': + header("HTTP/1.1 {$code} {$status_string}"); + header('Content-Type: text/plain; charset=utf-8'); + echo $msg; + break; + default: + common_log(LOG_ERR, 'Handled clientError (' . _ve($code) . ') but cannot output into desired format (' . _ve($this->format) . '): ' . _ve($msg)); + $action = new ClientErrorAction($msg, $code); + $action->execute(); + } + exit((int)$code); } - public function execute(array $args=array()) { + public function execute(array $args = []) + { // checkMirror stuff if (common_config('db', 'mirror') && $this->isReadOnly($args)) { if (is_array(common_config('db', 'mirror'))) { @@ -134,7 +190,7 @@ class Action extends HTMLOutputter // lawsuit if (Event::handle('StartActionExecute', array($this, &$args))) { $prepared = $this->prepare($args); if ($prepared) { - $this->handle($args); + $this->handle(); } else { common_debug('Prepare failed for Action.'); } @@ -144,14 +200,29 @@ class Action extends HTMLOutputter // lawsuit } } + /** + * Return true if read only. + * + * MAY override + * + * @param array $args other arguments + * + * @return boolean is read only action? + */ + public function isReadOnly($args) + { + return false; + } + /** * For initializing members of the class. * - * @param array $argarray misc. arguments + * @param array $args misc. arguments * * @return boolean true + * @throws ClientException */ - protected function prepare(array $args=array()) + protected function prepare(array $args = []) { if ($this->needPost && !$this->isPost()) { // TRANS: Client error. POST is a HTTP command. It should not be translated. @@ -183,23 +254,259 @@ class Action extends HTMLOutputter // lawsuit return true; } + /** + * Check if the current request is a POST + * + * @return boolean true if POST; otherwise false. + */ + + public function isPost() + { + return ($_SERVER['REQUEST_METHOD'] == 'POST'); + } + + // Must be run _after_ prepare + + /** + * Returns trimmed query argument or default value if not found + * + * @param string $key requested argument + * @param string $def default value to return if $key is not provided + * + * @return boolean is read only action? + */ + public function trimmed($key, $def = null) + { + $arg = $this->arg($key, $def); + return is_string($arg) ? trim($arg) : $arg; + } + + /** + * Returns query argument or default value if not found + * + * @param string $key requested argument + * @param string $def default value to return if $key is not provided + * + * @return boolean is read only action? + */ + public function arg($key, $def = null) + { + if (array_key_exists($key, $this->args)) { + return $this->args[$key]; + } else { + return $def; + } + } + + /** + * Boolean understands english (yes, no, true, false) + * + * @param string $key query key we're interested in + * @param string $def default value + * + * @return boolean interprets yes/no strings as boolean + */ + public function boolean($key, $def = false) + { + $arg = strtolower($this->trimmed($key)); + + if (is_null($arg)) { + return $def; + } elseif (in_array($arg, array('true', 'yes', '1', 'on'))) { + return true; + } elseif (in_array($arg, array('false', 'no', '0'))) { + return false; + } else { + return $def; + } + } + + /** + * If not logged in, take appropriate action (redir or exception) + * + * @param boolean $redir Redirect to login if not logged in + * + * @return boolean true if logged in (never returns if not) + * @throws ClientException + */ + public function checkLogin($redir = true) + { + if (common_logged_in()) { + return true; + } + + if ($redir == true) { + common_set_returnto($_SERVER['REQUEST_URI']); + common_redirect(common_local_url('login')); + } + + // TRANS: Error message displayed when trying to perform an action that requires a logged in user. + $this->clientError(_('Not logged in.'), 403); + } + public function updateScopedProfile() { $this->scoped = Profile::current(); return $this->scoped; } + /** + * Handler method + */ + protected function handle() + { + header('Vary: Accept-Encoding,Cookie'); + + $lm = $this->lastModified(); + $etag = $this->etag(); + + if ($etag) { + header('ETag: ' . $etag); + } + + if ($lm) { + header('Last-Modified: ' . date(DATE_RFC1123, $lm)); + if ($this->isCacheable()) { + header('Expires: ' . gmdate('D, d M Y H:i:s', 0) . ' GMT'); + header("Cache-Control: private, must-revalidate, max-age=0"); + header("Pragma:"); + } + } + + $checked = false; + if ($etag) { + $if_none_match = (array_key_exists('HTTP_IF_NONE_MATCH', $_SERVER)) ? + $_SERVER['HTTP_IF_NONE_MATCH'] : null; + if ($if_none_match) { + // If this check fails, ignore the if-modified-since below. + $checked = true; + if ($this->_hasEtag($etag, $if_none_match)) { + header('HTTP/1.1 304 Not Modified'); + // Better way to do this? + exit(0); + } + } + } + + if (!$checked && $lm && array_key_exists('HTTP_IF_MODIFIED_SINCE', $_SERVER)) { + $if_modified_since = $_SERVER['HTTP_IF_MODIFIED_SINCE']; + $ims = strtotime($if_modified_since); + if ($lm <= $ims) { + header('HTTP/1.1 304 Not Modified'); + // Better way to do this? + exit(0); + } + } + } + + /** + * Return last modified, if applicable. + * + * MAY override + * + * @return string last modified http header + */ + public function lastModified() + { + // For comparison with If-Last-Modified + // If not applicable, return null + return null; + } + + /** + * Return etag, if applicable. + * + * MAY override + * + * @return string etag http header + */ + public function etag() + { + return null; + } + + /** + * Is this action cacheable? + * + * If the action returns a last-modified + * + * @return boolean is read only action? + */ + public function isCacheable() + { + return true; + } + + /** + * HasĀ etag? (private) + * + * @param string $etag etag http header + * @param string $if_none_match ifNoneMatch http header + * + * @return boolean + */ + public function _hasEtag($etag, $if_none_match) + { + $etags = explode(',', $if_none_match); + return in_array($etag, $etags) || in_array('*', $etags); + } + + /** + * Server error + * + * @param string $msg error message to display + * @param integer $code http error code, 500 by default + * + * @param string $format + * @return void + */ + public function serverError($msg, $code = 500, $format = null) + { + if ($format === null) { + $format = $this->format; + } + + common_debug("Server error '{$code}' on '{$this->action}': {$msg}", __FILE__); + + if (!array_key_exists($code, ServerErrorAction::$status)) { + $code = 500; + } + + $status_string = ServerErrorAction::$status[$code]; + + switch ($format) { + case 'xml': + header("HTTP/1.1 {$code} {$status_string}"); + $this->initDocument('xml'); + $this->elementStart('hash'); + $this->element('error', null, $msg); + $this->element('request', null, $_SERVER['REQUEST_URI']); + $this->elementEnd('hash'); + $this->endDocument('xml'); + break; + case 'json': + if (!isset($this->callback)) { + header("HTTP/1.1 {$code} {$status_string}"); + } + $this->initDocument('json'); + $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']); + print(json_encode($error_array)); + $this->endDocument('json'); + break; + default: + common_log(LOG_ERR, 'Handled serverError (' . _ve($code) . ') but cannot output into desired format (' . _ve($this->format) . '): ' . _ve($msg)); + $action = new ServerErrorAction($msg, $code); + $action->execute(); + } + + exit((int)$code); + } + public function getScoped() { return ($this->scoped instanceof Profile) ? $this->scoped : null; } - // Must be run _after_ prepare - public function getActionName() - { - return $this->action; - } - public function isAction(array $names) { foreach ($names as $class) { @@ -216,7 +523,10 @@ class Action extends HTMLOutputter // lawsuit /** * Show page, a template method. * - * @return nothing + * @return void + * @throws ClientException + * @throws ReflectionException + * @throws ServerException */ public function showPage() { @@ -253,7 +563,7 @@ class Action extends HTMLOutputter // lawsuit $this->elementEnd('head'); $this->elementStart('body'); if ($this->getError()) { - $this->element('p', array('id'=>'error'), $this->getError()); + $this->element('p', array('id' => 'error'), $this->getError()); } else { $this->showContent(); } @@ -261,7 +571,37 @@ class Action extends HTMLOutputter // lawsuit $this->endHTML(); } - function endHTML() + /** + * Returns the page title + * + * SHOULD overload + * + * @return string page title + */ + + public function title() + { + // TRANS: Page title for a page without a title set. + return _('Untitled page'); + } + + public function getError() + { + return $this->error; + } + + /** + * Show content. + * + * MUST overload (unless there's not a notice) + * + * @return void + */ + protected function showContent() + { + } + + public function endHTML() { global $_startTime; @@ -271,15 +611,15 @@ class Action extends HTMLOutputter // lawsuit $this->raw(""); } - return parent::endHTML(); + parent::endHTML(); } /** * Show head, a template method. * - * @return nothing + * @return void */ - function showHead() + public function showHead() { // XXX: attributes (profile?) $this->elementStart('head'); @@ -302,54 +642,45 @@ class Action extends HTMLOutputter // lawsuit /** * Show title, a template method. * - * @return nothing + * @return void */ - function showTitle() + public function showTitle() { - $this->element('title', null, - // TRANS: Page title. %1$s is the title, %2$s is the site name. - sprintf(_('%1$s - %2$s'), - $this->title(), - common_config('site', 'name'))); - } - - /** - * Returns the page title - * - * SHOULD overload - * - * @return string page title - */ - - function title() - { - // TRANS: Page title for a page without a title set. - return _('Untitled page'); + $this->element( + 'title', + null, + // TRANS: Page title. %1$s is the title, %2$s is the site name. + sprintf( + _('%1$s - %2$s'), + $this->title(), + common_config('site', 'name') + ) + ); } /** * Show themed shortcut icon * - * @return nothing + * @return void */ - function showShortcutIcon() + public function showShortcutIcon() { if (is_readable(INSTALLDIR . '/theme/' . common_config('site', 'theme') . '/favicon.ico')) { $this->element('link', array('rel' => 'shortcut icon', - 'href' => Theme::path('favicon.ico'))); + 'href' => Theme::path('favicon.ico'))); } else { // favicon.ico should be HTTPS if the rest of the page is $this->element('link', array('rel' => 'shortcut icon', - 'href' => common_path('favicon.ico', GNUsocial::isHTTPS()))); + 'href' => common_path('favicon.ico', GNUsocial::isHTTPS()))); } if (common_config('site', 'mobile')) { if (is_readable(INSTALLDIR . '/theme/' . common_config('site', 'theme') . '/apple-touch-icon.png')) { $this->element('link', array('rel' => 'apple-touch-icon', - 'href' => Theme::path('apple-touch-icon.png'))); + 'href' => Theme::path('apple-touch-icon.png'))); } else { $this->element('link', array('rel' => 'apple-touch-icon', - 'href' => common_path('apple-touch-icon.png'))); + 'href' => common_path('apple-touch-icon.png'))); } } } @@ -357,9 +688,9 @@ class Action extends HTMLOutputter // lawsuit /** * Show stylesheets * - * @return nothing + * @return void */ - function showStylesheets() + public function showStylesheets() { if (Event::handle('StartShowStyles', array($this))) { @@ -390,7 +721,7 @@ class Action extends HTMLOutputter // lawsuit } } - function primaryCssLink($mainTheme=null, $media=null) + public function primaryCssLink($mainTheme = null, $media = null) { $theme = new Theme($mainTheme); @@ -415,127 +746,20 @@ class Action extends HTMLOutputter // lawsuit } } - /** - * Show javascript headers - * - * @return nothing - */ - function showScripts() - { - if (Event::handle('StartShowScripts', array($this))) { - if (Event::handle('StartShowJQueryScripts', array($this))) { - $this->script('extlib/jquery.js'); - $this->script('extlib/jquery.form.js'); - $this->script('extlib/jquery-ui/jquery-ui.js'); - $this->script('extlib/jquery.cookie.js'); - - Event::handle('EndShowJQueryScripts', array($this)); - } - if (Event::handle('StartShowStatusNetScripts', array($this))) { - $this->script('util.js'); - $this->script('xbImportNode.js'); - - // This route isn't available in single-user mode. - // Not sure why, but it causes errors here. - $this->inlineScript('var _peopletagAC = "' . - common_local_url('peopletagautocomplete') . '";'); - $this->showScriptMessages(); - $this->showScriptVariables(); - // Anti-framing code to avoid clickjacking attacks in older browsers. - // This will show a blank page if the page is being framed, which is - // consistent with the behavior of the 'X-Frame-Options: SAMEORIGIN' - // header, which prevents framing in newer browser. - if (common_config('javascript', 'bustframes')) { - $this->inlineScript('if (window.top !== window.self) { document.write = ""; window.top.location = window.self.location; setTimeout(function () { document.body.innerHTML = ""; }, 1); window.self.onload = function () { document.body.innerHTML = ""; }; }'); - } - Event::handle('EndShowStatusNetScripts', array($this)); - } - Event::handle('EndShowScripts', array($this)); - } - } - - /** - * Exports a map of localized text strings to JavaScript code. - * - * Plugins can add to what's exported by hooking the StartScriptMessages or EndScriptMessages - * events and appending to the array. Try to avoid adding strings that won't be used, as - * they'll be added to HTML output. - */ - function showScriptMessages() - { - $messages = array(); - - if (Event::handle('StartScriptMessages', array($this, &$messages))) { - // Common messages needed for timeline views etc... - - // TRANS: Localized tooltip for '...' expansion button on overlong remote messages. - $messages['showmore_tooltip'] = _m('TOOLTIP', 'Show more'); - $messages['popup_close_button'] = _m('TOOLTIP', 'Close popup'); - - $messages = array_merge($messages, $this->getScriptMessages()); - - Event::handle('EndScriptMessages', array($this, &$messages)); - } - - if (!empty($messages)) { - $this->inlineScript('SN.messages=' . json_encode($messages)); - } - - return $messages; - } - - protected function showScriptVariables() - { - $vars = array(); - - if (Event::handle('StartScriptVariables', array($this, &$vars))) { - $vars['urlNewNotice'] = common_local_url('newnotice'); - $vars['xhrTimeout'] = ini_get('max_execution_time')*1000; // milliseconds - Event::handle('EndScriptVariables', array($this, &$vars)); - } - - $this->inlineScript('SN.V = ' . json_encode($vars) . ';'); - - return $vars; - } - - /** - * If the action will need localizable text strings, export them here like so: - * - * return array('pool_deepend' => _('Deep end'), - * 'pool_shallow' => _('Shallow end')); - * - * The exported map will be available via SN.msg() to JS code: - * - * $('#pool').html('
'); - * $('#pool .deepend').text(SN.msg('pool_deepend')); - * $('#pool .shallow').text(SN.msg('pool_shallow')); - * - * Exports a map of localized text strings to JavaScript code. - * - * Plugins can add to what's exported on any action by hooking the StartScriptMessages or - * EndScriptMessages events and appending to the array. Try to avoid adding strings that won't - * be used, as they'll be added to HTML output. - */ - function getScriptMessages() - { - return array(); - } - /** * Show OpenSearch headers * - * @return nothing + * @return void */ - function showOpenSearch() + public function showOpenSearch() { $this->element('link', array('rel' => 'search', - 'type' => 'application/opensearchdescription+xml', - 'href' => common_local_url('opensearch', array('type' => 'people')), - 'title' => common_config('site', 'name').' People Search')); + 'type' => 'application/opensearchdescription+xml', + 'href' => common_local_url('opensearch', array('type' => 'people')), + 'title' => common_config('site', 'name') . ' People Search')); $this->element('link', array('rel' => 'search', 'type' => 'application/opensearchdescription+xml', - 'href' => common_local_url('opensearch', array('type' => 'notice')), - 'title' => common_config('site', 'name').' Notice Search')); + 'href' => common_local_url('opensearch', array('type' => 'notice')), + 'title' => common_config('site', 'name') . ' Notice Search')); } /** @@ -543,26 +767,38 @@ class Action extends HTMLOutputter // lawsuit * * MAY overload * - * @return nothing + * @return void */ - function showFeeds() + public function showFeeds() { foreach ($this->getFeeds() as $feed) { $this->element('link', array('rel' => $feed->rel(), - 'href' => $feed->url, - 'type' => $feed->mimeType(), - 'title' => $feed->title)); + 'href' => $feed->url, + 'type' => $feed->mimeType(), + 'title' => $feed->title)); } } + /** + * An array of feeds for this action. + * + * Returns an array of potential feeds for this action. + * + * @return array Feed object to show in head and links + */ + public function getFeeds() + { + return []; + } + /** * Show description. * * SHOULD overload * - * @return nothing + * @return void */ - function showDescription() + public function showDescription() { // does nothing by default } @@ -572,9 +808,9 @@ class Action extends HTMLOutputter // lawsuit * * MAY overload * - * @return nothing + * @return void */ - function extraHead() + public function extraHead() { // does nothing by default } @@ -584,9 +820,11 @@ class Action extends HTMLOutputter // lawsuit * * Calls template methods * - * @return nothing + * @return void + * @throws ServerException + * @throws ReflectionException */ - function showBody() + public function showBody() { $params = array('id' => $this->getActionName()); if ($this->scoped instanceof Profile) { @@ -611,14 +849,20 @@ class Action extends HTMLOutputter // lawsuit $this->elementEnd('body'); } + public function getActionName() + { + return $this->action; + } + /** * Show header of the page. * * Calls template methods * - * @return nothing + * @return void + * @throws ServerException */ - function showHeader() + public function showHeader() { $this->elementStart('div', array('id' => 'header')); $this->showLogo(); @@ -635,17 +879,20 @@ class Action extends HTMLOutputter // lawsuit /** * Show configured logo. * - * @return nothing + * @return void + * @throws ServerException */ - function showLogo() + public function showLogo() { $this->elementStart('address', array('id' => 'site_contact', 'class' => 'h-card')); if (Event::handle('StartAddressData', array($this))) { if (common_config('singleuser', 'enabled')) { $user = User::singleUser(); - $url = common_local_url('showstream', - array('nickname' => $user->nickname)); - } else if (common_logged_in()) { + $url = common_local_url( + 'showstream', + array('nickname' => $user->nickname) + ); + } elseif (common_logged_in()) { $cur = common_current_user(); $url = common_local_url('all', array('nickname' => $cur->nickname)); } else { @@ -653,7 +900,7 @@ class Action extends HTMLOutputter // lawsuit } $this->elementStart('a', array('class' => 'home bookmark', - 'href' => $url)); + 'href' => $url)); if (GNUsocial::isHTTPS()) { $logoUrl = common_config('site', 'ssllogo'); @@ -683,8 +930,8 @@ class Action extends HTMLOutputter // lawsuit if (!empty($logoUrl)) { $this->element('img', array('class' => 'logo u-photo p-name', - 'src' => $logoUrl, - 'alt' => common_config('site', 'name'))); + 'src' => $logoUrl, + 'alt' => common_config('site', 'name'))); } $this->elementEnd('a'); @@ -697,9 +944,9 @@ class Action extends HTMLOutputter // lawsuit /** * Show primary navigation. * - * @return nothing + * @return void */ - function showPrimaryNav() + public function showPrimaryNav() { $this->elementStart('div', array('id' => 'site_nav_global_primary')); @@ -718,115 +965,29 @@ class Action extends HTMLOutputter // lawsuit /** * Show site notice. * - * @return nothing + * @return void */ - function showSiteNotice() + public function showSiteNotice() { // Revist. Should probably do an hAtom pattern here $text = common_config('site', 'notice'); if ($text) { $this->elementStart('div', array('id' => 'site_notice', - 'class' => 'system_notice')); + 'class' => 'system_notice')); $this->raw($text); $this->elementEnd('div'); } } - /** - * Show notice form. - * - * MAY overload if no notice form needed... or direct message box???? - * - * @return nothing - */ - function showNoticeForm() - { - // TRANS: Tab on the notice form. - $tabs = array('status' => array('title' => _m('TAB','Status'), - 'href' => common_local_url('newnotice'))); - - $this->elementStart('div', 'input_forms'); - - $this->element('label', array('for'=>'input_form_nav'), _m('TAB', 'Share your:')); - - if (Event::handle('StartShowEntryForms', array(&$tabs))) { - $this->elementStart('ul', array('class' => 'nav', - 'id' => 'input_form_nav')); - - foreach ($tabs as $tag => $data) { - $tag = htmlspecialchars($tag); - $attrs = array('id' => 'input_form_nav_'.$tag, - 'class' => 'input_form_nav_tab'); - - if ($tag == 'status') { - $attrs['class'] .= ' current'; - } - $this->elementStart('li', $attrs); - - $this->element('a', - array('onclick' => 'return SN.U.switchInputFormTab("'.$tag.'");', - 'href' => $data['href']), - $data['title']); - $this->elementEnd('li'); - } - - $this->elementEnd('ul'); - - foreach ($tabs as $tag => $data) { - $attrs = array('class' => 'input_form', - 'id' => 'input_form_'.$tag); - if ($tag == 'status') { - $attrs['class'] .= ' current'; - } - - $this->elementStart('div', $attrs); - - $form = null; - - if (Event::handle('StartMakeEntryForm', array($tag, $this, &$form))) { - if ($tag == 'status') { - $options = $this->noticeFormOptions(); - $form = new NoticeForm($this, $options); - } - Event::handle('EndMakeEntryForm', array($tag, $this, $form)); - } - - if (!empty($form)) { - $form->show(); - } - - $this->elementEnd('div'); - } - } - - $this->elementEnd('div'); - } - - function noticeFormOptions() - { - return array(); - } - - /** - * Show anonymous message. - * - * SHOULD overload - * - * @return nothing - */ - function showAnonymousMessage() - { - // needs to be defined by the class - } - /** * Show core. * * Shows local navigation, content block and aside. * - * @return nothing + * @return void + * @throws ReflectionException */ - function showCore() + public function showCore() { $this->elementStart('div', array('id' => 'core')); $this->elementStart('div', array('id' => 'aside_primary_wrapper')); @@ -856,9 +1017,9 @@ class Action extends HTMLOutputter // lawsuit /** * Show local navigation block. * - * @return nothing + * @return void */ - function showLocalNavBlock() + public function showLocalNavBlock() { // Need to have this ID for CSS; I'm too lazy to add it to // all menus @@ -868,73 +1029,26 @@ class Action extends HTMLOutputter // lawsuit $this->elementEnd('div'); } - /** - * If there's a logged-in user, show a bit of login context - * - * @return nothing - */ - function showProfileBlock() - { - if (common_logged_in()) { - $block = new DefaultProfileBlock($this); - $block->show(); - } - } - /** * Show local navigation. * * SHOULD overload * - * @return nothing + * @return void */ - function showLocalNav() + public function showLocalNav() { $nav = new DefaultLocalNav($this); $nav->show(); } - /** - * Show menu for an object (group, profile) - * - * This block will only show if a subclass has overridden - * the showObjectNav() method. - * - * @return nothing - */ - function showObjectNavBlock() - { - $rmethod = new ReflectionMethod($this, 'showObjectNav'); - $dclass = $rmethod->getDeclaringClass()->getName(); - - if ($dclass != 'Action') { - // Need to have this ID for CSS; I'm too lazy to add it to - // all menus - $this->elementStart('div', array('id' => 'site_nav_object', - 'class' => 'section')); - $this->showObjectNav(); - $this->elementEnd('div'); - } - } - - /** - * Show object navigation. - * - * If there are things to do with this object, show it here. - * - * @return nothing - */ - function showObjectNav() - { - /* Nothing here. */ - } - /** * Show content block. * - * @return nothing + * @return void + * @throws ReflectionException */ - function showContentBlock() + public function showContentBlock() { $this->elementStart('div', array('id' => 'content')); if (common_logged_in()) { @@ -955,12 +1069,89 @@ class Action extends HTMLOutputter // lawsuit $this->elementEnd('div'); } + /** + * Show notice form. + * + * MAY overload if no notice form needed... or direct message box???? + * + * @return void + */ + public function showNoticeForm() + { + // TRANS: Tab on the notice form. + $tabs = array('status' => array('title' => _m('TAB', 'Status'), + 'href' => common_local_url('newnotice'))); + + $this->elementStart('div', 'input_forms'); + + $this->element('label', array('for' => 'input_form_nav'), _m('TAB', 'Share your:')); + + if (Event::handle('StartShowEntryForms', array(&$tabs))) { + $this->elementStart('ul', array('class' => 'nav', + 'id' => 'input_form_nav')); + + foreach ($tabs as $tag => $data) { + $tag = htmlspecialchars($tag); + $attrs = array('id' => 'input_form_nav_' . $tag, + 'class' => 'input_form_nav_tab'); + + if ($tag == 'status') { + $attrs['class'] .= ' current'; + } + $this->elementStart('li', $attrs); + + $this->element( + 'a', + array('onclick' => 'return SN.U.switchInputFormTab("' . $tag . '");', + 'href' => $data['href']), + $data['title'] + ); + $this->elementEnd('li'); + } + + $this->elementEnd('ul'); + + foreach ($tabs as $tag => $data) { + $attrs = array('class' => 'input_form', + 'id' => 'input_form_' . $tag); + if ($tag == 'status') { + $attrs['class'] .= ' current'; + } + + $this->elementStart('div', $attrs); + + $form = null; + + if (Event::handle('StartMakeEntryForm', array($tag, $this, &$form))) { + if ($tag == 'status') { + $options = $this->noticeFormOptions(); + $form = new NoticeForm($this, $options); + } + Event::handle('EndMakeEntryForm', array($tag, $this, $form)); + } + + if (!empty($form)) { + $form->show(); + } + + $this->elementEnd('div'); + } + } + + $this->elementEnd('div'); + } + + public function noticeFormOptions() + { + return []; + } + /** * Show page title. * - * @return nothing + * @return void */ - function showPageTitle() + public function showPageTitle() { $this->element('h1', null, $this->title()); } @@ -974,17 +1165,17 @@ class Action extends HTMLOutputter // lawsuit * 'page_notice' definition list is desired. This is to prevent * empty 'page_notice' definition lists from being output everywhere. * - * @return nothing + * @return void + * @throws ReflectionException */ - function showPageNoticeBlock() + public function showPageNoticeBlock() { $rmethod = new ReflectionMethod($this, 'showPageNotice'); $dclass = $rmethod->getDeclaringClass()->getName(); if ($dclass != 'Action' || Event::hasHandler('StartShowPageNotice')) { - $this->elementStart('div', array('id' => 'page_notice', - 'class' => 'system_notice')); + 'class' => 'system_notice')); if (Event::handle('StartShowPageNotice', array($this))) { $this->showPageNotice(); Event::handle('EndShowPageNotice', array($this)); @@ -998,32 +1189,22 @@ class Action extends HTMLOutputter // lawsuit * * SHOULD overload (unless there's not a notice) * - * @return nothing + * @return void */ - function showPageNotice() - { - } - - /** - * Show content. - * - * MUST overload (unless there's not a notice) - * - * @return nothing - */ - protected function showContent() + public function showPageNotice() { } /** * Show Aside. * - * @return nothing + * @return void + * @throws ReflectionException */ - function showAside() + public function showAside() { $this->elementStart('div', array('id' => 'aside_primary', - 'class' => 'aside')); + 'class' => 'aside')); $this->showProfileBlock(); if (Event::handle('StartShowObjectNavBlock', array($this))) { $this->showObjectNavBlock(); @@ -1040,12 +1221,74 @@ class Action extends HTMLOutputter // lawsuit $this->elementEnd('div'); } + /** + * If there's a logged-in user, show a bit of login context + * + * @return void + * @throws Exception + */ + public function showProfileBlock() + { + if (common_logged_in()) { + $block = new DefaultProfileBlock($this); + $block->show(); + } + } + + /** + * Show menu for an object (group, profile) + * + * This block will only show if a subclass has overridden + * the showObjectNav() method. + * + * @return void + * @throws ReflectionException + */ + public function showObjectNavBlock() + { + $rmethod = new ReflectionMethod($this, 'showObjectNav'); + $dclass = $rmethod->getDeclaringClass()->getName(); + + if ($dclass != 'Action') { + // Need to have this ID for CSS; I'm too lazy to add it to + // all menus + $this->elementStart('div', array('id' => 'site_nav_object', + 'class' => 'section')); + $this->showObjectNav(); + $this->elementEnd('div'); + } + } + + /** + * Show object navigation. + * + * If there are things to do with this object, show it here. + * + * @return void + */ + public function showObjectNav() + { + /* Nothing here. */ + } + + /** + * Show sections. + * + * SHOULD overload + * + * @return void + */ + public function showSections() + { + // for each section, show it + } + /** * Show export data feeds. * * @return void */ - function showExportData() + public function showExportData() { $feeds = $this->getFeeds(); if (!empty($feeds)) { @@ -1054,24 +1297,12 @@ class Action extends HTMLOutputter // lawsuit } } - /** - * Show sections. - * - * SHOULD overload - * - * @return nothing - */ - function showSections() - { - // for each section, show it - } - /** * Show footer. * - * @return nothing + * @return void */ - function showFooter() + public function showFooter() { $this->elementStart('div', array('id' => 'footer')); if (Event::handle('StartShowInsideFooter', array($this))) { @@ -1085,9 +1316,9 @@ class Action extends HTMLOutputter // lawsuit /** * Show secondary navigation. * - * @return nothing + * @return void */ - function showSecondaryNav() + public function showSecondaryNav() { $sn = new SecondaryNav($this); $sn->show(); @@ -1096,9 +1327,9 @@ class Action extends HTMLOutputter // lawsuit /** * Show licenses. * - * @return nothing + * @return void */ - function showLicenses() + public function showLicenses() { $this->showGNUsocialLicense(); $this->showContentLicense(); @@ -1107,9 +1338,9 @@ class Action extends HTMLOutputter // lawsuit /** * Show GNU social license. * - * @return nothing + * @return void */ - function showGNUsocialLicense() + public function showGNUsocialLicense() { if (common_config('site', 'broughtby')) { // TRANS: First sentence of the GNU social site license. Used if 'broughtby' is set. @@ -1135,67 +1366,76 @@ class Action extends HTMLOutputter // lawsuit /** * Show content license. * - * @return nothing + * @return void */ - function showContentLicense() + public function showContentLicense() { if (Event::handle('StartShowContentLicense', array($this))) { switch (common_config('license', 'type')) { - case 'private': - // TRANS: Content license displayed when license is set to 'private'. - // TRANS: %1$s is the site name. - $this->element('p', null, sprintf(_('Content and data of %1$s are private and confidential.'), - common_config('site', 'name'))); + case 'private': + // TRANS: Content license displayed when license is set to 'private'. + // TRANS: %1$s is the site name. + $this->element('p', null, sprintf( + _('Content and data of %1$s are private and confidential.'), + common_config('site', 'name') + )); // fall through - case 'allrightsreserved': - if (common_config('license', 'owner')) { - // TRANS: Content license displayed when license is set to 'allrightsreserved'. - // TRANS: %1$s is the copyright owner. - $this->element('p', null, sprintf(_('Content and data copyright by %1$s. All rights reserved.'), - common_config('license', 'owner'))); - } else { - // TRANS: Content license displayed when license is set to 'allrightsreserved' and no owner is set. - $this->element('p', null, _('Content and data copyright by contributors. All rights reserved.')); - } - break; - case 'cc': // fall through - default: - $this->elementStart('p'); - - $image = common_config('license', 'image'); - $sslimage = common_config('license', 'sslimage'); - - if (GNUsocial::isHTTPS()) { - if (!empty($sslimage)) { - $url = $sslimage; - } else if (preg_match('#^http://i.creativecommons.org/#', $image)) { - // CC support HTTPS on their images - $url = preg_replace('/^http/', 'https', $image, 1); + // no break + case 'allrightsreserved': + if (common_config('license', 'owner')) { + // TRANS: Content license displayed when license is set to 'allrightsreserved'. + // TRANS: %1$s is the copyright owner. + $this->element('p', null, sprintf( + _('Content and data copyright by %1$s. All rights reserved.'), + common_config('license', 'owner') + )); + } else { + // TRANS: Content license displayed when license is set to 'allrightsreserved' and no owner is set. + $this->element('p', null, _('Content and data copyright by contributors. All rights reserved.')); + } + break; + case 'cc': // fall through + default: + $this->elementStart('p'); + + $image = common_config('license', 'image'); + $sslimage = common_config('license', 'sslimage'); + + if (GNUsocial::isHTTPS()) { + if (!empty($sslimage)) { + $url = $sslimage; + } elseif (preg_match('#^http://i.creativecommons.org/#', $image)) { + // CC support HTTPS on their images + $url = preg_replace('/^http/', 'https', $image, 1); + } else { + // Better to show mixed content than no content + $url = $image; + } } else { - // Better to show mixed content than no content $url = $image; } - } else { - $url = $image; - } - $this->element('img', array('id' => 'license_cc', - 'src' => $url, - 'alt' => common_config('license', 'title'), - 'width' => '80', - 'height' => '15')); - $this->text(' '); - // TRANS: license message in footer. - // TRANS: %1$s is the site name, %2$s is a link to the license URL, with a licence name set in configuration. - $notice = _('All %1$s content and data are available under the %2$s license.'); - $link = sprintf('%2$s', - htmlspecialchars(common_config('license', 'url')), - htmlspecialchars(common_config('license', 'title'))); - $this->raw(@sprintf(htmlspecialchars($notice), - htmlspecialchars(common_config('site', 'name')), - $link)); - $this->elementEnd('p'); - break; + $this->element('img', array('id' => 'license_cc', + 'src' => $url, + 'alt' => common_config('license', 'title'), + 'width' => '80', + 'height' => '15')); + $this->text(' '); + // TRANS: license message in footer. + // TRANS: %1$s is the site name, %2$s is a link to the license URL, with a licence name set in configuration. + $notice = _('All %1$s content and data are available under the %2$s license.'); + $link = sprintf( + '%2$s', + htmlspecialchars(common_config('license', 'url')), + htmlspecialchars(common_config('license', 'title')) + ); + $this->raw(@sprintf( + htmlspecialchars($notice), + htmlspecialchars(common_config('site', 'name')), + $link + )); + $this->elementEnd('p'); + break; } Event::handle('EndShowContentLicense', array($this)); @@ -1203,176 +1443,122 @@ class Action extends HTMLOutputter // lawsuit } /** - * Return last modified, if applicable. + * Show javascript headers * - * MAY override - * - * @return string last modified http header + * @return void */ - function lastModified() + public function showScripts() { - // For comparison with If-Last-Modified - // If not applicable, return null - return null; - } + if (Event::handle('StartShowScripts', array($this))) { + if (Event::handle('StartShowJQueryScripts', array($this))) { + $this->script('extlib/jquery.js'); + $this->script('extlib/jquery.form.js'); + $this->script('extlib/jquery-ui/jquery-ui.js'); + $this->script('extlib/jquery.cookie.js'); - /** - * Return etag, if applicable. - * - * MAY override - * - * @return string etag http header - */ - function etag() - { - return null; - } - - /** - * Return true if read only. - * - * MAY override - * - * @param array $args other arguments - * - * @return boolean is read only action? - */ - function isReadOnly($args) - { - return false; - } - - /** - * Returns query argument or default value if not found - * - * @param string $key requested argument - * @param string $def default value to return if $key is not provided - * - * @return boolean is read only action? - */ - function arg($key, $def=null) - { - if (array_key_exists($key, $this->args)) { - return $this->args[$key]; - } else { - return $def; - } - } - - /** - * Returns trimmed query argument or default value if not found - * - * @param string $key requested argument - * @param string $def default value to return if $key is not provided - * - * @return boolean is read only action? - */ - function trimmed($key, $def=null) - { - $arg = $this->arg($key, $def); - return is_string($arg) ? trim($arg) : $arg; - } - - /** - * Handler method - * - * @return boolean is read only action? - */ - protected function handle() - { - header('Vary: Accept-Encoding,Cookie'); - - $lm = $this->lastModified(); - $etag = $this->etag(); - - if ($etag) { - header('ETag: ' . $etag); - } - - if ($lm) { - header('Last-Modified: ' . date(DATE_RFC1123, $lm)); - if ($this->isCacheable()) { - header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' ); - header( "Cache-Control: private, must-revalidate, max-age=0" ); - header( "Pragma:"); + Event::handle('EndShowJQueryScripts', array($this)); } - } + if (Event::handle('StartShowStatusNetScripts', array($this))) { + $this->script('util.js'); + $this->script('xbImportNode.js'); - $checked = false; - if ($etag) { - $if_none_match = (array_key_exists('HTTP_IF_NONE_MATCH', $_SERVER)) ? - $_SERVER['HTTP_IF_NONE_MATCH'] : null; - if ($if_none_match) { - // If this check fails, ignore the if-modified-since below. - $checked = true; - if ($this->_hasEtag($etag, $if_none_match)) { - header('HTTP/1.1 304 Not Modified'); - // Better way to do this? - exit(0); + // This route isn't available in single-user mode. + // Not sure why, but it causes errors here. + $this->inlineScript('var _peopletagAC = "' . + common_local_url('peopletagautocomplete') . '";'); + $this->showScriptMessages(); + $this->showScriptVariables(); + // Anti-framing code to avoid clickjacking attacks in older browsers. + // This will show a blank page if the page is being framed, which is + // consistent with the behavior of the 'X-Frame-Options: SAMEORIGIN' + // header, which prevents framing in newer browser. + if (common_config('javascript', 'bustframes')) { + $this->inlineScript('if (window.top !== window.self) { document.write = ""; window.top.location = window.self.location; setTimeout(function () { document.body.innerHTML = ""; }, 1); window.self.onload = function () { document.body.innerHTML = ""; }; }'); } + Event::handle('EndShowStatusNetScripts', array($this)); } - } - - if (!$checked && $lm && array_key_exists('HTTP_IF_MODIFIED_SINCE', $_SERVER)) { - $if_modified_since = $_SERVER['HTTP_IF_MODIFIED_SINCE']; - $ims = strtotime($if_modified_since); - if ($lm <= $ims) { - header('HTTP/1.1 304 Not Modified'); - // Better way to do this? - exit(0); - } + Event::handle('EndShowScripts', array($this)); } } /** - * Is this action cacheable? + * Exports a map of localized text strings to JavaScript code. * - * If the action returns a last-modified - * - * @param array $argarray is ignored since it's now passed in in prepare() - * - * @return boolean is read only action? + * Plugins can add to what's exported by hooking the StartScriptMessages or EndScriptMessages + * events and appending to the array. Try to avoid adding strings that won't be used, as + * they'll be added to HTML output. */ - function isCacheable() + public function showScriptMessages() { - return true; - } + $messages = []; - /** - * HasĀ etag? (private) - * - * @param string $etag etag http header - * @param string $if_none_match ifNoneMatch http header - * - * @return boolean - */ - function _hasEtag($etag, $if_none_match) - { - $etags = explode(',', $if_none_match); - return in_array($etag, $etags) || in_array('*', $etags); - } + if (Event::handle('StartScriptMessages', array($this, &$messages))) { + // Common messages needed for timeline views etc... - /** - * Boolean understands english (yes, no, true, false) - * - * @param string $key query key we're interested in - * @param string $def default value - * - * @return boolean interprets yes/no strings as boolean - */ - function boolean($key, $def=false) - { - $arg = strtolower($this->trimmed($key)); + // TRANS: Localized tooltip for '...' expansion button on overlong remote messages. + $messages['showmore_tooltip'] = _m('TOOLTIP', 'Show more'); + $messages['popup_close_button'] = _m('TOOLTIP', 'Close popup'); - if (is_null($arg)) { - return $def; - } else if (in_array($arg, array('true', 'yes', '1', 'on'))) { - return true; - } else if (in_array($arg, array('false', 'no', '0'))) { - return false; - } else { - return $def; + $messages = array_merge($messages, $this->getScriptMessages()); + + Event::handle('EndScriptMessages', array($this, &$messages)); } + + if (!empty($messages)) { + $this->inlineScript('SN.messages=' . json_encode($messages)); + } + + return $messages; + } + + /** + * If the action will need localizable text strings, export them here like so: + * + * return array('pool_deepend' => _('Deep end'), + * 'pool_shallow' => _('Shallow end')); + * + * The exported map will be available via SN.msg() to JS code: + * + * $('#pool').html('
'); + * $('#pool .deepend').text(SN.msg('pool_deepend')); + * $('#pool .shallow').text(SN.msg('pool_shallow')); + * + * Exports a map of localized text strings to JavaScript code. + * + * Plugins can add to what's exported on any action by hooking the StartScriptMessages or + * EndScriptMessages events and appending to the array. Try to avoid adding strings that won't + * be used, as they'll be added to HTML output. + */ + public function getScriptMessages() + { + return []; + } + + protected function showScriptVariables() + { + $vars = []; + + if (Event::handle('StartScriptVariables', array($this, &$vars))) { + $vars['urlNewNotice'] = common_local_url('newnotice'); + $vars['xhrTimeout'] = ini_get('max_execution_time') * 1000; // milliseconds + Event::handle('EndScriptVariables', array($this, &$vars)); + } + + $this->inlineScript('SN.V = ' . json_encode($vars) . ';'); + + return $vars; + } + + /** + * Show anonymous message. + * + * SHOULD overload + * + * @return void + */ + public function showAnonymousMessage() + { + // needs to be defined by the class } /** @@ -1383,7 +1569,7 @@ class Action extends HTMLOutputter // lawsuit * Upstream bug is:: * https://pear.php.net/bugs/bug.php?id=20291 */ - function booleanintstring($key, $def=false) + public function booleanintstring($key, $def = false) { return $this->boolean($key, $def) ? '1' : '0'; } @@ -1391,14 +1577,14 @@ class Action extends HTMLOutputter // lawsuit /** * Integer value of an argument * - * @param string $key query key we're interested in + * @param string $key query key we're interested in * @param string $defValue optional default value (default null) * @param string $maxValue optional max value (default null) * @param string $minValue optional min value (default null) * * @return integer integer value */ - function int($key, $defValue=null, $maxValue=null, $minValue=null) + public function int($key, $defValue = null, $maxValue = null, $minValue = null) { $arg = intval($this->arg($key)); @@ -1417,155 +1603,40 @@ class Action extends HTMLOutputter // lawsuit return $arg; } - /** - * Server error - * - * @param string $msg error message to display - * @param integer $code http error code, 500 by default - * - * @return nothing - */ - function serverError($msg, $code=500, $format=null) - { - if ($format === null) { - $format = $this->format; - } - - common_debug("Server error '{$code}' on '{$this->action}': {$msg}", __FILE__); - - if (!array_key_exists($code, ServerErrorAction::$status)) { - $code = 500; - } - - $status_string = ServerErrorAction::$status[$code]; - - switch ($format) { - case 'xml': - header("HTTP/1.1 {$code} {$status_string}"); - $this->initDocument('xml'); - $this->elementStart('hash'); - $this->element('error', null, $msg); - $this->element('request', null, $_SERVER['REQUEST_URI']); - $this->elementEnd('hash'); - $this->endDocument('xml'); - break; - case 'json': - if (!isset($this->callback)) { - header("HTTP/1.1 {$code} {$status_string}"); - } - $this->initDocument('json'); - $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']); - print(json_encode($error_array)); - $this->endDocument('json'); - break; - default: - common_log(LOG_ERR, 'Handled serverError ('._ve($code).') but cannot output into desired format ('._ve($this->format).'): '._ve($msg)); - $action = new ServerErrorAction($msg, $code); - $action->execute(); - } - - exit((int)$code); - } - - /** - * Client error - * - * @param string $msg error message to display - * @param integer $code http error code, 400 by default - * @param string $format error format (json, xml, text) for ApiAction - * - * @return nothing - * @throws ClientException always - */ - function clientError($msg, $code=400, $format=null) - { - // $format is currently only relevant for an ApiAction anyway - if ($format === null) { - $format = $this->format; - } - - common_debug("User error '{$code}' on '{$this->action}': {$msg}", __FILE__); - - if (!array_key_exists($code, ClientErrorAction::$status)) { - $code = 400; - } - - $status_string = ClientErrorAction::$status[$code]; - - switch ($format) { - case 'xml': - header("HTTP/1.1 {$code} {$status_string}"); - $this->initDocument('xml'); - $this->elementStart('hash'); - $this->element('error', null, $msg); - $this->element('request', null, $_SERVER['REQUEST_URI']); - $this->elementEnd('hash'); - $this->endDocument('xml'); - break; - case 'json': - if (!isset($this->callback)) { - header("HTTP/1.1 {$code} {$status_string}"); - } - $this->initDocument('json'); - $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']); - print(json_encode($error_array)); - $this->endDocument('json'); - break; - case 'text': - header("HTTP/1.1 {$code} {$status_string}"); - header('Content-Type: text/plain; charset=utf-8'); - echo $msg; - break; - default: - common_log(LOG_ERR, 'Handled clientError ('._ve($code).') but cannot output into desired format ('._ve($this->format).'): '._ve($msg)); - $action = new ClientErrorAction($msg, $code); - $action->execute(); - } - exit((int)$code); - } - - /** - * If not logged in, take appropriate action (redir or exception) - * - * @param boolean $redir Redirect to login if not logged in - * - * @return boolean true if logged in (never returns if not) - */ - public function checkLogin($redir=true) - { - if (common_logged_in()) { - return true; - } - - if ($redir==true) { - common_set_returnto($_SERVER['REQUEST_URI']); - common_redirect(common_local_url('login')); - } - - // TRANS: Error message displayed when trying to perform an action that requires a logged in user. - $this->clientError(_('Not logged in.'), 403); - } - /** * Returns the current URL * * @return string current URL */ - function selfUrl() + public function selfUrl() { list($action, $args) = $this->returnToArgs(); return common_local_url($action, $args); } + /** + * Generate pagination links + * + * @param boolean $have_before is there something before? + * @param boolean $have_after is there something after? + * @param integer $page current page + * @param string $action current action + * @param array $args rest of query arguments + * + * @return void + */ + // XXX: The messages in this pagination method only tailor to navigating + // notices. In other lists, "Previous"/"Next" type navigation is + // desirable, but not available. /** * Returns arguments sufficient for re-constructing URL * * @return array two elements: action, other args */ - function returnToArgs() + public function returnToArgs() { $action = $this->getActionName(); - $args = $this->args; + $args = $this->args; unset($args['action']); if (common_config('site', 'fancy')) { unset($args['p']); @@ -1582,20 +1653,20 @@ class Action extends HTMLOutputter // lawsuit /** * Generate a menu item * - * @param string $url menu URL - * @param string $text menu name - * @param string $title title attribute, null by default + * @param string $url menu URL + * @param string $text menu name + * @param string $title title attribute, null by default * @param boolean $is_selected current menu item, false by default - * @param string $id element id, null by default + * @param string $id element id, null by default * - * @return nothing + * @return void */ - function menuItem($url, $text, $title=null, $is_selected=false, $id=null, $class=null) + public function menuItem($url, $text, $title = null, $is_selected = false, $id = null, $class = null) { // Added @id to li for some control. // XXX: We might want to move this to htmloutputter.php - $lattrs = array(); - $classes = array(); + $lattrs = []; + $classes = []; if ($class !== null) { $classes[] = trim($class); } @@ -1620,64 +1691,6 @@ class Action extends HTMLOutputter // lawsuit $this->elementEnd('li'); } - /** - * Generate pagination links - * - * @param boolean $have_before is there something before? - * @param boolean $have_after is there something after? - * @param integer $page current page - * @param string $action current action - * @param array $args rest of query arguments - * - * @return nothing - */ - // XXX: The messages in this pagination method only tailor to navigating - // notices. In other lists, "Previous"/"Next" type navigation is - // desirable, but not available. - function pagination($have_before, $have_after, $page, $action, $args=null) - { - // Does a little before-after block for next/prev page - if ($have_before || $have_after) { - $this->elementStart('ul', array('class' => 'nav', - 'id' => 'pagination')); - } - if ($have_before) { - $pargs = array('page' => $page-1); - $this->elementStart('li', array('class' => 'nav_prev')); - $this->element('a', array('href' => common_local_url($action, $args, $pargs), - 'rel' => 'prev'), - // TRANS: Pagination message to go to a page displaying information more in the - // TRANS: present than the currently displayed information. - _('After')); - $this->elementEnd('li'); - } - if ($have_after) { - $pargs = array('page' => $page+1); - $this->elementStart('li', array('class' => 'nav_next')); - $this->element('a', array('href' => common_local_url($action, $args, $pargs), - 'rel' => 'next'), - // TRANS: Pagination message to go to a page displaying information more in the - // TRANS: past than the currently displayed information. - _('Before')); - $this->elementEnd('li'); - } - if ($have_before || $have_after) { - $this->elementEnd('ul'); - } - } - - /** - * An array of feeds for this action. - * - * Returns an array of potential feeds for this action. - * - * @return array Feed object to show in head and links - */ - function getFeeds() - { - return array(); - } - /** * Check the session token. * @@ -1688,7 +1701,47 @@ class Action extends HTMLOutputter // lawsuit */ // XXX: Finding this type of check with the same message about 50 times. // Possible to refactor? - function checkSessionToken() + + public function pagination($have_before, $have_after, $page, $action, $args = null) + { + // Does a little before-after block for next/prev page + if ($have_before || $have_after) { + $this->elementStart('ul', array('class' => 'nav', + 'id' => 'pagination')); + } + if ($have_before) { + $pargs = array('page' => $page - 1); + $this->elementStart('li', array('class' => 'nav_prev')); + $this->element( + 'a', + array('href' => common_local_url($action, $args, $pargs), + 'rel' => 'prev'), + // TRANS: Pagination message to go to a page displaying information more in the + // TRANS: present than the currently displayed information. + _('After') + ); + $this->elementEnd('li'); + } + if ($have_after) { + $pargs = array('page' => $page + 1); + $this->elementStart('li', array('class' => 'nav_next')); + $this->element( + 'a', + array('href' => common_local_url($action, $args, $pargs), + 'rel' => 'next'), + // TRANS: Pagination message to go to a page displaying information more in the + // TRANS: past than the currently displayed information. + _('Before') + ); + $this->elementEnd('li'); + } + if ($have_before || $have_after) { + $this->elementEnd('ul'); + } + } + + + public function checkSessionToken() { // CSRF protection $token = $this->trimmed('token'); @@ -1697,15 +1750,4 @@ class Action extends HTMLOutputter // lawsuit $this->clientError(_('There was a problem with your session token.')); } } - - /** - * Check if the current request is a POST - * - * @return boolean true if POST; otherwise false. - */ - - function isPost() - { - return ($_SERVER['REQUEST_METHOD'] == 'POST'); - } } diff --git a/lib/activityobject.php b/lib/activityobject.php index 597792ffde..7a68b20166 100644 --- a/lib/activityobject.php +++ b/lib/activityobject.php @@ -28,9 +28,11 @@ * @link http://status.net/ */ -if (!defined('GNUSOCIAL')) { exit(1); } +if (!defined('GNUSOCIAL')) { + exit(1); +} -require_once(INSTALLDIR.'/lib/activitystreamjsondocument.php'); +require_once(INSTALLDIR . '/lib/activitystreamjsondocument.php'); /** * A noun-ish thing in the activity universe @@ -51,47 +53,47 @@ require_once(INSTALLDIR.'/lib/activitystreamjsondocument.php'); */ class ActivityObject { - const ARTICLE = 'http://activitystrea.ms/schema/1.0/article'; - const BLOGENTRY = 'http://activitystrea.ms/schema/1.0/blog-entry'; - const NOTE = 'http://activitystrea.ms/schema/1.0/note'; - const STATUS = 'http://activitystrea.ms/schema/1.0/status'; - const FILE = 'http://activitystrea.ms/schema/1.0/file'; - const PHOTO = 'http://activitystrea.ms/schema/1.0/photo'; - const ALBUM = 'http://activitystrea.ms/schema/1.0/photo-album'; - const PLAYLIST = 'http://activitystrea.ms/schema/1.0/playlist'; - const VIDEO = 'http://activitystrea.ms/schema/1.0/video'; - const AUDIO = 'http://activitystrea.ms/schema/1.0/audio'; - const BOOKMARK = 'http://activitystrea.ms/schema/1.0/bookmark'; - const PERSON = 'http://activitystrea.ms/schema/1.0/person'; - const GROUP = 'http://activitystrea.ms/schema/1.0/group'; - const _LIST = 'http://activitystrea.ms/schema/1.0/list'; // LIST is reserved - const PLACE = 'http://activitystrea.ms/schema/1.0/place'; - const COMMENT = 'http://activitystrea.ms/schema/1.0/comment'; + const ARTICLE = 'http://activitystrea.ms/schema/1.0/article'; + const BLOGENTRY = 'http://activitystrea.ms/schema/1.0/blog-entry'; + const NOTE = 'http://activitystrea.ms/schema/1.0/note'; + const STATUS = 'http://activitystrea.ms/schema/1.0/status'; + const FILE = 'http://activitystrea.ms/schema/1.0/file'; + const PHOTO = 'http://activitystrea.ms/schema/1.0/photo'; + const ALBUM = 'http://activitystrea.ms/schema/1.0/photo-album'; + const PLAYLIST = 'http://activitystrea.ms/schema/1.0/playlist'; + const VIDEO = 'http://activitystrea.ms/schema/1.0/video'; + const AUDIO = 'http://activitystrea.ms/schema/1.0/audio'; + const BOOKMARK = 'http://activitystrea.ms/schema/1.0/bookmark'; + const PERSON = 'http://activitystrea.ms/schema/1.0/person'; + const GROUP = 'http://activitystrea.ms/schema/1.0/group'; + const _LIST = 'http://activitystrea.ms/schema/1.0/list'; // LIST is reserved + const PLACE = 'http://activitystrea.ms/schema/1.0/place'; + const COMMENT = 'http://activitystrea.ms/schema/1.0/comment'; // ^^^^^^^^^^ tea! - const ACTIVITY = 'http://activitystrea.ms/schema/1.0/activity'; - const SERVICE = 'http://activitystrea.ms/schema/1.0/service'; - const IMAGE = 'http://activitystrea.ms/schema/1.0/image'; - const COLLECTION = 'http://activitystrea.ms/schema/1.0/collection'; + const ACTIVITY = 'http://activitystrea.ms/schema/1.0/activity'; + const SERVICE = 'http://activitystrea.ms/schema/1.0/service'; + const IMAGE = 'http://activitystrea.ms/schema/1.0/image'; + const COLLECTION = 'http://activitystrea.ms/schema/1.0/collection'; const APPLICATION = 'http://activitystrea.ms/schema/1.0/application'; // Atom elements we snarf - const TITLE = 'title'; + const TITLE = 'title'; const SUMMARY = 'summary'; - const ID = 'id'; - const SOURCE = 'source'; + const ID = 'id'; + const SOURCE = 'source'; - const NAME = 'name'; - const URI = 'uri'; + const NAME = 'name'; + const URI = 'uri'; const EMAIL = 'email'; - const POSTEROUS = 'http://posterous.com/help/rss/1.0'; - const AUTHOR = 'author'; - const USERIMAGE = 'userImage'; - const PROFILEURL = 'profileUrl'; - const NICKNAME = 'nickName'; + const POSTEROUS = 'http://posterous.com/help/rss/1.0'; + const AUTHOR = 'author'; + const USERIMAGE = 'userImage'; + const PROFILEURL = 'profileUrl'; + const NICKNAME = 'nickName'; const DISPLAYNAME = 'displayName'; - + const MEDIA_DESCRIPTION = 'description'; public $element; public $type; public $id; @@ -99,21 +101,19 @@ class ActivityObject public $summary; public $content; public $owner; - public $link; - public $selfLink; // think APP (Atom Publishing Protocol) + public $link; // think APP (Atom Publishing Protocol) + public $selfLink; public $source; - public $avatarLinks = array(); + public $avatarLinks = []; public $geopoint; public $poco; - public $displayName; // @todo move this stuff to it's own PHOTO activity object - const MEDIA_DESCRIPTION = 'description'; - + public $displayName; public $thumbnail; public $largerImage; public $description; - public $extra = array(); + public $extra = []; public $stream; @@ -126,7 +126,7 @@ class ActivityObject * * @param DOMElement $element DOM thing to turn into an Activity thing */ - function __construct($element = null) + public function __construct($element = null) { if (empty($element)) { return; @@ -142,7 +142,7 @@ class ActivityObject if ($element->tagName == 'author') { $this->_fromAuthor($element); - } else if ($element->tagName == 'item') { + } elseif ($element->tagName == 'item') { $this->_fromRssItem($element); } else { $this->_fromAtomEntry($element); @@ -168,8 +168,7 @@ class ActivityObject } if ($this->type == self::PHOTO) { - - $this->thumbnail = ActivityUtils::getLink($element, 'preview'); + $this->thumbnail = ActivityUtils::getLink($element, 'preview'); $this->largerImage = ActivityUtils::getLink($element, 'enclosure'); $this->description = ActivityUtils::childContent( @@ -184,11 +183,18 @@ class ActivityObject } } + private function _childContent($element, $tag, $namespace = ActivityUtils::ATOM) + { + return ActivityUtils::childContent($element, $tag, $namespace); + } + private function _fromAuthor($element) { - $this->type = $this->_childContent($element, - Activity::OBJECTTYPE, - Activity::SPEC); + $this->type = $this->_childContent( + $element, + Activity::OBJECTTYPE, + Activity::SPEC + ); if (empty($this->type)) { $this->type = self::PERSON; // XXX: is this fair? @@ -231,7 +237,7 @@ class ActivityObject $email = $this->_childContent($element, self::EMAIL); if (!empty($email)) { // XXX: acct: ? - $this->id = 'mailto:'.$email; + $this->id = 'mailto:' . $email; } } @@ -244,50 +250,8 @@ class ActivityObject } } - private function _fromAtomEntry($element) - { - $this->type = $this->_childContent($element, Activity::OBJECTTYPE, - Activity::SPEC); - - if (empty($this->type)) { - $this->type = ActivityObject::NOTE; - } - - $this->summary = ActivityUtils::childHtmlContent($element, self::SUMMARY); - $this->content = ActivityUtils::getContent($element); - - // We don't like HTML in our titles, although it's technically allowed - $this->title = common_strip_html(ActivityUtils::childHtmlContent($element, self::TITLE)); - - $this->source = $this->_getSource($element); - - $this->link = ActivityUtils::getPermalink($element); - $this->selfLink = ActivityUtils::getSelfLink($element); - - $this->id = $this->_childContent($element, self::ID); - - if (empty($this->id) && !empty($this->link)) { // fallback if there's no ID - $this->id = $this->link; - } - - $els = $element->childNodes; - $out = array(); - - for ($i = 0; $i < $els->length; $i++) { - $link = $els->item($i); - if ($link->localName == ActivityUtils::LINK && $link->namespaceURI == ActivityUtils::ATOM) { - $attrs = array(); - foreach ($link->attributes as $attrName=>$attrNode) { - $attrs[$attrName] = $attrNode->nodeValue; - } - $this->extra[] = [$link->localName, - $attrs, - $link->nodeValue]; - } - } - } - // @todo FIXME: rationalize with Activity::_fromRssItem() + private function _fromRssItem($item) { if (empty($this->type)) { @@ -321,6 +285,67 @@ class ActivityObject } } + private function _fromAtomEntry($element) + { + $this->type = $this->_childContent( + $element, + Activity::OBJECTTYPE, + Activity::SPEC + ); + + if (empty($this->type)) { + $this->type = ActivityObject::NOTE; + } + + $this->summary = ActivityUtils::childHtmlContent($element, self::SUMMARY); + $this->content = ActivityUtils::getContent($element); + + // We don't like HTML in our titles, although it's technically allowed + $this->title = common_strip_html(ActivityUtils::childHtmlContent($element, self::TITLE)); + + $this->source = $this->_getSource($element); + + $this->link = ActivityUtils::getPermalink($element); + $this->selfLink = ActivityUtils::getSelfLink($element); + + $this->id = $this->_childContent($element, self::ID); + + if (empty($this->id) && !empty($this->link)) { // fallback if there's no ID + $this->id = $this->link; + } + + $els = $element->childNodes; + + for ($i = 0; $i < $els->length; $i++) { + $link = $els->item($i); + if ($link->localName == ActivityUtils::LINK && $link->namespaceURI == ActivityUtils::ATOM) { + $attrs = []; + foreach ($link->attributes as $attrName => $attrNode) { + $attrs[$attrName] = $attrNode->nodeValue; + } + $this->extra[] = [$link->localName, + $attrs, + $link->nodeValue]; + } + } + } + + private function _getSource($element) + { + $sourceEl = ActivityUtils::child($element, 'source'); + + if (empty($sourceEl)) { + return null; + } else { + $href = ActivityUtils::getLink($sourceEl, 'self'); + if (!empty($href)) { + return $href; + } else { + return ActivityUtils::childContent($sourceEl, 'id'); + } + } + } + public static function fromRssAuthor($el) { $text = $el->textContent; @@ -328,10 +353,10 @@ class ActivityObject if (preg_match('/^(.*?) \((.*)\)$/', $text, $match)) { $email = $match[1]; $name = $match[2]; - } else if (preg_match('/^(.*?) <(.*)>$/', $text, $match)) { + } elseif (preg_match('/^(.*?) <(.*)>$/', $text, $match)) { $name = $match[1]; $email = $match[2]; - } else if (preg_match('/.*@.*/', $text)) { + } elseif (preg_match('/.*@.*/', $text)) { $email = $text; $name = null; } else { @@ -345,11 +370,11 @@ class ActivityObject $obj->element = $el; - $obj->type = ActivityObject::PERSON; + $obj->type = ActivityObject::PERSON; $obj->title = $name; if (!empty($email)) { - $obj->id = 'mailto:'.$email; + $obj->id = 'mailto:' . $email; } return $obj; @@ -366,7 +391,7 @@ class ActivityObject $obj->element = $el; $obj->title = $text; - $obj->type = ActivityObject::PERSON; + $obj->type = ActivityObject::PERSON; return $obj; } @@ -380,8 +405,8 @@ class ActivityObject $obj->type = ActivityObject::PERSON; // @fixme guess better $obj->title = ActivityUtils::childContent($el, ActivityObject::TITLE, Activity::RSS); - $obj->link = ActivityUtils::childContent($el, ActivityUtils::LINK, Activity::RSS); - $obj->id = ActivityUtils::getLink($el, Activity::SELF); + $obj->link = ActivityUtils::childContent($el, ActivityUtils::LINK, Activity::RSS); + $obj->id = ActivityUtils::getLink($el, Activity::SELF); if (empty($obj->id)) { $obj->id = $obj->link; @@ -405,6 +430,8 @@ class ActivityObject return $obj; } + // Try to get a unique id for the source feed + public static function fromPosterousAuthor($el) { $obj = new ActivityObject(); @@ -420,93 +447,74 @@ class ActivityObject } $obj->link = ActivityUtils::childContent($el, self::PROFILEURL, self::POSTEROUS); - $obj->id = $obj->link; + $obj->id = $obj->link; $obj->poco = new PoCo(); $obj->poco->preferredUsername = ActivityUtils::childContent($el, self::NICKNAME, self::POSTEROUS); - $obj->poco->displayName = ActivityUtils::childContent($el, self::DISPLAYNAME, self::POSTEROUS); + $obj->poco->displayName = ActivityUtils::childContent($el, self::DISPLAYNAME, self::POSTEROUS); $obj->title = $obj->poco->displayName; return $obj; } - private function _childContent($element, $tag, $namespace=ActivityUtils::ATOM) - { - return ActivityUtils::childContent($element, $tag, $namespace); - } - - // Try to get a unique id for the source feed - - private function _getSource($element) - { - $sourceEl = ActivityUtils::child($element, 'source'); - - if (empty($sourceEl)) { - return null; - } else { - $href = ActivityUtils::getLink($sourceEl, 'self'); - if (!empty($href)) { - return $href; - } else { - return ActivityUtils::childContent($sourceEl, 'id'); - } - } - } - - static function fromGroup(User_group $group) + public static function fromGroup(User_group $group) { $object = new ActivityObject(); - if (Event::handle('StartActivityObjectFromGroup', array($group, &$object))) { + if (Event::handle('StartActivityObjectFromGroup', [$group, &$object])) { + $object->type = ActivityObject::GROUP; + $object->id = $group->getUri(); + $object->title = $group->getBestName(); + $object->link = $group->getUri(); - $object->type = ActivityObject::GROUP; - $object->id = $group->getUri(); - $object->title = $group->getBestName(); - $object->link = $group->getUri(); + $object->avatarLinks[] = AvatarLink::fromFilename( + $group->homepage_logo, + AVATAR_PROFILE_SIZE + ); - $object->avatarLinks[] = AvatarLink::fromFilename($group->homepage_logo, - AVATAR_PROFILE_SIZE); + $object->avatarLinks[] = AvatarLink::fromFilename( + $group->stream_logo, + AVATAR_STREAM_SIZE + ); - $object->avatarLinks[] = AvatarLink::fromFilename($group->stream_logo, - AVATAR_STREAM_SIZE); - - $object->avatarLinks[] = AvatarLink::fromFilename($group->mini_logo, - AVATAR_MINI_SIZE); + $object->avatarLinks[] = AvatarLink::fromFilename( + $group->mini_logo, + AVATAR_MINI_SIZE + ); $object->poco = PoCo::fromGroup($group); - Event::handle('EndActivityObjectFromGroup', array($group, &$object)); + Event::handle('EndActivityObjectFromGroup', [$group, &$object]); } return $object; } - static function fromPeopletag($ptag) + public static function fromPeopletag($ptag) { $object = new ActivityObject(); - if (Event::handle('StartActivityObjectFromPeopletag', array($ptag, &$object))) { - $object->type = ActivityObject::_LIST; + if (Event::handle('StartActivityObjectFromPeopletag', [$ptag, &$object])) { + $object->type = ActivityObject::_LIST; - $object->id = $ptag->getUri(); - $object->title = $ptag->tag; + $object->id = $ptag->getUri(); + $object->title = $ptag->tag; $object->summary = $ptag->description; - $object->link = $ptag->homeUrl(); - $object->owner = Profile::getKV('id', $ptag->tagger); - $object->poco = PoCo::fromProfile($object->owner); - Event::handle('EndActivityObjectFromPeopletag', array($ptag, &$object)); + $object->link = $ptag->homeUrl(); + $object->owner = Profile::getKV('id', $ptag->tagger); + $object->poco = PoCo::fromProfile($object->owner); + Event::handle('EndActivityObjectFromPeopletag', [$ptag, &$object]); } return $object; } - static function fromFile(File $file) + public static function fromFile(File $file) { $object = new ActivityObject(); - if (Event::handle('StartActivityObjectFromFile', array($file, &$object))) { - + if (Event::handle('StartActivityObjectFromFile', [$file, &$object])) { $object->type = self::mimeTypeToObjectType($file->mimetype); - $object->id = TagURI::mint(sprintf("file:%d", $file->id)); + $object->id = TagURI::mint(sprintf("file:%d", $file->id)); $object->link = $file->getAttachmentUrl(); if ($file->title) { @@ -527,39 +535,73 @@ class ActivityObject } switch (self::canonicalType($object->type)) { - case 'image': - $object->largerImage = $file->getUrl(); - break; - case 'video': - case 'audio': - $object->stream = $file->getUrl(); - break; + case 'image': + $object->largerImage = $file->getUrl(); + break; + case 'video': + case 'audio': + $object->stream = $file->getUrl(); + break; } - Event::handle('EndActivityObjectFromFile', array($file, &$object)); + Event::handle('EndActivityObjectFromFile', [$file, &$object]); } return $object; } - static function fromNoticeSource(Notice_source $source) + public static function mimeTypeToObjectType($mimeType) + { + $ot = null; + + // Default + + if (empty($mimeType)) { + return self::FILE; + } + + $parts = explode('/', $mimeType); + + switch ($parts[0]) { + case 'image': + $ot = self::IMAGE; + break; + case 'audio': + $ot = self::AUDIO; + break; + case 'video': + $ot = self::VIDEO; + break; + default: + $ot = self::FILE; + } + + return $ot; + } + + public static function canonicalType($type) + { + return ActivityUtils::resolveUri($type, true); + } + + public static function fromNoticeSource(Notice_source $source) { $object = new ActivityObject(); - $wellKnown = array('web', 'xmpp', 'mail', 'omb', 'system', 'api', 'ostatus', - 'activity', 'feed', 'mirror', 'twitter', 'facebook'); + $wellKnown = ['web', 'xmpp', 'mail', 'omb', 'system', 'api', 'ostatus', + 'activity', 'feed', 'mirror', 'twitter', 'facebook']; - if (Event::handle('StartActivityObjectFromNoticeSource', array($source, &$object))) { + if (Event::handle('StartActivityObjectFromNoticeSource', [$source, &$object])) { $object->type = ActivityObject::APPLICATION; if (in_array($source->code, $wellKnown)) { // We use one ID for all well-known StatusNet sources - $object->id = "tag:status.net,2009:notice-source:".$source->code; - } else if ($source->url) { + $object->id = "tag:status.net,2009:notice-source:" . $source->code; + } elseif ($source->url) { // They registered with an URL $object->id = $source->url; } else { // Locally-registered, no URL - $object->id = TagURI::mint("notice-source:".$source->code); + $object->id = TagURI::mint("notice-source:" . $source->code); } if ($source->url) { @@ -575,47 +617,62 @@ class ActivityObject if ($source->created) { $object->date = $source->created; } - - $object->extra[] = array('status_net', array('source_code' => $source->code)); - Event::handle('EndActivityObjectFromNoticeSource', array($source, &$object)); + $object->extra[] = ['status_net', ['source_code' => $source->code]]; + + Event::handle('EndActivityObjectFromNoticeSource', [$source, &$object]); } return $object; } - static function fromMessage(Message $message) + public static function fromMessage(Message $message) { $object = new ActivityObject(); - if (Event::handle('StartActivityObjectFromMessage', array($message, &$object))) { - - $object->type = ActivityObject::NOTE; - $object->id = ($message->uri) ? $message->uri : (($message->url) ? $message->url : TagURI::mint(sprintf("message:%d", $message->id))); + if (Event::handle('StartActivityObjectFromMessage', [$message, &$object])) { + $object->type = ActivityObject::NOTE; + $object->id = ($message->uri) ? $message->uri : (($message->url) ? $message->url : TagURI::mint(sprintf("message:%d", $message->id))); $object->content = $message->rendered; - $object->date = $message->created; + $object->date = $message->created; if ($message->url) { $object->link = $message->url; } else { - $object->link = common_local_url('showmessage', array('message' => $message->id)); + $object->link = common_local_url('showmessage', ['message' => $message->id]); } - $object->extra[] = array('status_net', array('message_id' => $message->id)); - - Event::handle('EndActivityObjectFromMessage', array($message, &$object)); + $object->extra[] = ['status_net', ['message_id' => $message->id]]; + + Event::handle('EndActivityObjectFromMessage', [$message, &$object]); } return $object; } - function outputTo($xo, $tag='activity:object') + /* + * Returns an array based on this Activity Object suitable for + * encoding as JSON. + * + * @return array $object the activity object array + */ + + public function asString($tag = 'activity:object') + { + $xs = new XMLStringer(true); + + $this->outputTo($xs, $tag); + + return $xs->getString(); + } + + public function outputTo($xo, $tag = 'activity:object') { if (!empty($tag)) { $xo->elementStart($tag); } - if (Event::handle('StartActivityObjectOutputAtom', array($this, $xo))) { + if (Event::handle('StartActivityObjectOutputAtom', [$this, $xo])) { $xo->element('activity:object-type', null, $this->type); // uses URI @@ -650,7 +707,7 @@ class ActivityObject // XXX: assuming HTML content here $xo->element( ActivityUtils::CONTENT, - array('type' => 'html'), + ['type' => 'html'], common_xml_safe_str($this->content) ); } @@ -658,45 +715,43 @@ class ActivityObject if (!empty($this->link)) { $xo->element( 'link', - array( + [ 'rel' => 'alternate', 'type' => 'text/html', 'href' => $this->link - ), - null + ] ); } if (!empty($this->selfLink)) { $xo->element( 'link', - array( + [ 'rel' => 'self', 'type' => 'application/atom+xml', 'href' => $this->selfLink - ), - null + ] ); } - if(!empty($this->owner)) { + if (!empty($this->owner)) { $owner = $this->owner->asActivityNoun(self::AUTHOR); $xo->raw($owner); } if ($this->type == ActivityObject::PERSON || $this->type == ActivityObject::GROUP) { - foreach ($this->avatarLinks as $alink) { - $xo->element('link', - array( - 'rel' => 'avatar', - 'type' => $alink->type, - 'media:width' => $alink->width, - 'media:height' => $alink->height, - 'href' => $alink->url, - ), - null); + $xo->element( + 'link', + [ + 'rel' => 'avatar', + 'type' => $alink->type, + 'media:width' => $alink->width, + 'media:height' => $alink->height, + 'href' => $alink->url, + ] + ); } } @@ -719,7 +774,7 @@ class ActivityObject $xo->element($extraTag, $attrs, $content); } - Event::handle('EndActivityObjectOutputAtom', array($this, $xo)); + Event::handle('EndActivityObjectOutputAtom', [$this, $xo]); } if (!empty($tag)) { @@ -729,27 +784,11 @@ class ActivityObject return; } - function asString($tag='activity:object') + public function asArray() { - $xs = new XMLStringer(true); + $object = []; - $this->outputTo($xs, $tag); - - return $xs->getString(); - } - - /* - * Returns an array based on this Activity Object suitable for - * encoding as JSON. - * - * @return array $object the activity object array - */ - - function asArray() - { - $object = array(); - - if (Event::handle('StartActivityObjectOutputJson', array($this, &$object))) { + if (Event::handle('StartActivityObjectOutputJson', [$this, &$object])) { // XXX: attachments are added by Activity // author (Add object for author? Could be useful for repeats.) @@ -762,7 +801,7 @@ class ActivityObject if ($this->id) { $object['id'] = $this->id; - } else if ($this->link) { + } elseif ($this->link) { $object['id'] = $this->link; } @@ -775,8 +814,8 @@ class ActivityObject // XXX: Not sure what the best avatar is to use for the // author's "image". For now, I'm using the large size. - $imgLink = null; - $avatarMediaLinks = array(); + $imgLink = null; + $avatarMediaLinks = []; foreach ($this->avatarLinks as $a) { @@ -798,14 +837,14 @@ class ActivityObject } if (!array_key_exists('status_net', $object)) { - $object['status_net'] = array(); + $object['status_net'] = []; } $object['status_net']['avatarLinks'] = $avatarMediaLinks; // extension // image if (!empty($imgLink)) { - $object['image'] = $imgLink->asArray(); + $object['image'] = $imgLink->asArray(); } } @@ -843,7 +882,7 @@ class ActivityObject $parts = explode(":", $objectName); if (count($parts) == 2 && $parts[0] == "statusnet") { if (!array_key_exists('status_net', $object)) { - $object['status_net'] = array(); + $object['status_net'] = []; } $object['status_net'][$parts[1]] = $props; } else { @@ -853,16 +892,15 @@ class ActivityObject } if (!empty($this->geopoint)) { - list($lat, $lon) = explode(' ', $this->geopoint); if (!empty($lat) && !empty($lon)) { - $object['location'] = array( + $object['location'] = [ 'objectType' => 'place', 'position' => sprintf("%+02.5F%+03.5F/", $lat, $lon), 'lat' => $lat, 'lon' => $lon - ); + ]; $loc = Location::fromLatLon((float)$lat, (float)$lon); @@ -887,9 +925,9 @@ class ActivityObject if (!empty($this->thumbnail)) { if (is_string($this->thumbnail)) { - $object['image'] = array('url' => $this->thumbnail); + $object['image'] = ['url' => $this->thumbnail]; } else { - $object['image'] = array('url' => $this->thumbnail->getUrl()); + $object['image'] = ['url' => $this->thumbnail->getUrl()]; if ($this->thumbnail->width) { $object['image']['width'] = $this->thumbnail->width; } @@ -900,63 +938,32 @@ class ActivityObject } switch (self::canonicalType($this->type)) { - case 'image': - if (!empty($this->largerImage)) { - $object['fullImage'] = array('url' => $this->largerImage); - } - break; - case 'audio': - case 'video': - if (!empty($this->stream)) { - $object['stream'] = array('url' => $this->stream); - } - break; + case 'image': + if (!empty($this->largerImage)) { + $object['fullImage'] = ['url' => $this->largerImage]; + } + break; + case 'audio': + case 'video': + if (!empty($this->stream)) { + $object['stream'] = ['url' => $this->stream]; + } + break; } - Event::handle('EndActivityObjectOutputJson', array($this, &$object)); + Event::handle('EndActivityObjectOutputJson', [$this, &$object]); } return array_filter($object); } - public function getIdentifiers() { - $ids = array(); - foreach(array('id', 'link', 'url') as $id) { + public function getIdentifiers() + { + $ids = []; + foreach (['id', 'link', 'url'] as $id) { if (isset($this->$id)) { $ids[] = $this->$id; } } return array_unique($ids); } - - static function canonicalType($type) { - return ActivityUtils::resolveUri($type, true); - } - - static function mimeTypeToObjectType($mimeType) { - $ot = null; - - // Default - - if (empty($mimeType)) { - return self::FILE; - } - - $parts = explode('/', $mimeType); - - switch ($parts[0]) { - case 'image': - $ot = self::IMAGE; - break; - case 'audio': - $ot = self::AUDIO; - break; - case 'video': - $ot = self::VIDEO; - break; - default: - $ot = self::FILE; - } - - return $ot; - } } diff --git a/lib/apiaction.php b/lib/apiaction.php index e285489007..f5928c7652 100644 --- a/lib/apiaction.php +++ b/lib/apiaction.php @@ -120,7 +120,7 @@ class ApiAction extends Action { const READ_ONLY = 1; const READ_WRITE = 2; - public static $reserved_sources = array('web', 'omb', 'ostatus', 'mail', 'xmpp', 'api'); + public static $reserved_sources = ['web', 'omb', 'ostatus', 'mail', 'xmpp', 'api']; public $user = null; public $auth_user = null; public $page = null; @@ -136,19 +136,19 @@ class ApiAction extends Action public function twitterRelationshipArray($source, $target) { - $relationship = array(); + $relationship = []; $relationship['source'] = $this->relationshipDetailsArray($source->getProfile(), $target->getProfile()); $relationship['target'] = $this->relationshipDetailsArray($target->getProfile(), $source->getProfile()); - return array('relationship' => $relationship); + return ['relationship' => $relationship]; } public function relationshipDetailsArray(Profile $source, Profile $target) { - $details = array(); + $details = []; $details['screen_name'] = $source->getNickname(); $details['followed_by'] = $target->isSubscribed($source); @@ -195,19 +195,18 @@ class ApiAction extends Action * See that method's documentation for more info. * * @param string $tag Element type or tagname - * @param array $attrs Array of element attributes, as - * key-value pairs - * @param string $content string content of the element + * @param array|string|null $attrs Array of element attributes, as key-value pairs + * @param string|null $content string content of the element * * @return void */ - public function element($tag, $attrs = [], $content = "") + public function element(string $tag, $attrs = null, $content = null) { if (is_bool($content)) { - $content = ($content ? 'true' : 'false'); + $content = ($content ? "true" : "false"); } - return parent::element($tag, $attrs, $content); + parent::element($tag, $attrs, $content); } public function showSingleXmlStatus($notice) @@ -254,23 +253,23 @@ class ApiAction extends Action $this->startXML(); $this->elementStart( 'rss', - array( + [ 'version' => '2.0', 'xmlns:atom' => 'http://www.w3.org/2005/Atom', 'xmlns:georss' => 'http://www.georss.org/georss' - ) + ] ); $this->elementStart('channel'); - Event::handle('StartApiRss', array($this)); + Event::handle('StartApiRss', [$this]); } public function initTwitterAtom() { $this->startXML(); // FIXME: don't hardcode the language here! - $this->elementStart('feed', array('xmlns' => 'http://www.w3.org/2005/Atom', + $this->elementStart('feed', ['xmlns' => 'http://www.w3.org/2005/Atom', 'xml:lang' => 'en-US', - 'xmlns:thr' => 'http://purl.org/syndication/thread/1.0')); + 'xmlns:thr' => 'http://purl.org/syndication/thread/1.0']); } public function twitterStatusArray($notice, $include_user = true) @@ -293,7 +292,7 @@ class ApiAction extends Action { $profile = $notice->getProfile(); - $twitter_status = array(); + $twitter_status = []; $twitter_status['text'] = $notice->content; $twitter_status['truncated'] = false; # Not possible on StatusNet $twitter_status['created_at'] = self::dateTwitter($notice->created); @@ -346,9 +345,9 @@ class ApiAction extends Action try { $notloc = Notice_location::locFromStored($notice); // This is the format that GeoJSON expects stuff to be in - $twitter_status['geo'] = array('type' => 'Point', - 'coordinates' => array((float)$notloc->lat, - (float)$notloc->lon)); + $twitter_status['geo'] = ['type' => 'Point', + 'coordinates' => [(float)$notloc->lat, + (float)$notloc->lon]]; } catch (ServerException $e) { $twitter_status['geo'] = null; } @@ -357,12 +356,12 @@ class ApiAction extends Action $attachments = $notice->attachments(); if (!empty($attachments)) { - $twitter_status['attachments'] = array(); + $twitter_status['attachments'] = []; foreach ($attachments as $attachment) { try { $enclosure_o = $attachment->getEnclosure(); - $enclosure = array(); + $enclosure = []; $enclosure['url'] = $enclosure_o->url; $enclosure['mimetype'] = $enclosure_o->mimetype; $enclosure['size'] = $enclosure_o->size; @@ -385,8 +384,8 @@ class ApiAction extends Action $twitter_status['statusnet_conversation_id'] = intval($notice->conversation); // The event call to handle NoticeSimpleStatusArray lets plugins add data to the output array - Event::handle('NoticeSimpleStatusArray', array($notice, &$twitter_status, $this->scoped, - array('include_user' => $include_user))); + Event::handle('NoticeSimpleStatusArray', [$notice, &$twitter_status, $this->scoped, + ['include_user' => $include_user]]); return $twitter_status; } @@ -401,7 +400,7 @@ class ApiAction extends Action public function twitterUserArray($profile, $get_notice = false) { - $twitter_user = array(); + $twitter_user = []; try { $user = $profile->getUser(); @@ -430,7 +429,7 @@ class ApiAction extends Action $twitter_user['profile_image_url_original'] = $origurl; $twitter_user['groups_count'] = $profile->getGroupCount(); - foreach (array('linkcolor', 'backgroundcolor') as $key) { + foreach (['linkcolor', 'backgroundcolor'] as $key) { $twitter_user[$key] = Profile_prefs::getConfigData($profile, 'theme', $key); } // END introduced by qvitter API, not necessary for StatusNet API @@ -489,14 +488,14 @@ class ApiAction extends Action $twitter_user['statusnet_profile_url'] = $profile->profileurl; // The event call to handle NoticeSimpleStatusArray lets plugins add data to the output array - Event::handle('TwitterUserArray', array($profile, &$twitter_user, $this->scoped, array())); + Event::handle('TwitterUserArray', [$profile, &$twitter_user, $this->scoped, []]); return $twitter_user; } public function showTwitterXmlStatus($twitter_status, $tag = 'status', $namespaces = false) { - $attrs = array(); + $attrs = []; if ($namespaces) { $attrs['xmlns:statusnet'] = 'http://status.net/schema/api/1/'; } @@ -537,7 +536,7 @@ class ApiAction extends Action public function showTwitterXmlUser($twitter_user, $role = 'user', $namespaces = false) { - $attrs = array(); + $attrs = []; if ($namespaces) { $attrs['xmlns:statusnet'] = 'http://status.net/schema/api/1/'; } @@ -557,9 +556,9 @@ class ApiAction extends Action public function showXmlAttachments($attachments) { if (!empty($attachments)) { - $this->elementStart('attachments', array('type' => 'array')); + $this->elementStart('attachments', ['type' => 'array']); foreach ($attachments as $attachment) { - $attrs = array(); + $attrs = []; $attrs['url'] = $attachment['url']; $attrs['mimetype'] = $attachment['mimetype']; $attrs['size'] = $attachment['size']; @@ -575,7 +574,7 @@ class ApiAction extends Action // empty geo element $this->element('geo'); } else { - $this->elementStart('geo', array('xmlns:georss' => 'http://www.georss.org/georss')); + $this->elementStart('geo', ['xmlns:georss' => 'http://www.georss.org/georss']); $this->element('georss:point', null, $geo['coordinates'][0] . ' ' . $geo['coordinates'][1]); $this->elementEnd('geo'); } @@ -641,12 +640,12 @@ class ApiAction extends Action public function showXmlTimeline($notice) { $this->initDocument('xml'); - $this->elementStart('statuses', array('type' => 'array', - 'xmlns:statusnet' => 'http://status.net/schema/api/1/')); + $this->elementStart('statuses', ['type' => 'array', + 'xmlns:statusnet' => 'http://status.net/schema/api/1/']); if (is_array($notice)) { //FIXME: make everything calling showJsonTimeline use only Notice objects - $ids = array(); + $ids = []; foreach ($notice as $n) { $ids[] = $n->getID(); } @@ -677,20 +676,20 @@ class ApiAction extends Action if (!is_null($self)) { $this->element( 'atom:link', - array( + [ 'type' => 'application/rss+xml', 'href' => $self, 'rel' => 'self' - ) + ] ); } if (!is_null($suplink)) { // For FriendFeed's SUP protocol - $this->element('link', array('xmlns' => 'http://www.w3.org/2005/Atom', + $this->element('link', ['xmlns' => 'http://www.w3.org/2005/Atom', 'rel' => 'http://api.friendfeed.com/2008/03#sup', 'href' => $suplink, - 'type' => 'application/json')); + 'type' => 'application/json']); } if (!is_null($logo)) { @@ -707,7 +706,7 @@ class ApiAction extends Action if (is_array($notice)) { //FIXME: make everything calling showJsonTimeline use only Notice objects - $ids = array(); + $ids = []; foreach ($notice as $n) { $ids[] = $n->getID(); } @@ -729,16 +728,16 @@ class ApiAction extends Action public function twitterRssEntryArray($notice) { - $entry = array(); + $entry = []; - if (Event::handle('StartRssEntryArray', array($notice, &$entry))) { + if (Event::handle('StartRssEntryArray', [$notice, &$entry])) { $profile = $notice->getProfile(); // We trim() to avoid extraneous whitespace in the output $entry['content'] = common_xml_safe_str(trim($notice->getRendered())); $entry['title'] = $profile->nickname . ': ' . common_xml_safe_str(trim($notice->content)); - $entry['link'] = common_local_url('shownotice', array('notice' => $notice->id)); + $entry['link'] = common_local_url('shownotice', ['notice' => $notice->id]); $entry['published'] = common_date_iso8601($notice->created); $taguribase = TagURI::base(); @@ -749,12 +748,12 @@ class ApiAction extends Action // Enclosures $attachments = $notice->attachments(); - $enclosures = array(); + $enclosures = []; foreach ($attachments as $attachment) { try { $enclosure_o = $attachment->getEnclosure(); - $enclosure = array(); + $enclosure = []; $enclosure['url'] = $enclosure_o->url; $enclosure['mimetype'] = $enclosure_o->mimetype; $enclosure['size'] = $enclosure_o->size; @@ -772,7 +771,7 @@ class ApiAction extends Action $tag = new Notice_tag(); $tag->notice_id = $notice->id; if ($tag->find()) { - $entry['tags'] = array(); + $entry['tags'] = []; while ($tag->fetch()) { $entry['tags'][] = $tag->tag; } @@ -788,14 +787,14 @@ class ApiAction extends Action $notloc = Notice_location::locFromStored($notice); // This is the format that GeoJSON expects stuff to be in. // showGeoRSS() below uses it for XML output, so we reuse it - $entry['geo'] = array('type' => 'Point', - 'coordinates' => array((float)$notloc->lat, - (float)$notloc->lon)); + $entry['geo'] = ['type' => 'Point', + 'coordinates' => [(float)$notloc->lat, + (float)$notloc->lon]]; } catch (ServerException $e) { $entry['geo'] = null; } - Event::handle('EndRssEntryArray', array($notice, &$entry)); + Event::handle('EndRssEntryArray', [$notice, &$entry]); } return $entry; @@ -813,7 +812,7 @@ class ApiAction extends Action // RSS only supports 1 enclosure per item 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); + $this->element('enclosure', ['url' => $enclosure['url'], 'type' => $enclosure['mimetype'], 'length' => $enclosure['size']]); } if (array_key_exists('tags', $entry)) { @@ -843,7 +842,7 @@ class ApiAction extends Action $this->element('title', null, $title); $this->element('id', null, $id); - $this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null); + $this->element('link', ['href' => $link, 'rel' => 'alternate', 'type' => 'text/html']); if (!is_null($logo)) { $this->element('logo', null, $logo); @@ -851,14 +850,14 @@ class ApiAction extends Action if (!is_null($suplink)) { // For FriendFeed's SUP protocol - $this->element('link', array('rel' => 'http://api.friendfeed.com/2008/03#sup', + $this->element('link', ['rel' => 'http://api.friendfeed.com/2008/03#sup', 'href' => $suplink, - 'type' => 'application/json')); + 'type' => 'application/json']); } if (!is_null($selfuri)) { - $this->element('link', array('href' => $selfuri, - 'rel' => 'self', 'type' => 'application/atom+xml'), null); + $this->element('link', ['href' => $selfuri, + 'rel' => 'self', 'type' => 'application/atom+xml']); } $this->element('updated', null, common_date_iso8601('now')); @@ -866,7 +865,7 @@ class ApiAction extends Action if (is_array($notice)) { //FIXME: make everything calling showJsonTimeline use only Notice objects - $ids = array(); + $ids = []; foreach ($notice as $n) { $ids[] = $n->getID(); } @@ -912,7 +911,7 @@ class ApiAction extends Action public function twitterRssGroupArray($group) { - $entry = array(); + $entry = []; $entry['content'] = $group->description; $entry['title'] = $group->nickname; $entry['link'] = $group->permalink(); @@ -934,18 +933,18 @@ class ApiAction extends Action $this->element('title', null, common_xml_safe_str($entry['title'])); $this->element( 'content', - array('type' => 'html'), + ['type' => 'html'], common_xml_safe_str($entry['content']) ); $this->element('id', null, $entry['id']); $this->element('published', null, $entry['published']); $this->element('updated', null, $entry['updated']); - $this->element('link', array('type' => 'text/html', + $this->element('link', ['type' => 'text/html', 'href' => $entry['link'], - 'rel' => 'alternate')); - $this->element('link', array('type' => $entry['avatar-type'], + 'rel' => 'alternate']); + $this->element('link', ['type' => $entry['avatar-type'], 'href' => $entry['avatar'], - 'rel' => 'image')); + 'rel' => 'image']); $this->elementStart('author'); $this->element('name', null, $entry['author-name']); @@ -961,11 +960,11 @@ class ApiAction extends Action $this->element('title', null, common_xml_safe_str($title)); $this->element('id', null, $id); - $this->element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null); + $this->element('link', ['href' => $link, 'rel' => 'alternate', 'type' => 'text/html']); if (!is_null($selfuri)) { - $this->element('link', array('href' => $selfuri, - 'rel' => 'self', 'type' => 'application/atom+xml'), null); + $this->element('link', ['href' => $selfuri, + 'rel' => 'self', 'type' => 'application/atom+xml']); } $this->element('updated', null, common_date_iso8601('now')); @@ -988,11 +987,11 @@ class ApiAction extends Action { $this->initDocument('json'); - $statuses = array(); + $statuses = []; if (is_array($notice)) { //FIXME: make everything calling showJsonTimeline use only Notice objects - $ids = array(); + $ids = []; foreach ($notice as $n) { $ids[] = $n->getID(); } @@ -1018,7 +1017,7 @@ class ApiAction extends Action { $this->initDocument('json'); - $groups = array(); + $groups = []; if (is_array($group)) { foreach ($group as $g) { @@ -1039,7 +1038,7 @@ class ApiAction extends Action public function twitterGroupArray($group) { - $twitter_group = array(); + $twitter_group = []; $twitter_group['id'] = intval($group->id); $twitter_group['url'] = $group->permalink(); @@ -1072,7 +1071,7 @@ class ApiAction extends Action public function showXmlGroups($group) { $this->initDocument('xml'); - $this->elementStart('groups', array('type' => 'array')); + $this->elementStart('groups', ['type' => 'array']); if (is_array($group)) { foreach ($group as $g) { @@ -1103,7 +1102,7 @@ class ApiAction extends Action { $this->initDocument('xml'); $this->elementStart('lists_list'); - $this->elementStart('lists', array('type' => 'array')); + $this->elementStart('lists', ['type' => 'array']); if (is_array($list)) { foreach ($list as $l) { @@ -1130,7 +1129,7 @@ class ApiAction extends Action { $profile = Profile::getKV('id', $list->tagger); - $twitter_list = array(); + $twitter_list = []; $twitter_list['id'] = $list->id; $twitter_list['name'] = $list->tag; $twitter_list['full_name'] = '@' . $profile->nickname . '/' . $list->tag;; @@ -1169,7 +1168,7 @@ class ApiAction extends Action { $this->initDocument('json'); - $lists = array(); + $lists = []; if (is_array($list)) { foreach ($list as $l) { @@ -1183,13 +1182,13 @@ class ApiAction extends Action } } - $lists_list = array( + $lists_list = [ 'lists' => $lists, 'next_cursor' => $next_cursor, 'next_cursor_str' => strval($next_cursor), 'previous_cursor' => $prev_cursor, 'previous_cursor_str' => strval($prev_cursor) - ); + ]; $this->showJsonObjects($lists_list); @@ -1199,8 +1198,8 @@ class ApiAction extends Action public function showTwitterXmlUsers($user) { $this->initDocument('xml'); - $this->elementStart('users', array('type' => 'array', - 'xmlns:statusnet' => 'http://status.net/schema/api/1/')); + $this->elementStart('users', ['type' => 'array', + 'xmlns:statusnet' => 'http://status.net/schema/api/1/']); if (is_array($user)) { foreach ($user as $u) { @@ -1222,7 +1221,7 @@ class ApiAction extends Action { $this->initDocument('json'); - $users = array(); + $users = []; if (is_array($user)) { foreach ($user as $u) { @@ -1299,7 +1298,6 @@ class ApiAction extends Action public function getTargetProfile($id) { if (empty($id)) { - // Twitter supports these other ways of passing the user ID if (self::is_decimal($this->arg('id'))) { return Profile::getKV($this->arg('id')); @@ -1322,14 +1320,16 @@ class ApiAction extends Action // Fall back to trying the currently authenticated user return $this->scoped; } - } elseif (self::is_decimal($id) && intval($id) > 0) { - return Profile::getByID($id); - } else { - // FIXME: check if isAcct to identify remote profiles and not just local nicknames - $nickname = common_canonical_nickname($id); - $user = User::getByNickname($nickname); - return $user->getProfile(); } + + if (self::is_decimal($id) && intval($id) > 0) { + return Profile::getByID($id); + } + + // FIXME: check if isAcct to identify remote profiles and not just local nicknames + $nickname = common_canonical_nickname($id); + $user = User::getByNickname($nickname); + return $user->getProfile(); } private static function is_decimal($str) @@ -1396,13 +1396,15 @@ class ApiAction extends Action } elseif ($this->arg('group_name')) { return User_group::getForNickname($this->arg('group_name')); } - } elseif (self::is_decimal($id)) { + } + + if (self::is_decimal($id)) { return User_group::getKV('id', $id); } elseif ($this->arg('uri')) { // FIXME: move this into empty($id) check? return User_group::getKV('uri', urldecode($this->arg('uri'))); - } else { - return User_group::getForNickname($id); } + + return User_group::getForNickname($id); } public function getTargetList($user = null, $id = null) @@ -1461,12 +1463,14 @@ class ApiAction extends Action // Fall back to trying the currently authenticated user return $this->scoped->getUser(); } - } elseif (self::is_decimal($id)) { - return User::getKV($id); - } else { - $nickname = common_canonical_nickname($id); - return User::getKV('nickname', $nickname); } + + if (self::is_decimal($id)) { + return User::getKV($id); + } + + $nickname = common_canonical_nickname($id); + return User::getKV('nickname', $nickname); } /** @@ -1480,7 +1484,7 @@ class ApiAction extends Action $action = mb_substr(get_class($this), 0, -6); // remove 'Action' $id = $this->arg('id'); - $aargs = array('format' => $this->format); + $aargs = ['format' => $this->format]; if (!empty($id)) { $aargs['id'] = $id; } @@ -1517,8 +1521,9 @@ class ApiAction extends Action * @param array $args Web and URL arguments * * @return boolean false if user doesn't exist + * @throws ClientException */ - protected function prepare(array $args = array()) + protected function prepare(array $args = []) { GNUsocial::setApi(true); // reduce exception reports to aid in debugging parent::prepare($args); @@ -1550,8 +1555,6 @@ class ApiAction extends Action /** * Handle a request * - * @param array $args Arguments from $_REQUEST - * * @return void */ protected function handle() diff --git a/lib/atom10feed.php b/lib/atom10feed.php index 2bba6d0a91..a2bd3af3de 100644 --- a/lib/atom10feed.php +++ b/lib/atom10feed.php @@ -206,7 +206,7 @@ class Atom10Feed extends XMLStringer { foreach ($this->links as $attrs) { - $this->element('link', $attrs, null); + $this->element('link', $attrs); } } diff --git a/lib/atomgroupnoticefeed.php b/lib/atomgroupnoticefeed.php index 5b6fcf4295..945d9fca2f 100644 --- a/lib/atomgroupnoticefeed.php +++ b/lib/atomgroupnoticefeed.php @@ -114,6 +114,6 @@ class AtomGroupNoticeFeed extends AtomNoticeFeed $attrs['member_count'] = $this->group->getMemberCount(); - $this->element('statusnet:group_info', $attrs, null); + $this->element('statusnet:group_info', $attrs); } } diff --git a/lib/erroraction.php b/lib/erroraction.php index 03a1960c62..20ab200779 100644 --- a/lib/erroraction.php +++ b/lib/erroraction.php @@ -44,7 +44,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { */ class ErrorAction extends InfoAction { - static $status = array(); + static $status = []; var $code = null; var $message = null; @@ -86,11 +86,11 @@ class ErrorAction extends InfoAction /** * Display content. * - * @return nothing + * @return void */ function showContent() { - $this->element('div', array('class' => 'error'), $this->message); + $this->element('div', ['class' => 'error'], $this->message); } function showNoticeForm() @@ -102,20 +102,19 @@ class ErrorAction extends InfoAction * * Goes back to the browser, where it's shown in a popup. * - * @param string $msg Message to show - * * @return void + * @throws ClientException */ function ajaxErrorMsg() { - $this->startHTML('text/xml;charset=utf-8', true); + $this->startHTML('text/xml;charset=utf-8'); $this->elementStart('head'); // TRANS: Page title after an AJAX error occurs on the send notice page. $this->element('title', null, _('Ajax Error')); $this->elementEnd('head'); $this->elementStart('body'); - $this->element('p', array('id' => 'error'), $this->message); + $this->element('p', ['id' => 'error'], $this->message); $this->elementEnd('body'); $this->endHTML(); } diff --git a/lib/htmloutputter.php b/lib/htmloutputter.php index 5e40037952..c0c7485c77 100644 --- a/lib/htmloutputter.php +++ b/lib/htmloutputter.php @@ -28,7 +28,9 @@ * @link http://status.net/ */ -if (!defined('GNUSOCIAL')) { exit(1); } +if (!defined('GNUSOCIAL')) { + exit(1); +} // Can include XHTML options but these are too fragile in practice. define('PAGE_TYPE_PREFS', 'text/html'); @@ -51,22 +53,22 @@ define('PAGE_TYPE_PREFS', 'text/html'); * @see Action * @see XMLOutputter */ - class HTMLOutputter extends XMLOutputter { - protected $DTD = array('doctype' => 'html', - 'spec' => '-//W3C//DTD XHTML 1.0 Strict//EN', - 'uri' => 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'); + protected $DTD = ['doctype' => 'html', + 'spec' => '-//W3C//DTD XHTML 1.0 Strict//EN', + 'uri' => 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd']; + /** * Constructor * * Just wraps the XMLOutputter constructor. * - * @param string $output URI to output to, default = stdout + * @param string $output URI to output to, default = stdout * @param boolean $indent Whether to indent output, default true */ - function __construct($output='php://output', $indent=null) + public function __construct($output = 'php://output', $indent = null) { parent::__construct($output, $indent); } @@ -80,16 +82,17 @@ class HTMLOutputter extends XMLOutputter * * @param string $type MIME type to use; default is to do negotation. * + * @return void + * @throws ClientException * @todo extract content negotiation code to an HTTP module or class. * - * @return void */ - function startHTML($type=null) + public function startHTML($type = null) { if (!$type) { $httpaccept = isset($_SERVER['HTTP_ACCEPT']) ? - $_SERVER['HTTP_ACCEPT'] : null; + $_SERVER['HTTP_ACCEPT'] : null; // XXX: allow content negotiation for RDF, RSS, or XRDS @@ -100,16 +103,16 @@ class HTMLOutputter extends XMLOutputter if (!$type) { // TRANS: Client exception 406 - throw new ClientException(_('This page is not available in a '. - 'media type you accept'), 406); + throw new ClientException(_('This page is not available in a ' . + 'media type you accept'), 406); } } - header('Content-Type: '.$type); + header('Content-Type: ' . $type); - // Output anti-framing headers to prevent clickjacking (respected by newer + // Output anti-framing headers to prevent clickjacking (respected by newer // browsers). - if (common_config('javascript', 'bustframes')) { + if (common_config('javascript', 'bustframes')) { header('X-XSS-Protection: 1; mode=block'); // detect XSS Reflection attacks header('X-Frame-Options: SAMEORIGIN'); // no rendering if origin mismatch } @@ -124,55 +127,57 @@ class HTMLOutputter extends XMLOutputter $language = $this->getLanguage(); - $attrs = array( + $attrs = [ 'xmlns' => 'http://www.w3.org/1999/xhtml', 'xml:lang' => $language, 'lang' => $language - ); + ]; - if (Event::handle('StartHtmlElement', array($this, &$attrs))) { + if (Event::handle('StartHtmlElement', [$this, &$attrs])) { $this->elementStart('html', $attrs); - Event::handle('EndHtmlElement', array($this, &$attrs)); + Event::handle('EndHtmlElement', [$this, &$attrs]); } } - public function setDTD($doctype, $spec, $uri) + /** + * To specify additional HTTP headers for the action + * + * @return void + */ + public function extraHeaders() { - $this->DTD = array('doctype' => $doctype, 'spec' => $spec, 'uri' => $uri); + // Needs to be overloaded } protected function writeDTD() { - $this->xw->writeDTD($this->DTD['doctype'], - $this->DTD['spec'], - $this->DTD['uri']); + $this->xw->writeDTD( + $this->DTD['doctype'], + $this->DTD['spec'], + $this->DTD['uri'] + ); } - function getLanguage() + public function getLanguage() { // FIXME: correct language for interface return common_language(); } - /** - * Ends an HTML document - * - * @return void - */ - function endHTML() + public function setDTD($doctype, $spec, $uri) { - $this->elementEnd('html'); - $this->endXML(); + $this->DTD = ['doctype' => $doctype, 'spec' => $spec, 'uri' => $uri]; } /** - * To specify additional HTTP headers for the action - * - * @return void - */ - function extraHeaders() + * Ends an HTML document + * + * @return void + */ + public function endHTML() { - // Needs to be overloaded + $this->elementEnd('html'); + $this->endXML(); } /** @@ -185,24 +190,23 @@ class HTMLOutputter extends XMLOutputter * * If $attrs['type'] does not exist it will be set to 'text'. * - * @param string $id element ID, must be unique on page - * @param string $label text of label for the element - * @param string $value value of the element, default null + * @param string $id element ID, must be unique on page + * @param string $label text of label for the element + * @param string $value value of the element, default null * @param string $instructions instructions for valid input - * @param string $name name of the element; if null, the id will - * be used - * @param bool $required HTML5 required attribute (exclude when false) - * @param array $attrs Initial attributes manually set in an array (overwritten by previous options) + * @param string $name name of the element; if null, the id will be used + * @param bool $required HTML5 required attribute (exclude when false) + * @param array $attrs Initial attributes manually set in an array (overwritten by previous options) * + * @return void * @todo add a $maxLength parameter * @todo add a $size parameter * - * @return void */ - function input($id, $label, $value=null, $instructions=null, $name=null, $required=false, array $attrs=array()) + public function input($id, $label, $value = null, $instructions = null, $name = null, $required = false, array $attrs = []) { - $this->element('label', array('for' => $id), $label); + $this->element('label', ['for' => $id], $label); if (!array_key_exists('type', $attrs)) { $attrs['type'] = 'text'; } @@ -234,25 +238,31 @@ class HTMLOutputter extends XMLOutputter * Note that the value is default 'true' (the string), which can * be used by Action::boolean() * - * @param string $id element ID, must be unique on page - * @param string $label text of label for the element - * @param string $checked if the box is checked, default false + * @param string $id element ID, must be unique on page + * @param string $label text of label for the element + * @param bool $checked if the box is checked, default false * @param string $instructions instructions for valid input - * @param string $value value of the checkbox, default 'true' - * @param string $disabled show the checkbox disabled, default false + * @param string $value value of the checkbox, default 'true' + * @param bool $disabled show the checkbox disabled, default false * * @return void * * @todo add a $name parameter */ - function checkbox($id, $label, $checked=false, $instructions=null, - $value='true', $disabled=false) + public function checkbox( + $id, + $label, + $checked = false, + $instructions = null, + $value = 'true', + $disabled = false + ) { - $attrs = array('name' => $id, - 'type' => 'checkbox', - 'class' => 'checkbox', - 'id' => $id); + $attrs = ['name' => $id, + 'type' => 'checkbox', + 'class' => 'checkbox', + 'id' => $id]; if ($value) { $attrs['value'] = $value; } @@ -264,9 +274,12 @@ class HTMLOutputter extends XMLOutputter } $this->element('input', $attrs); $this->text(' '); - $this->element('label', array('class' => 'checkbox', - 'for' => $id), - $label); + $this->element( + 'label', + ['class' => 'checkbox', + 'for' => $id], + $label + ); $this->text(' '); if ($instructions) { $this->element('p', 'form_guide', $instructions); @@ -280,33 +293,42 @@ class HTMLOutputter extends XMLOutputter * the key is the option value attribute and the value is the option * text. (Careful on the overuse of 'value' here.) * - * @param string $id element ID, must be unique on page - * @param string $label text of label for the element - * @param array $content options array, value => text + * @param string $id element ID, must be unique on page + * @param string $label text of label for the element + * @param array $content options array, value => text * @param string $instructions instructions for valid input - * @param string $blank_select whether to have a blank entry, default false - * @param string $selected selected value, default null + * @param bool $blank_select whether to have a blank entry, default false + * @param string $selected selected value, default null * * @return void * * @todo add a $name parameter */ - function dropdown($id, $label, $content, $instructions=null, - $blank_select=false, $selected=null) + public function dropdown( + $id, + $label, + $content, + $instructions = null, + $blank_select = false, + $selected = null + ) { - $this->element('label', array('for' => $id), $label); - $this->elementStart('select', array('id' => $id, 'name' => $id)); + $this->element('label', ['for' => $id], $label); + $this->elementStart('select', ['id' => $id, 'name' => $id]); if ($blank_select) { - $this->element('option', array('value' => '')); + $this->element('option', ['value' => '']); } foreach ($content as $value => $option) { if ($value == $selected) { - $this->element('option', array('value' => $value, - 'selected' => 'selected'), - $option); + $this->element( + 'option', + ['value' => $value, + 'selected' => 'selected'], + $option + ); } else { - $this->element('option', array('value' => $value), $option); + $this->element('option', ['value' => $value], $option); } } $this->elementEnd('select'); @@ -320,26 +342,26 @@ class HTMLOutputter extends XMLOutputter * * $id is re-used as name * - * @param string $id element ID, must be unique on page + * @param string $id element ID, must be unique on page * @param string $value hidden element value, default null - * @param string $name name, if different than ID + * @param string $name name, if different than ID * * @return void */ - function hidden($id, $value, $name=null) + public function hidden($id, $value, $name = null) { - $this->element('input', array('name' => $name ?: $id, - 'type' => 'hidden', - 'id' => $id, - 'value' => $value)); + $this->element('input', ['name' => $name ?: $id, + 'type' => 'hidden', + 'id' => $id, + 'value' => $value]); } /** * output an HTML password input and associated elements * - * @param string $id element ID, must be unique on page - * @param string $label text of label for the element + * @param string $id element ID, must be unique on page + * @param string $label text of label for the element * @param string $instructions instructions for valid input * * @return void @@ -347,13 +369,13 @@ class HTMLOutputter extends XMLOutputter * @todo add a $name parameter */ - function password($id, $label, $instructions=null) + public function password($id, $label, $instructions = null) { - $this->element('label', array('for' => $id), $label); - $attrs = array('name' => $id, - 'type' => 'password', - 'class' => 'password', - 'id' => $id); + $this->element('label', ['for' => $id], $label); + $attrs = ['name' => $id, + 'type' => 'password', + 'class' => 'password', + 'id' => $id]; $this->element('input', $attrs); if ($instructions) { $this->element('p', 'form_guide', $instructions); @@ -363,39 +385,38 @@ class HTMLOutputter extends XMLOutputter /** * output an HTML submit input and associated elements * - * @param string $id element ID, must be unique on page + * @param string $id element ID, must be unique on page * @param string $label text of the button - * @param string $cls class of the button, default 'submit' - * @param string $name name, if different than ID - * @param string $title title text for the submit button + * @param string $cls class of the button, default 'submit' + * @param string $name name, if different than ID + * @param string $title title text for the submit button * * @return void * * @todo add a $name parameter */ - function submit($id, $label, $cls='submit', $name=null, $title=null) + public function submit($id, $label, $cls = 'submit', $name = null, $title = null) { - $this->element('input', array('type' => 'submit', - 'id' => $id, - 'name' => $name ?: $id, - 'class' => $cls, - 'value' => $label, - 'title' => $title)); + $this->element('input', ['type' => 'submit', + 'id' => $id, + 'name' => $name ?: $id, + 'class' => $cls, + 'value' => $label, + 'title' => $title]); } /** * output a script (almost always javascript) tag * - * @param string $src relative or absolute script path - * @param string $type 'type' attribute value of the tag + * @param string $src relative or absolute script path + * @param string $type 'type' attribute value of the tag * * @return void */ - function script($src, $type='text/javascript') + public function script($src, $type = 'text/javascript') { - if (Event::handle('StartScriptElement', array($this,&$src,&$type))) { - + if (Event::handle('StartScriptElement', [$this, &$src, &$type])) { $url = parse_url($src); if (empty($url['scheme']) && empty($url['host']) && empty($url['query']) && empty($url['fragment'])) { @@ -403,35 +424,28 @@ class HTMLOutputter extends XMLOutputter // XXX: this seems like a big assumption if (strpos($src, 'plugins/') === 0 || strpos($src, 'local/') === 0) { - $src = common_path($src, GNUsocial::isHTTPS()) . '?version=' . GNUSOCIAL_VERSION; - } else { - if (GNUsocial::isHTTPS()) { + $server = common_config('javascript', 'sslserver'); - $sslserver = common_config('javascript', 'sslserver'); - - if (empty($sslserver)) { + if (empty($server)) { if (is_string(common_config('site', 'sslserver')) && mb_strlen(common_config('site', 'sslserver')) > 0) { $server = common_config('site', 'sslserver'); - } else if (common_config('site', 'server')) { + } elseif (common_config('site', 'server')) { $server = common_config('site', 'server'); } - $path = common_config('site', 'path') . '/js/'; + $path = common_config('site', 'path') . '/js/'; } else { - $server = $sslserver; - $path = common_config('javascript', 'sslpath'); + $path = common_config('javascript', 'sslpath'); if (empty($path)) { $path = common_config('javascript', 'path'); } } $protocol = 'https'; - } else { - $path = common_config('javascript', 'path'); if (empty($path)) { @@ -447,79 +461,55 @@ class HTMLOutputter extends XMLOutputter $protocol = 'http'; } - if ($path[strlen($path)-1] != '/') { + if ($path[strlen($path) - 1] != '/') { $path .= '/'; } if ($path[0] != '/') { - $path = '/'.$path; + $path = '/' . $path; } - $src = $protocol.'://'.$server.$path.$src . '?version=' . GNUSOCIAL_VERSION; + $src = $protocol . '://' . $server . $path . $src . '?version=' . GNUSOCIAL_VERSION; } } - $this->element('script', array('type' => $type, - 'src' => $src), - ' '); + $this->element( + 'script', + ['type' => $type, + 'src' => $src], + ' ' + ); - Event::handle('EndScriptElement', array($this,$src,$type)); - } - } - - /** - * output a script (almost always javascript) tag with inline - * code. - * - * @param string $code code to put in the script tag - * @param string $type 'type' attribute value of the tag - * - * @return void - */ - - function inlineScript($code, $type='text/javascript') - { - if(Event::handle('StartInlineScriptElement', array($this,&$code,&$type))) { - $this->elementStart('script', array('type' => $type)); - if($type == 'text/javascript') { - $this->raw('/*raw($code); - if($type == 'text/javascript') { - $this->raw(' /*]]>*/'); // XHTML compat - } - $this->elementEnd('script'); - Event::handle('EndInlineScriptElement', array($this,$code,$type)); + Event::handle('EndScriptElement', [$this, $src, $type]); } } /** * output a css link * - * @param string $src relative path within the theme directory, or an absolute path - * @param string $theme 'theme' that contains the stylesheet + * @param string $src relative path within the theme directory, or an absolute path + * @param string $theme 'theme' that contains the stylesheet * @param string media 'media' attribute of the tag * * @return void */ - function cssLink($src,$theme=null,$media=null) + public function cssLink($src, $theme = null, $media = null) { - if(Event::handle('StartCssLinkElement', array($this,&$src,&$theme,&$media))) { + if (Event::handle('StartCssLinkElement', [$this, &$src, &$theme, &$media])) { $url = parse_url($src); - if( empty($url['scheme']) && empty($url['host']) && empty($url['query']) && empty($url['fragment'])) - { - if(file_exists(Theme::file($src,$theme))){ - $src = Theme::path($src, $theme); - }else{ + if (empty($url['scheme']) && empty($url['host']) && empty($url['query']) && empty($url['fragment'])) { + if (file_exists(Theme::file($src, $theme))) { + $src = Theme::path($src, $theme); + } else { $src = common_path($src, GNUsocial::isHTTPS()); } - $src.= '?version=' . GNUSOCIAL_VERSION; + $src .= '?version=' . GNUSOCIAL_VERSION; } - $this->element('link', array('rel' => 'stylesheet', - 'type' => 'text/css', - 'href' => $src, - 'media' => $media)); - Event::handle('EndCssLinkElement', array($this,$src,$theme,$media)); + $this->element('link', ['rel' => 'stylesheet', + 'type' => 'text/css', + 'href' => $src, + 'media' => $media]); + Event::handle('EndCssLinkElement', [$this, $src, $theme, $media]); } } @@ -527,87 +517,119 @@ class HTMLOutputter extends XMLOutputter * output a style (almost always css) tag with inline * code. * - * @param string $code code to put in the style tag - * @param string $type 'type' attribute value of the tag - * @param string $media 'media' attribute value of the tag + * @param string $code code to put in the style tag + * @param string $type 'type' attribute value of the tag + * @param string $media 'media' attribute value of the tag * * @return void */ - function style($code, $type = 'text/css', $media = null) + public function style($code, $type = 'text/css', $media = null) { - if(Event::handle('StartStyleElement', array($this,&$code,&$type,&$media))) { - $this->elementStart('style', array('type' => $type, 'media' => $media)); + if (Event::handle('StartStyleElement', [$this, &$code, &$type, &$media])) { + $this->elementStart('style', ['type' => $type, 'media' => $media]); $this->raw($code); $this->elementEnd('style'); - Event::handle('EndStyleElement', array($this,$code,$type,$media)); + Event::handle('EndStyleElement', [$this, $code, $type, $media]); } } /** * output an HTML textarea and associated elements * - * @param string $id element ID, must be unique on page - * @param string $label text of label for the element - * @param string $content content of the textarea, default none + * @param string $id element ID, must be unique on page + * @param string $label text of label for the element + * @param string $content content of the textarea, default none * @param string $instructions instructions for valid input - * @param string $name name of textarea; if null, $id will be used - * @param int $cols number of columns - * @param int $rows number of rows - * @param bool $required HTML5 required attribute (exclude when false) + * @param string $name name of textarea; if null, $id will be used + * @param int $cols number of columns + * @param int $rows number of rows + * @param bool $required HTML5 required attribute (exclude when false) * * @return void */ - function textarea( + public function textarea( $id, $label, - $content = null, + $content = null, $instructions = null, - $name = null, - $cols = null, - $rows = null, - $required = false - ) { - $this->element('label', array('for' => $id), $label); - $attrs = array( + $name = null, + $cols = null, + $rows = null, + $required = false + ) + { + $this->element('label', ['for' => $id], $label); + $attrs = [ 'rows' => 3, 'cols' => 40, 'id' => $id - ); + ]; $attrs['name'] = is_null($name) ? $id : $name; if ($cols != null) { $attrs['cols'] = $cols; - } if ($rows != null) { $attrs['rows'] = $rows; } + + if (!empty($required)) { + $attrs['required'] = 'required'; + } + $this->element( 'textarea', $attrs, - is_null($content) ? '' : $content + $content ); if ($instructions) { $this->element('p', 'form_guide', $instructions); } } - /** - * Internal script to autofocus the given element on page onload. - * - * @param string $id element ID, must refer to an existing element - * - * @return void - * - */ - function autofocus($id) + /** + * Internal script to autofocus the given element on page onload. + * + * @param string $id element ID, must refer to an existing element + * + * @return void + * + */ + public function autofocus($id) { $this->inlineScript( - ' $(document).ready(function() {'. - ' var el = $("#' . $id . '");'. - ' if (el.length) { el.focus(); }'. - ' });'); + ' $(document).ready(function() {' . + ' var el = $("#' . $id . '");' . + ' if (el.length) { el.focus(); }' . + ' });' + ); + } + + /** + * output a script (almost always javascript) tag with inline + * code. + * + * @param string $code code to put in the script tag + * @param string $type 'type' attribute value of the tag + * + * @return void + */ + + public function inlineScript($code, $type = 'text/javascript') + { + if (Event::handle('StartInlineScriptElement', [$this, &$code, &$type])) { + $this->elementStart('script', ['type' => $type]); + if ($type == 'text/javascript') { + $this->raw('/*raw($code); + if ($type == 'text/javascript') { + $this->raw(' /*]]>*/'); // XHTML compat + } + $this->elementEnd('script'); + Event::handle('EndInlineScriptElement', [$this, $code, $type]); + } } } diff --git a/lib/infoaction.php b/lib/infoaction.php index 27563b7efc..2b3d388202 100644 --- a/lib/infoaction.php +++ b/lib/infoaction.php @@ -64,7 +64,7 @@ class InfoAction extends ManagedAction /** * Page title. * - * @return page title + * @return string page title */ function title() @@ -81,8 +81,8 @@ class InfoAction extends ManagedAction function showBody() { - $this->elementStart('body', array('id' => 'error')); - $this->elementStart('div', array('id' => 'wrap')); + $this->elementStart('body', ['id' => 'error']); + $this->elementStart('div', ['id' => 'wrap']); $this->showHeader(); $this->showCore(); $this->showFooter(); @@ -92,10 +92,10 @@ class InfoAction extends ManagedAction function showCore() { - $this->elementStart('div', array('id' => 'core')); - $this->elementStart('div', array('id' => 'aside_primary_wrapper')); - $this->elementStart('div', array('id' => 'content_wrapper')); - $this->elementStart('div', array('id' => 'site_nav_local_views_wrapper')); + $this->elementStart('div', ['id' => 'core']); + $this->elementStart('div', ['id' => 'aside_primary_wrapper']); + $this->elementStart('div', ['id' => 'content_wrapper']); + $this->elementStart('div', ['id' => 'site_nav_local_views_wrapper']); $this->showContentBlock(); $this->elementEnd('div'); $this->elementEnd('div'); @@ -105,7 +105,7 @@ class InfoAction extends ManagedAction function showHeader() { - $this->elementStart('div', array('id' => 'header')); + $this->elementStart('div', ['id' => 'header']); $this->showLogo(); $this->showPrimaryNav(); $this->elementEnd('div'); @@ -114,11 +114,11 @@ class InfoAction extends ManagedAction /** * Display content. * - * @return nothing + * @return void */ function showContent() { - $this->element('div', array('class' => 'info'), $this->message); + $this->element('div', ['class' => 'info'], $this->message); } } diff --git a/lib/menu.php b/lib/menu.php index 236e99263d..5fd58f1077 100644 --- a/lib/menu.php +++ b/lib/menu.php @@ -150,12 +150,12 @@ class Menu extends Widget function submenu($label, $menu) { - if (Event::handle('StartSubMenu', array($this->action, $menu, $label))) { + if (Event::handle('StartSubMenu', [$this->action, $menu, $label])) { $this->action->elementStart('li'); $this->action->element('h3', null, $label); $menu->show(); $this->action->elementEnd('li'); - Event::handle('EndSubMenu', array($this->action, $menu, $label)); + Event::handle('EndSubMenu', [$this->action, $menu, $label]); } } } diff --git a/lib/servererroraction.php b/lib/servererroraction.php index 8ca281614b..da2a337109 100644 --- a/lib/servererroraction.php +++ b/lib/servererroraction.php @@ -29,7 +29,9 @@ * along with this program. If not, see . */ -if (!defined('GNUSOCIAL')) { exit(1); } +if (!defined('GNUSOCIAL')) { + exit(1); +} /** * Class for displaying HTTP server errors @@ -48,17 +50,16 @@ if (!defined('GNUSOCIAL')) { exit(1); } * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 * @link http://status.net/ */ - class ServerErrorAction extends ErrorAction { - static $status = array(500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported'); + static $status = [500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported']; - function __construct($message='Error', $code=500, $ex=null) + function __construct($message = 'Error', $code = 500, $ex = null) { parent::__construct($message, $code); @@ -85,23 +86,23 @@ class ServerErrorAction extends ErrorAction /** * To specify additional HTTP headers for the action * - * @return void + * @return void */ function extraHeaders() { - $status_string = @self::$status[$this->code]; - header('HTTP/1.1 '.$this->code.' '.$status_string); + $status_string = self::$status[$this->code]; + header('HTTP/1.1 ' . $this->code . ' ' . $status_string); } /** * Page title. * - * @return page title + * @return string page title */ function title() { - return @self::$status[$this->code]; + return self::$status[$this->code]; } } diff --git a/lib/serverexception.php b/lib/serverexception.php index 764e11c0fc..0f4c509957 100644 --- a/lib/serverexception.php +++ b/lib/serverexception.php @@ -45,7 +45,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { class ServerException extends Exception { - public function __construct($message = null, $code = 500) { + public function __construct($message = "", $code = 500) { parent::__construct($message, $code); } diff --git a/lib/xmloutputter.php b/lib/xmloutputter.php index 463f91be30..61b119e9b0 100644 --- a/lib/xmloutputter.php +++ b/lib/xmloutputter.php @@ -48,7 +48,6 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { * @see Action * @see HTMLOutputter */ - class XMLOutputter { /** @@ -56,25 +55,25 @@ class XMLOutputter * for output. */ - var $xw = null; + public $xw = null; /** * Constructor * * Initializes the wrapped XMLWriter. * - * @param string $output URL for outputting, if null it defaults to stdout ('php://output') + * @param string $output URL for outputting, if null it defaults to stdout ('php://output') * @param boolean $indent Whether to indent output, default true */ - function __construct($output=null, $indent=null) + public function __construct($output = null, $indent = null) { if (is_null($output)) { $output = 'php://output'; } $this->xw = new XMLWriter(); $this->xw->openURI($output); - if(is_null($indent)) { + if (is_null($indent)) { $indent = common_config('site', 'indent'); } $this->xw->setIndent($indent); @@ -83,14 +82,14 @@ class XMLOutputter /** * Start a new XML document * - * @param string $doc document element + * @param string $doc document element * @param string $public public identifier * @param string $system system identifier * * @return void */ - function startXML($doc=null, $public=null, $system=null) + public function startXML($doc = null, $public = null, $system = null) { $this->xw->startDocument('1.0', 'UTF-8'); if ($doc) { @@ -107,7 +106,7 @@ class XMLOutputter * @return void */ - function endXML() + public function endXML() { $this->xw->endDocument(); $this->xw->flush(); @@ -128,28 +127,18 @@ class XMLOutputter * If $attrs is a string instead of an array, it will be treated * as the class attribute of the element. * - * @param string $tag Element type or tagname - * @param array $attrs Array of element attributes, as - * key-value pairs - * @param string $content string content of the element + * @param string $tag Element type or tagname + * @param array|string|null $attrs Array of element attributes, as key-value pairs + * @param string|null $content string content of the element * * @return void */ - function element($tag, $attrs=null, $content=null) + public function element(string $tag, $attrs = null, $content = null) { $this->elementStart($tag, $attrs); if (!is_null($content)) { - $this->xw->text($content); - } - $this->elementEnd($tag); - } - - function elementNS(array $ns, $tag, $attrs=null, $content=null) - { - $this->elementStartNS($ns, $tag, $attrs); - if (!is_null($content)) { - $this->xw->text($content); + $this->xw->text(strval($content)); } $this->elementEnd($tag); } @@ -163,34 +152,20 @@ class XMLOutputter * If $attrs is a string instead of an array, it will be treated * as the class attribute of the element. * - * @param string $tag Element type or tagname - * @param array $attrs Array of element attributes + * @param string $tag Element type or tagname + * @param array|string|null $attrs Attributes * * @return void */ - function elementStart($tag, $attrs=null) + public function elementStart(string $tag, $attrs = null) { $this->xw->startElement($tag); if (is_array($attrs)) { foreach ($attrs as $name => $value) { $this->xw->writeAttribute($name, $value); } - } else if (is_string($attrs)) { - $this->xw->writeAttribute('class', $attrs); - } - } - - function elementStartNS(array $ns, $tag, $attrs=null) - { - reset($ns); // array pointer to 0 - $uri = key($ns); - $this->xw->startElementNS($ns[$uri], $tag, $uri); - if (is_array($attrs)) { - foreach ($attrs as $name => $value) { - $this->xw->writeAttribute($name, $value); - } - } else if (is_string($attrs)) { + } elseif (is_string($attrs)) { $this->xw->writeAttribute('class', $attrs); } } @@ -211,11 +186,11 @@ class XMLOutputter * @return void */ - function elementEnd($tag) + public function elementEnd(string $tag) { - static $empty_tag = array('base', 'meta', 'link', 'hr', - 'br', 'param', 'img', 'area', - 'input', 'col', 'source'); + static $empty_tag = ['base', 'meta', 'link', 'hr', + 'br', 'param', 'img', 'area', + 'input', 'col', 'source']; // XXX: check namespace if (in_array($tag, $empty_tag)) { $this->xw->endElement(); @@ -224,6 +199,29 @@ class XMLOutputter } } + public function elementNS(array $ns, $tag, $attrs = null, $content = null) + { + $this->elementStartNS($ns, $tag, $attrs); + if (!is_null($content)) { + $this->xw->text($content); + } + $this->elementEnd($tag); + } + + public function elementStartNS(array $ns, $tag, $attrs = null) + { + reset($ns); // array pointer to 0 + $uri = key($ns); + $this->xw->startElementNS($ns[$uri], $tag, $uri); + if (is_array($attrs)) { + foreach ($attrs as $name => $value) { + $this->xw->writeAttribute($name, $value); + } + } elseif (is_string($attrs)) { + $this->xw->writeAttribute('class', $attrs); + } + } + /** * output plain text * @@ -235,7 +233,7 @@ class XMLOutputter * @return void */ - function text($txt) + public function text($txt) { $this->xw->text($txt); } @@ -251,7 +249,7 @@ class XMLOutputter * @return void */ - function raw($xml) + public function raw($xml) { $this->xw->writeRaw($xml); } @@ -264,7 +262,7 @@ class XMLOutputter * @return void */ - function comment($txt) + public function comment($txt) { $this->xw->writeComment($txt); } @@ -275,7 +273,7 @@ class XMLOutputter * @return void */ - function flush() + public function flush() { $this->xw->flush(); } diff --git a/lib/xmlstringer.php b/lib/xmlstringer.php index b505e40d39..14602c7e1b 100644 --- a/lib/xmlstringer.php +++ b/lib/xmlstringer.php @@ -42,27 +42,26 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { * @see Action * @see HTMLOutputter */ - class XMLStringer extends XMLOutputter { - function __construct($indent=false) + public function __construct($indent = false) { $this->xw = new XMLWriter(); $this->xw->openMemory(); $this->xw->setIndent($indent); } - function getString() - { - return $this->xw->outputMemory(); - } - - // utility for quickly creating XML-strings - - static function estring($tag, $attrs=null, $content=null) + public static function estring($tag, $attrs = null, $content = null) { $xs = new XMLStringer(); $xs->element($tag, $attrs, $content); return $xs->getString(); } -} \ No newline at end of file + + // utility for quickly creating XML-strings + + public function getString() + { + return $this->xw->outputMemory(); + } +} diff --git a/plugins/Directory/lib/sortablegrouplist.php b/plugins/Directory/lib/sortablegrouplist.php index 0da705e919..7f4e042ce7 100644 --- a/plugins/Directory/lib/sortablegrouplist.php +++ b/plugins/Directory/lib/sortablegrouplist.php @@ -97,7 +97,7 @@ class SortableGroupList extends SortableSubscriptionList // TRANS: Column header in table for members of a group. $this->out->element('th', array('id' => 'Members'), _m('Members')); - $this->out->element('th', array('id' => 'controls'), null); + $this->out->element('th', array('id' => 'controls')); $this->out->elementEnd('tr'); $this->out->elementEnd('thead'); diff --git a/plugins/Directory/lib/sortablesubscriptionlist.php b/plugins/Directory/lib/sortablesubscriptionlist.php index ec8874b2e5..56005dae3d 100644 --- a/plugins/Directory/lib/sortablesubscriptionlist.php +++ b/plugins/Directory/lib/sortablesubscriptionlist.php @@ -100,7 +100,7 @@ class SortableSubscriptionList extends SubscriptionList $this->out->element('th', array('id' => 'subscriptions'), _m('Subscriptions')); // TRANS: Column header for number of notices. $this->out->element('th', array('id' => 'notices'), _m('Notices')); - $this->out->element('th', array('id' => 'controls'), null); + $this->out->element('th', array('id' => 'controls')); $this->out->elementEnd('tr'); $this->out->elementEnd('thead'); diff --git a/plugins/Oembed/OembedPlugin.php b/plugins/Oembed/OembedPlugin.php index 3ccca20538..ce4803c506 100644 --- a/plugins/Oembed/OembedPlugin.php +++ b/plugins/Oembed/OembedPlugin.php @@ -103,7 +103,7 @@ class OembedPlugin extends Plugin array('format'=>'json', 'url'=> common_local_url('attachment', array('attachment' => $action->attachment->getID())))), - 'title'=>'oEmbed'),null); + 'title'=>'oEmbed')); $action->element('link',array('rel'=>'alternate', 'type'=>'text/xml+oembed', 'href'=>common_local_url( @@ -112,7 +112,7 @@ class OembedPlugin extends Plugin array('format'=>'xml','url'=> common_local_url('attachment', array('attachment' => $action->attachment->getID())))), - 'title'=>'oEmbed'),null); + 'title'=>'oEmbed')); break; case 'shownotice': if (!$action->notice->isLocal()) { @@ -125,14 +125,14 @@ class OembedPlugin extends Plugin 'oembed', array(), array('format'=>'json','url'=>$action->notice->getUrl())), - 'title'=>'oEmbed'),null); + 'title'=>'oEmbed')); $action->element('link',array('rel'=>'alternate', 'type'=>'text/xml+oembed', 'href'=>common_local_url( 'oembed', array(), array('format'=>'xml','url'=>$action->notice->getUrl())), - 'title'=>'oEmbed'),null); + 'title'=>'oEmbed')); } catch (InvalidUrlException $e) { // The notice is probably a share or similar, which don't // have a representational URL of their own. diff --git a/plugins/OpenID/actions/openidlogin.php b/plugins/OpenID/actions/openidlogin.php index b06189e2fd..bb948172a4 100644 --- a/plugins/OpenID/actions/openidlogin.php +++ b/plugins/OpenID/actions/openidlogin.php @@ -137,8 +137,8 @@ class OpenidloginAction extends Action $appendUsername = common_config('openid', 'append_username'); if ($provider) { // TRANS: Field label. - $this->element('label', array(), _m('LABEL','OpenID provider')); - $this->element('span', array(), $provider); + $this->element('label', [], _m('LABEL','OpenID provider')); + $this->element('span', [], $provider); if ($appendUsername) { $this->element('input', array('id' => 'openid_username', 'name' => 'openid_username',