X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=lib%2Faction.php;h=c2d60e11e974bec1414fe2b9b143b4babdb4ce0d;hb=0baa9debbca911ba43b289624b5acbbb94469828;hp=aa85c40f22fdbd1a41b529c9c1f62131bb57a08c;hpb=a72dbc1aff774446c0c31bb219d748484453b59e;p=quix0rs-gnu-social.git diff --git a/lib/action.php b/lib/action.php index aa85c40f22..c2d60e11e9 100644 --- a/lib/action.php +++ b/lib/action.php @@ -28,12 +28,7 @@ * @link http://status.net/ */ -if (!defined('STATUSNET') && !defined('LACONICA')) { - exit(1); -} - -require_once INSTALLDIR.'/lib/noticeform.php'; -require_once INSTALLDIR.'/lib/htmloutputter.php'; +if (!defined('GNUSOCIAL')) { exit(1); } /** * Base class for all actions @@ -55,7 +50,24 @@ require_once INSTALLDIR.'/lib/htmloutputter.php'; */ class Action extends HTMLOutputter // lawsuit { - var $args; + // This should be protected/private in the future + public $args = array(); + + // Action properties, set per-class + protected $action = false; + 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? + + // The currently scoped profile (normally Profile::current; from $this->auth_user for API) + protected $scoped = null; + + // Related to front-end user representation + protected $format = null; + protected $error = null; + protected $msg = null; /** * Constructor @@ -73,6 +85,52 @@ class Action extends HTMLOutputter // lawsuit parent::__construct($output, $indent); } + function getError() + { + return $this->error; + } + + function getInfo() + { + return $this->msg; + } + + static public function run(array $args=array(), $output='php://output', $indent=null) { + $class = get_called_class(); + $action = new $class($output, $indent); + $action->execute($args); + return $action; + } + + public function execute(array $args=array()) { + // checkMirror stuff + if (common_config('db', 'mirror') && $this->isReadOnly($args)) { + if (is_array(common_config('db', 'mirror'))) { + // "load balancing", ha ha + $arr = common_config('db', 'mirror'); + $k = array_rand($arr); + $mirror = $arr[$k]; + } else { + $mirror = common_config('db', 'mirror'); + } + + // everyone else uses the mirror + common_config_set('db', 'database', $mirror); + } + + if (Event::handle('StartActionExecute', array($this, &$args))) { + $prepared = $this->prepare($args); + if ($prepared) { + $this->handle($args); + } else { + common_debug('Prepare failed for Action.'); + } + + $this->flush(); + Event::handle('EndActionExecute', array($this)); + } + } + /** * For initializing members of the class. * @@ -80,25 +138,87 @@ class Action extends HTMLOutputter // lawsuit * * @return boolean true */ - function prepare($argarray) + protected function prepare(array $args=array()) { - $this->args =& common_copy_args($argarray); + if ($this->needPost && !$this->isPost()) { + // TRANS: Client error. POST is a HTTP command. It should not be translated. + $this->clientError(_('This method requires a POST.'), 405); + } + + // needPost, of course, overrides canPost if true + if (!$this->canPost) { + $this->canPost = $this->needPost; + } + + $this->args = common_copy_args($args); + + // This could be set with get_called_action and then + // chop off 'Action' from the class name. In lower case. + $this->action = strtolower($this->trimmed('action')); + + if ($this->ajax || $this->boolean('ajax')) { + // check with GNUsocial::isAjax() + GNUsocial::setAjax(true); + } + + if ($this->needLogin) { + $this->checkLogin(); // if not logged in, this redirs/excepts + } + + $this->updateScopedProfile(); + return true; } + public function updateScopedProfile() + { + $this->scoped = Profile::current(); + return $this->scoped; + } + + 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) { + // PHP is case insensitive, and we have stuff like ApiUpperCaseAction, + // but we at least make a point out of wanting to do stuff case-sensitive. + $class = ucfirst($class) . 'Action'; + if ($this instanceof $class) { + return true; + } + } + return false; + } + /** * Show page, a template method. * * @return nothing */ - function showPage() + public function showPage() { + if (GNUsocial::isAjax()) { + self::showAjax(); + return; + } if (Event::handle('StartShowHTML', array($this))) { $this->startHTML(); + $this->flush(); Event::handle('EndShowHTML', array($this)); } if (Event::handle('StartShowHead', array($this))) { $this->showHead(); + $this->flush(); Event::handle('EndShowHead', array($this)); } if (Event::handle('StartShowBody', array($this))) { @@ -111,6 +231,23 @@ class Action extends HTMLOutputter // lawsuit } } + public function showAjax() + { + $this->startHTML('text/xml;charset=utf-8'); + $this->elementStart('head'); + // TRANS: Title for conversation page. + $this->element('title', null, $this->title()); + $this->elementEnd('head'); + $this->elementStart('body'); + if ($this->getError()) { + $this->element('p', array('id'=>'error'), $this->getError()); + } else { + $this->showContent(); + } + $this->elementEnd('body'); + $this->endHTML(); + } + function endHTML() { global $_startTime; @@ -158,7 +295,7 @@ class Action extends HTMLOutputter // lawsuit { $this->element('title', null, // TRANS: Page title. %1$s is the title, %2$s is the site name. - sprintf(_("%1\$s - %2\$s"), + sprintf(_('%1$s - %2$s'), $this->title(), common_config('site', 'name'))); } @@ -174,7 +311,7 @@ class Action extends HTMLOutputter // lawsuit function title() { // TRANS: Page title for a page without a title set. - return _("Untitled page"); + return _('Untitled page'); } /** @@ -190,7 +327,7 @@ class Action extends HTMLOutputter // lawsuit } 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', StatusNet::isHTTPS()))); + 'href' => common_path('favicon.ico', GNUsocial::isHTTPS()))); } if (common_config('site', 'mobile')) { @@ -215,44 +352,17 @@ class Action extends HTMLOutputter // lawsuit // Use old name for StatusNet for compatibility on events - if (Event::handle('StartShowStatusNetStyles', array($this)) && - Event::handle('StartShowLaconicaStyles', array($this))) { + if (Event::handle('StartShowStylesheets', array($this))) { $this->primaryCssLink(null, 'screen, projection, tv, print'); - Event::handle('EndShowStatusNetStyles', array($this)); - Event::handle('EndShowLaconicaStyles', array($this)); + Event::handle('EndShowStylesheets', array($this)); } - $this->cssLink(common_path('js/css/smoothness/jquery-ui.css')); + $this->cssLink('js/extlib/jquery-ui/css/smoothness/jquery-ui.css'); if (Event::handle('StartShowUAStyles', array($this))) { - $this->comment('[if IE]>comment('[if lte IE '.$ver.']>comment('[if IE]>viewdesigns) { - $design = $this->getDesign(); - - if (!empty($design)) { - $design->showCSS($this); - } - } - - Event::handle('EndShowDesign', array($this)); - } Event::handle('EndShowStyles', array($this)); if (common_config('custom_css', 'enabled')) { @@ -284,6 +394,13 @@ class Action extends HTMLOutputter // lawsuit $this->cssLink('css/display.css', $baseTheme, $media); } $this->cssLink('css/display.css', $mainTheme, $media); + + // Additional styles for RTL languages + if (is_rtl(common_language())) { + if (file_exists(Theme::file('css/rtl.css'))) { + $this->cssLink('css/rtl.css', $mainTheme, $media); + } + } } /** @@ -295,37 +412,33 @@ class Action extends HTMLOutputter // lawsuit { if (Event::handle('StartShowScripts', array($this))) { if (Event::handle('StartShowJQueryScripts', array($this))) { - if (common_config('site', 'minify')) { - $this->script('jquery.min.js'); - $this->script('jquery.form.min.js'); - $this->script('jquery-ui.min.js'); - $this->script('jquery.cookie.min.js'); - $this->inlineScript('if (typeof window.JSON !== "object") { $.getScript("'.common_path('js/json2.min.js').'"); }'); - $this->script('jquery.joverlay.min.js'); - } else { - $this->script('jquery.js'); - $this->script('jquery.form.js'); - $this->script('jquery-ui.min.js'); - $this->script('jquery.cookie.js'); - $this->inlineScript('if (typeof window.JSON !== "object") { $.getScript("'.common_path('js/json2.js').'"); }'); - $this->script('jquery.joverlay.js'); - } + $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'); + $this->inlineScript('if (typeof window.JSON !== "object") { $.getScript("'.common_path('js/extlib/json2.js', GNUsocial::isHTTPS()).'"); }'); + Event::handle('EndShowJQueryScripts', array($this)); } - if (Event::handle('StartShowStatusNetScripts', array($this)) && - Event::handle('StartShowLaconicaScripts', array($this))) { - if (common_config('site', 'minify')) { - $this->script('util.min.js'); - } else { - $this->script('util.js'); - $this->script('xbImportNode.js'); - $this->script('geometa.js'); - } + if (Event::handle('StartShowStatusNetScripts', array($this))) { + $this->script('util.js'); + $this->script('xbImportNode.js'); + $this->script('geometa.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(); - // Frame-busting code to avoid clickjacking attacks. - $this->inlineScript('if (window.top !== window.self) { window.top.location.href = window.self.location.href; }'); + $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('EndShowLaconicaScripts', array($this)); } Event::handle('EndShowScripts', array($this)); } @@ -338,7 +451,6 @@ class Action extends HTMLOutputter // lawsuit * 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(); @@ -348,16 +460,11 @@ class Action extends HTMLOutputter // lawsuit // TRANS: Localized tooltip for '...' expansion button on overlong remote messages. $messages['showmore_tooltip'] = _m('TOOLTIP', 'Show more'); - - // TRANS: Inline reply form submit button: submits a reply comment. - $messages['reply_submit'] = _m('BUTTON', 'Reply'); - - // TRANS: Placeholder text for inline reply form. Clicking in this box will turn it into a mini notice form. - $messages['reply_placeholder'] = _m('Write a reply...'); + $messages['popup_close_button'] = _m('TOOLTIP', 'Close popup'); $messages = array_merge($messages, $this->getScriptMessages()); - Event::handle('EndScriptMessages', array($this, &$messages)); + Event::handle('EndScriptMessages', array($this, &$messages)); } if (!empty($messages)) { @@ -367,6 +474,19 @@ class Action extends HTMLOutputter // lawsuit return $messages; } + protected function showScriptVariables() + { + $vars = array(); + + if (Event::handle('StartScriptVariables', array($this, &$vars))) { + $vars['urlNewNotice'] = common_local_url('newnotice'); + } + if (!empty($vars)) { + $this->inlineScript('SN.V = ' . json_encode($vars)); + } + return $vars; + } + /** * If the action will need localizable text strings, export them here like so: * @@ -415,15 +535,11 @@ class Action extends HTMLOutputter // lawsuit */ function showFeeds() { - $feeds = $this->getFeeds(); - - if ($feeds) { - foreach ($feeds as $feed) { - $this->element('link', array('rel' => $feed->rel(), - 'href' => $feed->url, - 'type' => $feed->mimeType(), - 'title' => $feed->title)); - } + foreach ($this->getFeeds() as $feed) { + $this->element('link', array('rel' => $feed->rel(), + 'href' => $feed->url, + 'type' => $feed->mimeType(), + 'title' => $feed->title)); } } @@ -460,17 +576,22 @@ class Action extends HTMLOutputter // lawsuit */ function showBody() { - $this->elementStart('body', (common_current_user()) ? array('id' => strtolower($this->trimmed('action')), - 'class' => 'user_in') - : array('id' => strtolower($this->trimmed('action')))); + $params = array('id' => $this->getActionName()); + if ($this->scoped instanceof Profile) { + $params['class'] = 'user_in'; + } + $this->elementStart('body', $params); $this->elementStart('div', array('id' => 'wrap')); if (Event::handle('StartShowHeader', array($this))) { $this->showHeader(); + $this->flush(); Event::handle('EndShowHeader', array($this)); } $this->showCore(); + $this->flush(); if (Event::handle('StartShowFooter', array($this))) { $this->showFooter(); + $this->flush(); Event::handle('EndShowFooter', array($this)); } $this->elementEnd('div'); @@ -506,8 +627,7 @@ class Action extends HTMLOutputter // lawsuit */ function showLogo() { - $this->elementStart('address', array('id' => 'site_contact', - 'class' => 'vcard')); + $this->elementStart('address', array('id' => 'site_contact', 'class' => 'h-card')); if (Event::handle('StartAddressData', array($this))) { if (common_config('singleuser', 'enabled')) { $user = User::singleUser(); @@ -520,16 +640,16 @@ class Action extends HTMLOutputter // lawsuit $url = common_local_url('public'); } - $this->elementStart('a', array('class' => 'url home bookmark', + $this->elementStart('a', array('class' => 'home bookmark', 'href' => $url)); - if (StatusNet::isHTTPS()) { + if (GNUsocial::isHTTPS()) { $logoUrl = common_config('site', 'ssllogo'); if (empty($logoUrl)) { // if logo is an uploaded file, try to fall back to HTTPS file URL $httpUrl = common_config('site', 'logo'); if (!empty($httpUrl)) { - $f = File::staticGet('url', $httpUrl); + $f = File::getKV('url', $httpUrl); if (!empty($f) && !empty($f->filename)) { // this will handle the HTTPS case $logoUrl = File::url($f->filename); @@ -546,13 +666,11 @@ class Action extends HTMLOutputter // lawsuit } if (!empty($logoUrl)) { - $this->element('img', array('class' => 'logo photo', + $this->element('img', array('class' => 'logo u-photo p-name', 'src' => $logoUrl, 'alt' => common_config('site', 'name'))); } - $this->text(' '); - $this->element('span', array('class' => 'fn org'), common_config('site', 'name')); $this->elementEnd('a'); Event::handle('EndAddressData', array($this)); @@ -568,6 +686,14 @@ class Action extends HTMLOutputter // lawsuit function showPrimaryNav() { $this->elementStart('div', array('id' => 'site_nav_global_primary')); + + $user = common_current_user(); + + if (!empty($user) || !common_config('site', 'private')) { + $form = new SearchForm($this); + $form->show(); + } + $pn = new PrimaryNav($this); $pn->show(); $this->elementEnd('div'); @@ -599,47 +725,43 @@ class Action extends HTMLOutputter // lawsuit */ function showNoticeForm() { - $tabs = array('status' => _('Status')); + // TRANS: Tab on the notice form. + $tabs = array('status' => array('title' => _m('TAB','Status'), + 'href' => common_local_url('newnotice'))); $this->elementStart('div', 'input_forms'); - if (Event::handle('StartShowEntryForms', array(&$tabs))) { + $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 => $title) { - + foreach ($tabs as $tag => $data) { + $tag = htmlspecialchars($tag); $attrs = array('id' => 'input_form_nav_'.$tag, 'class' => 'input_form_nav_tab'); if ($tag == 'status') { - // We're actually showing the placeholder form, - // but we special-case the 'Status' tab as if - // it were a small version of it. $attrs['class'] .= ' current'; } $this->elementStart('li', $attrs); $this->element('a', - array('href' => 'javascript:SN.U.switchInputFormTab("'.$tag.'")'), - $title); + array('onclick' => 'return SN.U.switchInputFormTab("'.$tag.'");', + 'href' => $data['href']), + $data['title']); $this->elementEnd('li'); } $this->elementEnd('ul'); - $attrs = array('class' => 'input_form current', - 'id' => 'input_form_placeholder'); - $this->elementStart('div', $attrs); - $form = new NoticePlaceholderForm($this); - $form->show(); - $this->elementEnd('div'); - - foreach ($tabs as $tag => $title) { - + foreach ($tabs as $tag => $data) { $attrs = array('class' => 'input_form', 'id' => 'input_form_'.$tag); + if ($tag == 'status') { + $attrs['class'] .= ' current'; + } $this->elementStart('div', $attrs); @@ -647,7 +769,8 @@ class Action extends HTMLOutputter // lawsuit if (Event::handle('StartMakeEntryForm', array($tag, $this, &$form))) { if ($tag == 'status') { - $form = new NoticeForm($this); + $options = $this->noticeFormOptions(); + $form = new NoticeForm($this, $options); } Event::handle('EndMakeEntryForm', array($tag, $this, $form)); } @@ -663,6 +786,11 @@ class Action extends HTMLOutputter // lawsuit $this->elementEnd('div'); } + function noticeFormOptions() + { + return array(); + } + /** * Show anonymous message. * @@ -690,14 +818,17 @@ class Action extends HTMLOutputter // lawsuit $this->elementStart('div', array('id' => 'site_nav_local_views_wrapper')); if (Event::handle('StartShowLocalNavBlock', array($this))) { $this->showLocalNavBlock(); + $this->flush(); Event::handle('EndShowLocalNavBlock', array($this)); } if (Event::handle('StartShowContentBlock', array($this))) { $this->showContentBlock(); + $this->flush(); Event::handle('EndShowContentBlock', array($this)); } if (Event::handle('StartShowAside', array($this))) { $this->showAside(); + $this->flush(); Event::handle('EndShowAside', array($this)); } $this->elementEnd('div'); @@ -717,7 +848,6 @@ class Action extends HTMLOutputter // lawsuit // all menus $this->elementStart('div', array('id' => 'site_nav_local_views')); // Cheat cheat cheat! - $this->showProfileBlock(); $this->showLocalNav(); $this->elementEnd('div'); } @@ -727,7 +857,6 @@ class Action extends HTMLOutputter // lawsuit * * @return nothing */ - function showProfileBlock() { if (common_logged_in()) { @@ -866,7 +995,7 @@ class Action extends HTMLOutputter // lawsuit * * @return nothing */ - function showContent() + protected function showContent() { } @@ -879,6 +1008,7 @@ class Action extends HTMLOutputter // lawsuit { $this->elementStart('div', array('id' => 'aside_primary', 'class' => 'aside')); + $this->showProfileBlock(); if (Event::handle('StartShowObjectNavBlock', array($this))) { $this->showObjectNavBlock(); Event::handle('EndShowObjectNavBlock', array($this)); @@ -902,9 +1032,9 @@ class Action extends HTMLOutputter // lawsuit function showExportData() { $feeds = $this->getFeeds(); - if ($feeds) { - $fl = new FeedList($this); - $fl->show($feeds); + if (!empty($feeds)) { + $fl = new FeedList($this, $feeds); + $fl->show(); } } @@ -954,33 +1084,33 @@ class Action extends HTMLOutputter // lawsuit */ function showLicenses() { - $this->showStatusNetLicense(); + $this->showGNUsocialLicense(); $this->showContentLicense(); } /** - * Show StatusNet license. + * Show GNU social license. * * @return nothing */ - function showStatusNetLicense() + function showGNUsocialLicense() { if (common_config('site', 'broughtby')) { - // TRANS: First sentence of the StatusNet site license. Used if 'broughtby' is set. + // TRANS: First sentence of the GNU social site license. Used if 'broughtby' is set. // TRANS: Text between [] is a link description, text between () is the link itself. // TRANS: Make sure there is no whitespace between "]" and "(". // TRANS: "%%site.broughtby%%" is the value of the variable site.broughtby - $instr = _('**%%site.name%%** is a microblogging service brought to you by [%%site.broughtby%%](%%site.broughtbyurl%%).'); + $instr = _('**%%site.name%%** is a social network, courtesy of [%%site.broughtby%%](%%site.broughtbyurl%%).'); } else { - // TRANS: First sentence of the StatusNet site license. Used if 'broughtby' is not set. - $instr = _('**%%site.name%%** is a microblogging service.'); + // TRANS: First sentence of the GNU social site license. Used if 'broughtby' is not set. + $instr = _('**%%site.name%%** is a social network.'); } $instr .= ' '; - // TRANS: Second sentence of the StatusNet site license. Mentions the StatusNet source code license. + // TRANS: Second sentence of the GNU social site license. Mentions the GNU social source code license. // TRANS: Make sure there is no whitespace between "]" and "(". - // TRANS: Text between [] is a link description, text between () is the link itself. - // TRANS: %s is the version of StatusNet that is being used. - $instr .= sprintf(_('It runs the [StatusNet](http://status.net/) microblogging software, version %s, available under the [GNU Affero General Public License](http://www.fsf.org/licensing/licenses/agpl-3.0.html).'), STATUSNET_VERSION); + // TRANS: [%1$s](%2$s) is a link description followed by the link itself + // TRANS: %3$s is the version of GNU social that is being used. + $instr .= sprintf(_('It runs on [%1$s](%2$s), version %3$s, available under the [GNU Affero General Public License](http://www.fsf.org/licensing/licenses/agpl-3.0.html).'), GNUSOCIAL_ENGINE, GNUSOCIAL_ENGINE_URL, GNUSOCIAL_VERSION); $output = common_markup_to_html($instr); $this->raw($output); // do it @@ -1019,7 +1149,7 @@ class Action extends HTMLOutputter // lawsuit $image = common_config('license', 'image'); $sslimage = common_config('license', 'sslimage'); - if (StatusNet::isHTTPS()) { + if (GNUsocial::isHTTPS()) { if (!empty($sslimage)) { $url = $sslimage; } else if (preg_match('#^http://i.creativecommons.org/#', $image)) { @@ -1042,12 +1172,10 @@ class Action extends HTMLOutputter // lawsuit // 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 = "" . - htmlspecialchars(common_config('license', 'title')) . - ""; - $this->raw(sprintf(htmlspecialchars($notice), + $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'); @@ -1132,11 +1260,9 @@ class Action extends HTMLOutputter // lawsuit /** * Handler method * - * @param array $argarray is ignored since it's now passed in in prepare() - * * @return boolean is read only action? */ - function handle($argarray=null) + protected function handle() { header('Vary: Accept-Encoding,Cookie'); @@ -1233,6 +1359,19 @@ class Action extends HTMLOutputter // lawsuit } } + /** + * This is a cheap hack to avoid a bug in DB_DataObject + * where '' is non-type-aware compared to 0, which means it + * will always be true for values like false and 0 too... + * + * Upstream bug is:: + * https://pear.php.net/bugs/bug.php?id=20291 + */ + function booleanintstring($key, $def=false) + { + return $this->boolean($key, $def) ? '1' : '0'; + } + /** * Integer value of an argument * @@ -1245,9 +1384,9 @@ class Action extends HTMLOutputter // lawsuit */ function int($key, $defValue=null, $maxValue=null, $minValue=null) { - $arg = strtolower($this->trimmed($key)); + $arg = intval($this->arg($key)); - if (is_null($arg) || !is_integer($arg)) { + if (!is_numeric($this->arg($key)) || $arg != $this->arg($key)) { return $defValue; } @@ -1270,26 +1409,121 @@ class Action extends HTMLOutputter // lawsuit * * @return nothing */ - function serverError($msg, $code=500) + function serverError($msg, $code=500, $format=null) { - $action = $this->trimmed('action'); - common_debug("Server error '$code' on '$action': $msg", __FILE__); - throw new ServerException($msg, $code); + 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: + throw new ServerException($msg, $code); + } + + exit((int)$code); } /** * Client error * - * @param string $msg error message to display - * @param integer $code http error code, 400 by default + * @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: + throw new ClientException($msg, $code); + } + 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) */ - function clientError($msg, $code=400) + public function checkLogin($redir=true) { - $action = $this->trimmed('action'); - common_debug("User error '$code' on '$action': $msg", __FILE__); - throw new ClientException($msg, $code); + 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); } /** @@ -1310,7 +1544,7 @@ class Action extends HTMLOutputter // lawsuit */ function returnToArgs() { - $action = $this->trimmed('action'); + $action = $this->getActionName(); $args = $this->args; unset($args['action']); if (common_config('site', 'fancy')) { @@ -1336,16 +1570,26 @@ class Action extends HTMLOutputter // lawsuit * * @return nothing */ - function menuItem($url, $text, $title=null, $is_selected=false, $id=null) + 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(); + $lattrs = array(); + $classes = array(); + if ($class !== null) { + $classes[] = trim($class); + } if ($is_selected) { - $lattrs['class'] = 'current'; + $classes[] = 'current'; + } + + if (!empty($classes)) { + $lattrs['class'] = implode(' ', $classes); } - (is_null($id)) ? $lattrs : $lattrs['id'] = $id; + if (!is_null($id)) { + $lattrs['id'] = $id; + } $this->elementStart('li', $lattrs); $attrs['href'] = $url; @@ -1411,17 +1655,7 @@ class Action extends HTMLOutputter // lawsuit */ function getFeeds() { - return null; - } - - /** - * A design for this action - * - * @return Design a design object to use - */ - function getDesign() - { - return Design::siteDesign(); + return array(); } /**