diff options
author | Evan Prodromou <evan@controlyourself.ca> | 2009-01-20 23:13:02 -0500 |
---|---|---|
committer | Evan Prodromou <evan@controlyourself.ca> | 2009-01-20 23:13:02 -0500 |
commit | 4b9df58c90e25ea79aeec64c6e96f828fe06d7df (patch) | |
tree | ac237014d015535d07babba42d40d86608de0ec5 /lib/action.php | |
parent | bd056218f9a595184ac7bcac0c1dc6a8981d7af2 (diff) | |
parent | 65bfda33b8464caf14ce268c9bea2e7eceb27fe5 (diff) |
Merge branch 'master' into groups
Conflicts:
.gitignore
Diffstat (limited to 'lib/action.php')
-rw-r--r-- | lib/action.php | 519 |
1 files changed, 494 insertions, 25 deletions
diff --git a/lib/action.php b/lib/action.php index 486b40387..5987abf3c 100644 --- a/lib/action.php +++ b/lib/action.php @@ -1,9 +1,12 @@ <?php /** - * Laconica - a distributed open-source microblogging tool - * Copyright (C) 2008, Controlez-Vous, Inc. + * Laconica, the distributed open-source microblogging tool * - * This program is free software: you can redistribute it and/or modify + * Base class for all actions (~views) + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. @@ -15,19 +18,61 @@ * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Action + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @author Sarven Capadisli <csarven@controlyourself.ca> + * @copyright 2008 Control Yourself, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ */ if (!defined('LACONICA')) { exit(1); } -class Action // lawsuit -{ +require_once INSTALLDIR.'/lib/noticeform.php'; +require_once INSTALLDIR.'/lib/htmloutputter.php'; +/** + * Base class for all actions + * + * This is the base class for all actions in the package. An action is + * more or less a "view" in an MVC framework. + * + * Actions are responsible for extracting and validating parameters; using + * model classes to read and write to the database; and doing ouput. + * + * @category Output + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @author Sarven Capadisli <csarven@controlyourself.ca> + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://laconi.ca/ + * + * @see HTMLOutputter + */ + +class Action extends HTMLOutputter // lawsuit +{ var $args; - function Action() + /** + * Constructor + * + * Just wraps the HTMLOutputter constructor. + * + * @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=true) { + parent::__construct($output, $indent); } // For initializing members of the class @@ -38,10 +83,388 @@ class Action // lawsuit return true; } + function showPage() + { + $this->startHTML(); + $this->showHead(); + $this->showBody(); + $this->endHTML(); + } + + function showHead() + { + // XXX: attributes (profile?) + $this->elementStart('head'); + $this->showTitle(); + $this->showStylesheets(); + $this->showScripts(); + $this->showOpenSearch(); + $this->showFeeds(); + $this->showDescription(); + $this->extraHead(); + $this->elementEnd('head'); + } + + function showTitle() + { + $this->element('title', null, + sprintf(_("%s - %s"), + $this->title(), + common_config('site', 'name'))); + } + + // SHOULD overload + + function title() + { + return _("Untitled page"); + } + + function showStylesheets() + { + $this->element('link', array('rel' => 'stylesheet', + 'type' => 'text/css', + 'href' => theme_path('css/display.css', 'base') . '?version=' . LACONICA_VERSION, + 'media' => 'screen, projection, tv')); + $this->element('link', array('rel' => 'stylesheet', + 'type' => 'text/css', + 'href' => theme_path('css/thickbox.css', 'base') . '?version=' . LACONICA_VERSION, + 'media' => 'screen, projection, tv')); + $this->element('link', array('rel' => 'stylesheet', + 'type' => 'text/css', + 'href' => theme_path('css/display.css', null) . '?version=' . LACONICA_VERSION, + 'media' => 'screen, projection, tv')); + $this->comment('[if IE]><link rel="stylesheet" type="text/css" '. + 'href="'.theme_path('css/ie.css', 'base').'?version='.LACONICA_VERSION.'" /><![endif]'); + foreach (array(6,7) as $ver) { + if (file_exists(theme_file('ie'.$ver.'.css'))) { + // Yes, IE people should be put in jail. + $this->comment('[if lte IE '.$ver.']><link rel="stylesheet" type="text/css" '. + 'href="'.theme_path('css/ie'.$ver.'.css', 'base').'?version='.LACONICA_VERSION.'" /><![endif]'); + } + } + } + + function showScripts() + { + $this->element('script', array('type' => 'text/javascript', + 'src' => common_path('js/jquery.min.js')), + ' '); + $this->element('script', array('type' => 'text/javascript', + 'src' => common_path('js/jquery.form.js')), + ' '); + $this->element('script', array('type' => 'text/javascript', + 'src' => common_path('js/xbImportNode.js')), + ' '); + $this->element('script', array('type' => 'text/javascript', + 'src' => common_path('js/util.js?version='.LACONICA_VERSION)), + ' '); + } + + 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')); + + $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')); + } + + // MAY overload + + function showFeeds() + { + // does nothing by default + } + + // SHOULD overload + + function showDescription() + { + // does nothing by default + } + + // MAY overload + + function extraHead() + { + // does nothing by default + } + + function showBody() + { + $this->elementStart('body', array('id' => $this->trimmed('action'))); + $this->elementStart('div', 'wrap'); + $this->showHeader(); + $this->showCore(); + $this->showFooter(); + $this->elementEnd('div'); + $this->elementEnd('body'); + } + + function showHeader() + { + $this->elementStart('div', array('id' => 'header')); + $this->showLogo(); + $this->showPrimaryNav(); + $this->showSiteNotice(); + if (common_logged_in()) { + $this->showNoticeForm(); + } else { + $this->showAnonymousMessage(); + } + $this->elementEnd('div'); + } + + function showLogo() + { + $this->elementStart('address', array('id' => 'site_contact', + 'class' => 'vcard')); + $this->elementStart('a', array('class' => 'url home bookmark', + 'href' => common_local_url('public'))); + if (common_config('site', 'logo') || file_exists(theme_file('logo.png'))) + { + $this->element('img', array('class' => 'logo photo', + 'src' => (common_config('site', 'logo')) ? common_config('site', 'logo') : theme_path('logo.png'), + 'alt' => common_config('site', 'name'))); + } + $this->element('span', array('class' => 'fn org'), common_config('site', 'name')); + $this->elementEnd('a'); + $this->elementEnd('address'); + } + + function showPrimaryNav() + { + $this->elementStart('dl', array('id' => 'site_nav_global_primary')); + $this->element('dt', null, _('Primary site navigation')); + $this->elementStart('dd'); + $user = common_current_user(); + $this->elementStart('ul', array('class' => 'nav')); + if ($user) { + $this->menuItem(common_local_url('all', array('nickname' => $user->nickname)), + _('Home'), _('Personal profile and friends timeline'), false, 'nav_home'); + } + $this->menuItem(common_local_url('peoplesearch'), + _('Search'), _('Search for people or text'), false, 'nav_search'); + if ($user) { + $this->menuItem(common_local_url('profilesettings'), + _('Account'), _('Change your email, avatar, password, profile'), false, 'nav_account'); + $this->menuItem(common_local_url('imsettings'), + _('Connect'), _('Connect to IM, SMS, Twitter'), false, 'nav_connect'); + $this->menuItem(common_local_url('logout'), + _('Logout'), _('Logout from the site'), false, 'nav_logout'); + } else { + $this->menuItem(common_local_url('login'), + _('Login'), _('Login to the site'), false, 'nav_login'); + if (!common_config('site', 'closed')) { + $this->menuItem(common_local_url('register'), + _('Register'), _('Create an account'), false, 'nav_register'); + } + $this->menuItem(common_local_url('openidlogin'), + _('OpenID'), _('Login with OpenID'), false, 'nav_openid'); + } + $this->menuItem(common_local_url('doc', array('title' => 'help')), + _('Help'), _('Help me!'), false, 'nav_help'); + $this->elementEnd('ul'); + $this->elementEnd('dd'); + $this->elementEnd('dl'); + } + + // Revist. Should probably do an hAtom pattern here + function showSiteNotice() + { + $text = common_config('site', 'notice'); + if ($text) { + $this->elementStart('dl', array('id' => 'site_notice', + 'class' => 'system_notice')); + $this->element('dt', null, _('Site notice')); + $this->element('dd', null, $text); + $this->elementEnd('dl'); + } + } + + // MAY overload if no notice form needed... or direct message box???? + + function showNoticeForm() + { + $notice_form = new NoticeForm($this); + $notice_form->show(); + } + + function showAnonymousMessage() + { + // needs to be defined by the class + } + + function showCore() + { + $this->elementStart('div', array('id' => 'core')); + $this->showLocalNavBlock(); + $this->showContentBlock(); + $this->showAside(); + $this->elementEnd('div'); + } + + function showLocalNavBlock() + { + $this->elementStart('dl', array('id' => 'site_nav_local_views')); + $this->element('dt', null, _('Local views')); + $this->elementStart('dd'); + $this->showLocalNav(); + $this->elementEnd('dd'); + $this->elementEnd('dl'); + } + + // SHOULD overload + + function showLocalNav() + { + // does nothing by default + } + + function showContentBlock() + { + $this->elementStart('div', array('id' => 'content')); + $this->showPageTitle(); + $this->showPageNoticeBlock(); + $this->elementStart('div', array('id' => 'content_inner')); + // show the actual content (forms, lists, whatever) + $this->showContent(); + $this->elementEnd('div'); + $this->elementEnd('div'); + } + + function showPageTitle() { + $this->element('h1', NULL, $this->title()); + } + + function showPageNoticeBlock() + { + $this->elementStart('dl', array('id' => 'page_notice', + 'class' => 'system_notice')); + $this->element('dt', null, _('Page notice')); + $this->elementStart('dd'); + $this->showPageNotice(); + $this->elementEnd('dd'); + $this->elementEnd('dl'); + } + + // SHOULD overload (unless there's not a notice) + + function showPageNotice() + { + } + + // MUST overload + + function showContent() + { + } + + function showAside() + { + $this->elementStart('div', array('id' => 'aside_primary', + 'class' => 'aside')); + $this->showExportData(); + $this->showSections(); + $this->elementEnd('div'); + } + + // MAY overload if there are feeds + + function showExportData() + { + // is there structure to this? + // list of (visible!) feed links + // can we reuse list of feeds from showFeeds() ? + } + + // SHOULD overload + + function showSections() { + // for each section, show it + } + + function showFooter() + { + $this->elementStart('div', array('id' => 'footer')); + $this->showSecondaryNav(); + $this->showLicenses(); + $this->elementEnd('div'); + } + + function showSecondaryNav() + { + $this->elementStart('dl', array('id' => 'site_nav_global_secondary')); + $this->element('dt', null, _('Secondary site navigation')); + $this->elementStart('dd', null); + $this->elementStart('ul', array('class' => 'nav')); + $this->menuItem(common_local_url('doc', array('title' => 'help')), + _('Help')); + $this->menuItem(common_local_url('doc', array('title' => 'about')), + _('About')); + $this->menuItem(common_local_url('doc', array('title' => 'faq')), + _('FAQ')); + $this->menuItem(common_local_url('doc', array('title' => 'privacy')), + _('Privacy')); + $this->menuItem(common_local_url('doc', array('title' => 'source')), + _('Source')); + $this->menuItem(common_local_url('doc', array('title' => 'contact')), + _('Contact')); + $this->elementEnd('ul'); + $this->elementEnd('dd'); + $this->elementEnd('dl'); + } + + function showLicenses() + { + $this->elementStart('dl', array('id' => 'licenses')); + $this->showLaconicaLicense(); + $this->showContentLicense(); + $this->elementEnd('dl'); + } + + function showLaconicaLicense() + { + $this->element('dt', array('id' => 'site_laconica_license'), _('Laconica software license')); + $this->elementStart('dd', null); + if (common_config('site', 'broughtby')) { + $instr = _('**%%site.name%%** is a microblogging service brought to you by [%%site.broughtby%%](%%site.broughtbyurl%%). '); + } else { + $instr = _('**%%site.name%%** is a microblogging service. '); + } + $instr .= sprintf(_('It runs the [Laconica](http://laconi.ca/) microblogging software, version %s, available under the [GNU Affero General Public License](http://www.fsf.org/licensing/licenses/agpl-3.0.html).'), LACONICA_VERSION); + $output = common_markup_to_html($instr); + $this->raw($output); + $this->elementEnd('dd'); + // do it + } + + function showContentLicense() + { + $this->element('dt', array('id' => 'site_content_license'), _('Laconica software license')); + $this->elementStart('dd', array('id' => 'site_content_license_cc')); + $this->elementStart('p'); + $this->element('img', array('id' => 'license_cc', + 'src' => common_config('license', 'image'), + 'alt' => common_config('license', 'title'))); + //TODO: This is dirty: i18n + $this->text(_('All '.common_config('site', 'name').' content and data are available under the ')); + $this->element('a', array('class' => 'license', + 'rel' => 'external license', + 'href' => common_config('license', 'url')), + common_config('license', 'title')); + $this->text(_('license.')); + $this->elementEnd('p'); + $this->elementEnd('dd'); + } + // For comparison with If-Last-Modified // If not applicable, return null - function last_modified() + function lastModified() { return null; } @@ -51,7 +474,7 @@ class Action // lawsuit return null; } - function is_readonly() + function isReadOnly() { return false; } @@ -76,7 +499,7 @@ class Action // lawsuit function handle($argarray=null) { - $lm = $this->last_modified(); + $lm = $this->lastModified(); $etag = $this->etag(); if ($etag) { @@ -90,7 +513,7 @@ class Action // lawsuit $ims = strtotime($if_modified_since); if ($lm <= $ims) { if (!$etag || - $this->_has_etag($etag, $_SERVER['HTTP_IF_NONE_MATCH'])) { + $this->_hasEtag($etag, $_SERVER['HTTP_IF_NONE_MATCH'])) { header('HTTP/1.1 304 Not Modified'); // Better way to do this? exit(0); @@ -100,7 +523,7 @@ class Action // lawsuit } } - function _has_etag($etag, $if_none_match) + function _hasEtag($etag, $if_none_match) { return ($if_none_match) && in_array($etag, explode(',', $if_none_match)); } @@ -120,21 +543,21 @@ class Action // lawsuit } } - function server_error($msg, $code=500) + function serverError($msg, $code=500) { $action = $this->trimmed('action'); common_debug("Server error '$code' on '$action': $msg", __FILE__); common_server_error($msg, $code); } - function client_error($msg, $code=400) + function clientError($msg, $code=400) { $action = $this->trimmed('action'); common_debug("User error '$code' on '$action': $msg", __FILE__); common_user_error($msg, $code); } - function self_url() + function selfUrl() { $action = $this->trimmed('action'); $args = $this->args; @@ -145,17 +568,63 @@ class Action // lawsuit return common_local_url($action, $args); } - function nav_menu($menu) + // Added @id to li for some control. + // XXX: We might want to move this to htmloutputter.php + + function menuItem($url, $text, $title=null, $is_selected=false, $id=null) { - $action = $this->trimmed('action'); - common_element_start('ul', array('id' => 'nav_views')); - foreach ($menu as $menuaction => $menudesc) { - common_menu_item(common_local_url($menuaction, - isset($menudesc[2]) ? $menudesc[2] : null), - $menudesc[0], - $menudesc[1], - $action == $menuaction); - } - common_element_end('ul'); + $lattrs = array(); + if ($is_selected) { + $lattrs['class'] = 'current'; + } + + (is_null($id)) ? $lattrs : $lattrs['id'] = $id; + + $this->elementStart('li', $lattrs); + $attrs['href'] = $url; + if ($title) { + $attrs['title'] = $title; + } + $this->element('a', $attrs, $text); + $this->elementEnd('li'); + } + + // Does a little before-after block for next/prev page + + function pagination($have_before, $have_after, $page, $action, $args=null) + { + if ($have_before || $have_after) { + $this->elementStart('div', array('class' => 'pagination')); + $this->elementStart('dl', null); + $this->element('dt', null, _('Pagination')); + $this->elementStart('dd', null); + $this->elementStart('ul', array('class' => 'nav')); + } + + if ($have_before) { + $pargs = array('page' => $page-1); + $newargs = ($args) ? array_merge($args,$pargs) : $pargs; + + $this->elementStart('li', array('class' => 'nav_prev')); + $this->element('a', array('href' => common_local_url($action, $newargs), 'rel' => 'prev'), + _('After')); + $this->elementEnd('li'); + } + + if ($have_after) { + $pargs = array('page' => $page+1); + $newargs = ($args) ? array_merge($args,$pargs) : $pargs; + $this->elementStart('li', array('class' => 'nav_next')); + $this->element('a', array('href' => common_local_url($action, $newargs), 'rel' => 'next'), + _('Before')); + $this->elementEnd('li'); + } + + if ($have_before || $have_after) { + $this->elementEnd('ul'); + $this->elementEnd('dd'); + $this->elementEnd('dl'); + $this->elementEnd('div'); + } } } |