]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - lib/action.php
Add an optional theme parameter to theme functions
[quix0rs-gnu-social.git] / lib / action.php
1 <?php
2 /**
3  * Laconica, the distributed open-source microblogging tool
4  *
5  * Base class for all actions (~views)
6  *
7  * PHP version 5
8  *
9  * LICENCE: This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU Affero General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Affero General Public License for more details.
18  *
19  * You should have received a copy of the GNU Affero General Public License
20  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
21  *
22  * @category  Action
23  * @package   Laconica
24  * @author    Evan Prodromou <evan@controlyourself.ca>
25  * @author    Sarven Capadisli <csarven@controlyourself.ca>
26  * @copyright 2008 Control Yourself, Inc.
27  * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
28  * @link      http://laconi.ca/
29  */
30
31 if (!defined('LACONICA')) {
32     exit(1);
33 }
34
35 require_once INSTALLDIR.'/lib/form.php';
36 require_once INSTALLDIR.'/lib/htmloutputter.php';
37
38 /**
39  * Base class for all actions
40  *
41  * This is the base class for all actions in the package. An action is
42  * more or less a "view" in an MVC framework.
43  *
44  * Actions are responsible for extracting and validating parameters; using
45  * model classes to read and write to the database; and doing ouput.
46  *
47  * @category Output
48  * @package  Laconica
49  * @author   Evan Prodromou <evan@controlyourself.ca>
50  * @author   Sarven Capadisli <csarven@controlyourself.ca>
51  * @license  http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
52  * @link     http://laconi.ca/
53  *
54  * @see      HTMLOutputter
55  */
56
57 class Action extends HTMLOutputter // lawsuit
58 {
59     var $args;
60
61     function Action()
62     {
63     }
64
65     // For initializing members of the class
66
67     function prepare($argarray)
68     {
69         $this->args =& common_copy_args($argarray);
70         return true;
71     }
72
73     function showPage()
74     {
75         $this->startHTML();
76         $this->showHead();
77         $this->showBody();
78         $this->endHTML();
79     }
80
81     function showHead()
82     {
83         // XXX: attributes (profile?)
84         $this->startElement('head');
85         $this->showTitle();
86         $this->showStylesheets();
87         $this->showScripts();
88         $this->showOpenSearch();
89         $this->showFeeds();
90         $this->showDescription();
91         $this->extraHead();
92         $this->elementElement('head');
93     }
94
95     function showTitle()
96     {
97         $this->element('title', null,
98                        sprintf(_("%s - %s"),
99                                $this->title(),
100                                common_config('site', 'name')));
101     }
102
103     // SHOULD overload
104
105     function title()
106     {
107         return _("Untitled page");
108     }
109
110     function showStylesheets()
111     {
112         $this->element('link', array('rel' => 'stylesheet',
113                                      'type' => 'text/css',
114                                      'href' => theme_path('display.css') . '?version=' . LACONICA_VERSION,
115                                      'media' => 'screen, projection, tv'));
116         foreach (array(6,7) as $ver) {
117             if (file_exists(theme_file('ie'.$ver.'.css'))) {
118                 // Yes, IE people should be put in jail.
119                 $this->comment('[if lte IE '.$ver.']><link rel="stylesheet" type="text/css" '.
120                                'href="'.theme_path('ie'.$ver.'.css').'?version='.LACONICA_VERSION.'" /><![endif]');
121             }
122         }
123     }
124
125     function showScripts()
126     {
127         $this->element('script', array('type' => 'text/javascript',
128                                        'src' => common_path('js/jquery.min.js')),
129                        ' ');
130         $this->element('script', array('type' => 'text/javascript',
131                                        'src' => common_path('js/jquery.form.js')),
132                        ' ');
133         $this->element('script', array('type' => 'text/javascript',
134                                        'src' => common_path('js/xbImportNode.js')),
135                        ' ');
136         $this->element('script', array('type' => 'text/javascript',
137                                        'src' => common_path('js/util.js?version='.LACONICA_VERSION)),
138                        ' ');
139     }
140
141     function showOpenSearch()
142     {
143         $this->element('link', array('rel' => 'search', 'type' => 'application/opensearchdescription+xml',
144                                      'href' =>  common_local_url('opensearch', array('type' => 'people')),
145                                      'title' => common_config('site', 'name').' People Search'));
146
147         $this->element('link', array('rel' => 'search', 'type' => 'application/opensearchdescription+xml',
148                                      'href' =>  common_local_url('opensearch', array('type' => 'notice')),
149                                      'title' => common_config('site', 'name').' Notice Search'));
150     }
151
152     // MAY overload
153
154     function showFeeds()
155     {
156         // does nothing by default
157     }
158
159     // SHOULD overload
160
161     function showDescription()
162     {
163         // does nothing by default
164     }
165
166     // MAY overload
167
168     function extraHead()
169     {
170         // does nothing by default
171     }
172
173     function showBody()
174     {
175         $this->elementStart('body');
176         $this->elementStart('wrap');
177         $this->showHeader();
178         $this->showCore();
179         $this->showFooter();
180         $this->elementEnd('wrap');
181         $this->elementEnd('body');
182     }
183
184     function showHeader()
185     {
186         $this->elementStart('div', array('id' => 'header'));
187         $this->showLogo();
188         $this->showPrimaryNav();
189         $this->showSiteNotice();
190         $this->showNoticeForm();
191         $this->elementEnd('div');
192     }
193
194     function showLogo()
195     {
196         $this->elementStart('address', array('id' => 'site_contact',
197                                               'class' => 'vcard'));
198         $this->elementStart('a', array('class' => 'url home bookmark',
199                                         'href' => common_local_url('public')));
200         if ((isset($config['site']['logo']) && is_string($config['site']['logo']) && (strlen($config['site']['logo']) > 0))
201             || file_exists(theme_file('logo.png')))
202         {
203             $this->element('img', array('class' => 'logo photo',
204                                         'src' => isset($config['site']['logo']) ?
205                                         ($config['site']['logo']) : theme_path('logo.png'),
206                                         'alt' => $config['site']['name']));
207         }
208         $this->element('span', array('class' => 'fn org'), $config['site']['name']);
209         $this->elementEnd('a');
210         $this->elementEnd('address');
211     }
212
213     function showPrimaryNav()
214     {
215         $this->elementStart('dl', array('id' => 'site_nav_global_primary'));
216         $this->element('dt', null, _('Primary site navigation'));
217         $user = common_current_user();
218         $this->elementStart('ul', array('id' => 'nav'));
219         if ($user) {
220             common_menu_item(common_local_url('all', array('nickname' => $user->nickname)),
221                              _('Home'));
222         }
223         common_menu_item(common_local_url('peoplesearch'), _('Search'));
224         if ($user) {
225             common_menu_item(common_local_url('profilesettings'),
226                              _('Settings'));
227             common_menu_item(common_local_url('invite'),
228                              _('Invite'));
229             common_menu_item(common_local_url('logout'),
230                              _('Logout'));
231         } else {
232             common_menu_item(common_local_url('login'), _('Login'));
233             if (!common_config('site', 'closed')) {
234                 common_menu_item(common_local_url('register'), _('Register'));
235             }
236             common_menu_item(common_local_url('openidlogin'), _('OpenID'));
237         }
238         common_menu_item(common_local_url('doc', array('title' => 'help')),
239                          _('Help'));
240         $this->elementEnd('ul');
241         $this->elementEnd('dl');
242     }
243
244     // Revist. Should probably do an hAtom pattern here
245     function showSiteNotice()
246     {
247         $this->elementStart('dl', array('id' => 'site_notice',
248                                         'class' => 'system_notice'));
249         $this->element('dt', null, _('Site notice'));
250         $this->elementStart('dd', null);
251         // Output a bunch of paragraphs here
252         $this->elementEnd('dd');
253     }
254
255     // MAY overload if no notice form needed... or direct message box????
256
257     function showNoticeForm()
258     {
259         $notice_form = new NoticeForm();
260         $notice_form->show();
261     }
262
263     function showCore()
264     {
265         $this->elementStart('div', array('class' => 'core'));
266         $this->showLocalNav();
267         $this->showContentBlock();
268         $this->showAside();
269         $this->elementEnd('div');
270     }
271
272     // SHOULD overload (perhaps this should be a MUST because sometimes it is not used)
273
274     function showLocalNav($menu)
275     {
276         $action = $this->trimmed('action');
277
278         $this->elementStart('dl', array('id' => 'site_nav_local_views'));
279         $this->element('dt', null, _('Local views'));
280         $this->elementStart('ul', array('id' => 'nav'));
281         foreach ($menu as $menuaction => $menudesc) {
282             common_menu_item(common_local_url($menuaction,
283                                               isset($menudesc[2]) ? $menudesc[2] : null),
284                              $menudesc[0],
285                              $menudesc[1],
286                              $action == $menuaction);
287         }
288         $this->elementEnd('ul');
289         $this->elementEnd('dd');
290         $this->elementEnd('dl');
291     }
292
293     function showContentBlock()
294     {
295         $this->elementStart('div', array('id' => 'content'));
296         $this->showPageTitle();
297         $this->showPageNotice();
298         $this->showContent();
299         $this->elementEnd('div');
300     }
301
302     function showPageTitle() {
303         $this->element('h1', NULL, $this->title());
304     }
305
306     // SHOULD overload (unless there's not a notice)
307
308     function showPageNotice()
309     {
310         $this->elementStart('dl', array('id' => 'page_notice',
311                                         'class' => 'system_notice'));
312         $this->element('dt', null, _('Page notice'));
313         $this->elementStart('dd', null);
314         // Output a bunch of paragraphs here
315         $this->elementEnd('dd');
316     }
317
318     // MUST overload
319
320     function showContent()
321     {
322         // show the actual content (forms, lists, whatever)
323         $this->elementStart('div', array('id' => 'content_inner'));
324         $this->elementEnd('div');
325     }
326
327     function showAside()
328     {
329         $this->showExportData();
330         $this->showSections();
331     }
332
333     // MAY overload if there are feeds
334
335     function showExportData()
336     {
337         // is there structure to this?
338         // list of (visible!) feed links
339         // can we reuse list of feeds from showFeeds() ?
340     }
341
342     // SHOULD overload
343
344     function showSections() {
345         // for each section, show it
346     }
347
348     function showFooter()
349     {
350         $this->elementStart('div', array('id' => 'footer'));
351         $this->showSecondaryNav();
352         $this->showLicenses();
353         $this->elementEnd('div');
354     }
355
356     function showSecondaryNav()
357     {
358         $this->elementStart('ul', array('id' => 'nav_sub'));
359         common_menu_item(common_local_url('doc', array('title' => 'help')),
360                          _('Help'));
361         common_menu_item(common_local_url('doc', array('title' => 'about')),
362                          _('About'));
363         common_menu_item(common_local_url('doc', array('title' => 'faq')),
364                          _('FAQ'));
365         common_menu_item(common_local_url('doc', array('title' => 'privacy')),
366                          _('Privacy'));
367         common_menu_item(common_local_url('doc', array('title' => 'source')),
368                          _('Source'));
369         common_menu_item(common_local_url('doc', array('title' => 'contact')),
370                          _('Contact'));
371         $this->elementEnd('ul');
372     }
373
374     function showLicenses()
375     {
376         $this->elementStart('dl', array('id' => 'licenses'));
377         $this->showLaconicaLicense();
378         $this->showContentLicense();
379         $this->elementEnd('dl');
380     }
381
382     function showLaconicaLicense()
383     {
384         $this->element('dt', array('id' => 'site_laconica_license'), _('Laconica software license'));
385         $this->elementStart('dd', null);
386         if (common_config('site', 'broughtby')) {
387             $instr = _('**%%site.name%%** is a microblogging service brought to you by [%%site.broughtby%%](%%site.broughtbyurl%%). ');
388         } else {
389             $instr = _('**%%site.name%%** is a microblogging service. ');
390         }
391         $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);
392         $output = common_markup_to_html($instr);
393         common_raw($output);
394         $this->elementEnd('dd');
395         // do it
396     }
397
398     function showContentLicense()
399     {
400         $this->element('dt', array('id' => 'site_content_license'), _('Laconica software license'));
401         $this->elementStart('dd', array('id' => 'site_content_license_cc'));
402         $this->elementStart('p');
403         common_text(_('Unless otherwise specified, contents of this site are copyright by the contributors and available under the '));
404         $this->element('a', array('class' => 'license',
405                                   'rel' => 'external license',
406                                   'href' => $config['license']['url']),
407                        $config['license']['title']);
408         common_text(_('. Contributors should be attributed by full name or nickname.'));
409         $this->elementEnd('p');
410         $this->element('img', array('id' => 'license_cc',
411                                     'src' => $config['license']['image'],
412                                     'alt' => $config['license']['title']));
413         $this->elementEnd('dd');
414     }
415
416     // For comparison with If-Last-Modified
417     // If not applicable, return null
418
419     function last_modified()
420     {
421         return null;
422     }
423
424     function etag()
425     {
426         return null;
427     }
428
429     function is_readonly()
430     {
431         return false;
432     }
433
434     function arg($key, $def=null)
435     {
436         if (array_key_exists($key, $this->args)) {
437             return $this->args[$key];
438         } else {
439             return $def;
440         }
441     }
442
443     function trimmed($key, $def=null)
444     {
445         $arg = $this->arg($key, $def);
446         return (is_string($arg)) ? trim($arg) : $arg;
447     }
448
449     // Note: argarray ignored, since it's now passed in in prepare()
450
451     function handle($argarray=null)
452     {
453
454         $lm = $this->last_modified();
455         $etag = $this->etag();
456
457         if ($etag) {
458             header('ETag: ' . $etag);
459         }
460
461         if ($lm) {
462             header('Last-Modified: ' . date(DATE_RFC1123, $lm));
463             $if_modified_since = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
464             if ($if_modified_since) {
465                 $ims = strtotime($if_modified_since);
466                 if ($lm <= $ims) {
467                     if (!$etag ||
468                         $this->_has_etag($etag, $_SERVER['HTTP_IF_NONE_MATCH'])) {
469                         header('HTTP/1.1 304 Not Modified');
470                         // Better way to do this?
471                         exit(0);
472                     }
473                 }
474             }
475         }
476     }
477
478     function _has_etag($etag, $if_none_match)
479     {
480         return ($if_none_match) && in_array($etag, explode(',', $if_none_match));
481     }
482
483     function boolean($key, $def=false)
484     {
485         $arg = strtolower($this->trimmed($key));
486
487         if (is_null($arg)) {
488             return $def;
489         } else if (in_array($arg, array('true', 'yes', '1'))) {
490             return true;
491         } else if (in_array($arg, array('false', 'no', '0'))) {
492             return false;
493         } else {
494             return $def;
495         }
496     }
497
498     function server_error($msg, $code=500)
499     {
500         $action = $this->trimmed('action');
501         common_debug("Server error '$code' on '$action': $msg", __FILE__);
502         common_server_error($msg, $code);
503     }
504
505     function client_error($msg, $code=400)
506     {
507         $action = $this->trimmed('action');
508         common_debug("User error '$code' on '$action': $msg", __FILE__);
509         common_user_error($msg, $code);
510     }
511
512     function self_url()
513     {
514         $action = $this->trimmed('action');
515         $args = $this->args;
516         unset($args['action']);
517         foreach (array_keys($_COOKIE) as $cookie) {
518             unset($args[$cookie]);
519         }
520         return common_local_url($action, $args);
521     }
522
523     function nav_menu($menu)
524     {
525         $action = $this->trimmed('action');
526         $this->elementStart('ul', array('id' => 'nav_views'));
527         foreach ($menu as $menuaction => $menudesc) {
528             common_menu_item(common_local_url($menuaction,
529                                               isset($menudesc[2]) ? $menudesc[2] : null),
530                              $menudesc[0],
531                              $menudesc[1],
532                              $action == $menuaction);
533         }
534         $this->elementEnd('ul');
535     }
536
537     function common_show_header($pagetitle, $callable=null, $data=null, $headercall=null)
538     {
539         global $config, $xw;
540         global $action; /* XXX: kind of cheating here. */
541
542         common_start_html();
543
544         $this->elementStart('head');
545
546         if ($callable) {
547             if ($data) {
548                 call_user_func($callable, $data);
549             } else {
550                 call_user_func($callable);
551             }
552         }
553         $this->elementEnd('head');
554         $this->elementStart('body', $action);
555         $this->elementStart('div', array('id' => 'wrap'));
556         $this->elementStart('div', array('id' => 'content'));
557     }
558
559     // Added @id to li for some control. We might want to move this to htmloutputter.php
560     function common_menu_item($id=null, $url, $text, $title=null, $is_selected=false)
561     {
562         $lattrs = array();
563         if ($is_selected) {
564             $lattrs['class'] = 'current';
565         }
566
567         $this->elementStart('li', (is_null($id)) ? null : array('id' => $id),  $lattrs);
568         $attrs['href'] = $url;
569         if ($title) {
570             $attrs['title'] = $title;
571         }
572         $this->element('a', $attrs, $text);
573         $this->elementEnd('li');
574     }
575
576     // Does a little before-after block for next/prev page
577
578     function pagination($have_before, $have_after, $page, $action, $args=null)
579     {
580         if ($have_before || $have_after) {
581             $this->elementStart('div', array('class' => 'pagination'));
582             $this->elementStart('dl', null);
583             $this->element('dt', null, _('Pagination'));
584             $this->elementStart('dd', null);
585             $this->elementStart('ul', array('class' => 'nav'));
586         }
587
588         if ($have_before) {
589             $pargs = array('page' => $page-1);
590             $newargs = ($args) ? array_merge($args,$pargs) : $pargs;
591
592             $this->elementStart('li', array('class' => 'nav_prev'));
593             $this->element('a', array('href' => common_local_url($action, $newargs), 'rel' => 'prev'),
594                            _('After'));
595             $this->elementEnd('li');
596         }
597
598         if ($have_after) {
599             $pargs = array('page' => $page+1);
600             $newargs = ($args) ? array_merge($args,$pargs) : $pargs;
601             $this->elementStart('li', array('class' => 'nav_next'));
602             $this->element('a', array('href' => common_local_url($action, $newargs), 'rel' => 'next'),
603                            _('Before'));
604             $this->elementEnd('li');
605         }
606
607         if ($have_before || $have_after) {
608             $this->elementEnd('ul');
609             $this->elementEnd('dd');
610             $this->elementEnd('dl');
611             $this->elementEnd('div');
612         }
613     }
614 }