diff options
Diffstat (limited to 'actions')
-rw-r--r-- | actions/attachment.php | 205 | ||||
-rw-r--r-- | actions/attachment_ajax.php | 119 | ||||
-rw-r--r-- | actions/attachment_thumbnail.php | 118 | ||||
-rw-r--r-- | actions/conversation.php | 298 | ||||
-rw-r--r-- | actions/deletenotice.php | 4 | ||||
-rw-r--r-- | actions/designsettings.php | 264 | ||||
-rw-r--r-- | actions/facebookhome.php | 2 | ||||
-rw-r--r-- | actions/facebooksettings.php | 2 | ||||
-rw-r--r-- | actions/file.php | 40 | ||||
-rw-r--r-- | actions/logout.php | 16 | ||||
-rw-r--r-- | actions/newnotice.php | 126 | ||||
-rw-r--r-- | actions/register.php | 13 | ||||
-rw-r--r-- | actions/shownotice.php | 4 | ||||
-rw-r--r-- | actions/showstream.php | 16 | ||||
-rw-r--r-- | actions/twittersettings.php | 28 | ||||
-rw-r--r-- | actions/userrss.php | 24 |
16 files changed, 1255 insertions, 24 deletions
diff --git a/actions/attachment.php b/actions/attachment.php new file mode 100644 index 000000000..16ee723d9 --- /dev/null +++ b/actions/attachment.php @@ -0,0 +1,205 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * Show notice attachments + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Personal + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @copyright 2008-2009 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); +} + +require_once INSTALLDIR.'/lib/attachmentlist.php'; + +/** + * Show notice attachments + * + * @category Personal + * @package Laconica + * @author Evan Prodromou <evan@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/ + */ + +class AttachmentAction extends Action +{ + /** + * Attachment object to show + */ + + var $attachment = null; + + /** + * Load attributes based on database arguments + * + * Loads all the DB stuff + * + * @param array $args $_REQUEST array + * + * @return success flag + */ + + function prepare($args) + { + parent::prepare($args); + + if ($id = $this->trimmed('attachment')) { + $this->attachment = File::staticGet($id); + } + + if (empty($this->attachment)) { + $this->clientError(_('No such attachment.'), 404); + return false; + } + return true; + } + + /** + * Is this action read-only? + * + * @return boolean true + */ + + function isReadOnly($args) + { + return true; + } + + /** + * Title of the page + * + * @return string title of the page + */ + function title() + { + $a = new Attachment($this->attachment); + return $a->title(); + } + + /** + * Last-modified date for page + * + * When was the content of this page last modified? Based on notice, + * profile, avatar. + * + * @return int last-modified date as unix timestamp + */ +/* + function lastModified() + { + return max(strtotime($this->notice->created), + strtotime($this->profile->modified), + ($this->avatar) ? strtotime($this->avatar->modified) : 0); + } +*/ + + /** + * An entity tag for this page + * + * Shows the ETag for the page, based on the notice ID and timestamps + * for the notice, profile, and avatar. It's weak, since we change + * the date text "one hour ago", etc. + * + * @return string etag + */ +/* + function etag() + { + $avtime = ($this->avatar) ? + strtotime($this->avatar->modified) : 0; + + return 'W/"' . implode(':', array($this->arg('action'), + common_language(), + $this->notice->id, + strtotime($this->notice->created), + strtotime($this->profile->modified), + $avtime)) . '"'; + } +*/ + + + /** + * Handle input + * + * Only handles get, so just show the page. + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + $this->showPage(); + } + + /** + * Don't show local navigation + * + * @return void + */ + + function showLocalNavBlock() + { + } + + /** + * Fill the content area of the page + * + * Shows a single notice list item. + * + * @return void + */ + + function showContent() + { + $ali = new Attachment($this->attachment, $this); + $cnt = $ali->show(); + } + + /** + * Don't show page notice + * + * @return void + */ + + function showPageNoticeBlock() + { + } + + /** + * Show aside: this attachments appears in what notices + * + * @return void + */ + function showSections() { + $ns = new AttachmentNoticeSection($this); + $ns->show(); + $atcs = new AttachmentTagCloudSection($this); + $atcs->show(); + } +} + diff --git a/actions/attachment_ajax.php b/actions/attachment_ajax.php new file mode 100644 index 000000000..3d83393c5 --- /dev/null +++ b/actions/attachment_ajax.php @@ -0,0 +1,119 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * Show notice attachments + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Personal + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @copyright 2008-2009 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); +} + +require_once INSTALLDIR.'/actions/attachment.php'; + +/** + * Show notice attachments + * + * @category Personal + * @package Laconica + * @author Evan Prodromou <evan@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/ + */ + +class Attachment_ajaxAction extends AttachmentAction +{ + /** + * Show page, a template method. + * + * @return nothing + */ + function showPage() + { + if (Event::handle('StartShowBody', array($this))) { + $this->showCore(); + Event::handle('EndShowBody', array($this)); + } + } + + /** + * Show core. + * + * Shows local navigation, content block and aside. + * + * @return nothing + */ + function showCore() + { + $this->elementStart('div', array('id' => 'core')); + if (Event::handle('StartShowContentBlock', array($this))) { + $this->showContentBlock(); + Event::handle('EndShowContentBlock', array($this)); + } + $this->elementEnd('div'); + } + + /** + * Last-modified date for page + * + * When was the content of this page last modified? Based on notice, + * profile, avatar. + * + * @return int last-modified date as unix timestamp + */ +/* + function lastModified() + { + return max(strtotime($this->notice->created), + strtotime($this->profile->modified), + ($this->avatar) ? strtotime($this->avatar->modified) : 0); + } +*/ + + /** + * An entity tag for this page + * + * Shows the ETag for the page, based on the notice ID and timestamps + * for the notice, profile, and avatar. It's weak, since we change + * the date text "one hour ago", etc. + * + * @return string etag + */ +/* + function etag() + { + $avtime = ($this->avatar) ? + strtotime($this->avatar->modified) : 0; + + return 'W/"' . implode(':', array($this->arg('action'), + common_language(), + $this->notice->id, + strtotime($this->notice->created), + strtotime($this->profile->modified), + $avtime)) . '"'; + } +*/ +} + diff --git a/actions/attachment_thumbnail.php b/actions/attachment_thumbnail.php new file mode 100644 index 000000000..b4070e747 --- /dev/null +++ b/actions/attachment_thumbnail.php @@ -0,0 +1,118 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * Show notice attachments + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Personal + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @copyright 2008-2009 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); +} + +require_once INSTALLDIR.'/actions/attachment.php'; + +/** + * Show notice attachments + * + * @category Personal + * @package Laconica + * @author Evan Prodromou <evan@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/ + */ + +class Attachment_thumbnailAction extends AttachmentAction +{ + /** + * Show page, a template method. + * + * @return nothing + */ + function showPage() + { + if (Event::handle('StartShowBody', array($this))) { + $this->showCore(); + Event::handle('EndShowBody', array($this)); + } + } + + /** + * Show core. + * + * Shows local navigation, content block and aside. + * + * @return nothing + */ + function showCore() + { + $file_thumbnail = File_thumbnail::staticGet('file_id', $this->attachment->id); + if (empty($file_thumbnail->url)) { + return; + } + $this->element('img', array('src' => $file_thumbnail->url, 'alt' => 'Thumbnail')); + } + + /** + * Last-modified date for page + * + * When was the content of this page last modified? Based on notice, + * profile, avatar. + * + * @return int last-modified date as unix timestamp + */ +/* + function lastModified() + { + return max(strtotime($this->notice->created), + strtotime($this->profile->modified), + ($this->avatar) ? strtotime($this->avatar->modified) : 0); + } +*/ + + /** + * An entity tag for this page + * + * Shows the ETag for the page, based on the notice ID and timestamps + * for the notice, profile, and avatar. It's weak, since we change + * the date text "one hour ago", etc. + * + * @return string etag + */ +/* + function etag() + { + $avtime = ($this->avatar) ? + strtotime($this->avatar->modified) : 0; + + return 'W/"' . implode(':', array($this->arg('action'), + common_language(), + $this->notice->id, + strtotime($this->notice->created), + strtotime($this->profile->modified), + $avtime)) . '"'; + } +*/ +} + diff --git a/actions/conversation.php b/actions/conversation.php new file mode 100644 index 000000000..0d7cb9a87 --- /dev/null +++ b/actions/conversation.php @@ -0,0 +1,298 @@ +<?php +/** + * Display a conversation in the browser + * + * PHP version 5 + * + * @category Action + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://laconi.ca/ + * + * Laconica - a distributed open-source microblogging tool + * Copyright (C) 2009, Control Yourself, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +if (!defined('LACONICA')) { + exit(1); +} + +require_once INSTALLDIR.'/lib/noticelist.php'; + +/** + * Conversation tree in the browser + * + * @category Action + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://laconi.ca/ + */ + +class ConversationAction extends Action +{ + var $id = null; + var $page = null; + + /** + * Initialization. + * + * @param array $args Web and URL arguments + * + * @return boolean false if id not passed in + */ + + function prepare($args) + { + parent::prepare($args); + $this->id = $this->trimmed('id'); + if (empty($this->id)) { + return false; + } + $this->page = $this->trimmed('page'); + if (empty($this->page)) { + $this->page = 1; + } + return true; + } + + /** + * Handle the action + * + * @param array $args Web and URL arguments + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + $this->showPage(); + } + + /** + * Returns the page title + * + * @return string page title + */ + + function title() + { + return _("Conversation"); + } + + /** + * Show content. + * + * Display a hierarchical unordered list in the content area. + * Uses ConversationTree to do most of the heavy lifting. + * + * @return void + */ + + function showContent() + { + // FIXME this needs to be a tree, not a list + + $qry = 'SELECT * FROM notice WHERE conversation = %s '; + + $offset = ($this->page-1) * NOTICES_PER_PAGE; + $limit = NOTICES_PER_PAGE + 1; + + $txt = sprintf($qry, $this->id); + + $notices = Notice::getStream($txt, + 'notice:conversation:'.$this->id, + $offset, $limit); + + $ct = new ConversationTree($notices, $this); + + $cnt = $ct->show(); + + $this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, + $this->page, 'conversation', array('id' => $this->id)); + } + +} + +/** + * Conversation tree + * + * The widget class for displaying a hierarchical list of notices. + * + * @category Widget + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://laconi.ca/ + */ + +class ConversationTree extends NoticeList +{ + var $tree = null; + var $table = null; + + /** + * Show the tree of notices + * + * @return void + */ + + function show() + { + $cnt = 0; + + $this->tree = array(); + $this->table = array(); + + while ($this->notice->fetch()) { + + $cnt++; + + $id = $this->notice->id; + $notice = clone($this->notice); + + $this->table[$id] = $notice; + + if (is_null($notice->reply_to)) { + $this->tree['root'] = array($notice->id); + } else if (array_key_exists($notice->reply_to, $this->tree)) { + $this->tree[$notice->reply_to][] = $notice->id; + } else { + $this->tree[$notice->reply_to] = array($notice->id); + } + } + + $this->out->elementStart('div', array('id' =>'notices_primary')); + $this->out->element('h2', null, _('Notices')); + $this->out->elementStart('ol', array('class' => 'notices xoxo')); + + if (array_key_exists('root', $this->tree)) { + $rootid = $this->tree['root'][0]; + $this->showNoticePlus($rootid); + } + + $this->out->elementEnd('ol'); + $this->out->elementEnd('div'); + + return $cnt; + } + + /** + * Shows a notice plus its list of children. + * + * @param integer $id ID of the notice to show + * + * @return void + */ + + function showNoticePlus($id) + { + $notice = $this->table[$id]; + + // We take responsibility for doing the li + + $this->out->elementStart('li', array('class' => 'hentry notice', + 'id' => 'notice-' . $this->notice->id)); + + $item = $this->newListItem($notice); + $item->show(); + + if (array_key_exists($id, $this->tree)) { + $children = $this->tree[$id]; + + $this->out->elementStart('ol', array('class' => 'notices')); + + foreach ($children as $child) { + $this->showNoticePlus($child); + } + + $this->out->elementEnd('ol'); + } + + $this->out->elementEnd('li'); + } + + /** + * Override parent class to return our preferred item. + * + * @param Notice $notice Notice to display + * + * @return NoticeListItem a list item to show + */ + + function newListItem($notice) + { + return new ConversationTreeItem($notice, $this->out); + } +} + +/** + * Conversation tree list item + * + * Special class of NoticeListItem for use inside conversation trees. + * + * @category Widget + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://laconi.ca/ + */ + +class ConversationTreeItem extends NoticeListItem +{ + /** + * start a single notice. + * + * The default creates the <li>; we skip, since the ConversationTree + * takes care of that. + * + * @return void + */ + + function showStart() + { + return; + } + + /** + * finish the notice + * + * The default closes the <li>; we skip, since the ConversationTree + * takes care of that. + * + * @return void + */ + + function showEnd() + { + return; + } + + /** + * show link to notice conversation page + * + * Since we're only used on the conversation page, we skip this + * + * @return void + */ + + function showContext() + { + return; + } +} diff --git a/actions/deletenotice.php b/actions/deletenotice.php index 6c350b33a..e733f9650 100644 --- a/actions/deletenotice.php +++ b/actions/deletenotice.php @@ -112,8 +112,8 @@ class DeletenoticeAction extends DeleteAction $this->hidden('token', common_session_token()); $this->hidden('notice', $this->trimmed('notice')); $this->element('p', null, _('Are you sure you want to delete this notice?')); - $this->submit('form_action-yes', _('Yes'), 'submit form_action-primary', 'yes'); - $this->submit('form_action-no', _('No'), 'submit form_action-secondary', 'no'); + $this->submit('form_action-no', _('No'), 'submit form_action-primary', 'no', _("Do not delete this notice")); + $this->submit('form_action-yes', _('Yes'), 'submit form_action-secondary', 'yes', _('Delete this notice')); $this->elementEnd('fieldset'); $this->elementEnd('form'); } diff --git a/actions/designsettings.php b/actions/designsettings.php new file mode 100644 index 000000000..5774b8537 --- /dev/null +++ b/actions/designsettings.php @@ -0,0 +1,264 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * Change user password + * + * PHP version 5 + * + * LICENCE: This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category Settings + * @package Laconica + * @author Sarven Capadisli <csarven@controlyourself.ca> + * @copyright 2008-2009 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); +} + +require_once INSTALLDIR.'/lib/accountsettingsaction.php'; + + + +class DesignsettingsAction extends AccountSettingsAction +{ + /** + * Title of the page + * + * @return string Title of the page + */ + + function title() + { + return _('Profile design'); + } + + /** + * Instructions for use + * + * @return instructions for use + */ + + function getInstructions() + { + return _('Customize the way your profile looks with a background image and a colour palette of your choice.'); + } + + /** + * Content area of the page + * + * Shows a form for changing the password + * + * @return void + */ + + function showContent() + { + $user = common_current_user(); + $this->elementStart('form', array('method' => 'post', + 'id' => 'form_settings_design', + 'class' => 'form_settings', + 'action' => + common_local_url('designsettings'))); + $this->elementStart('fieldset'); + $this->hidden('token', common_session_token()); + + $this->elementStart('fieldset', array('id' => 'settings_design_background-image')); + $this->element('legend', null, _('Change background image')); + $this->elementStart('ul', 'form_data'); + $this->elementStart('li'); + $this->element('label', array('for' => 'design_background-image_file'), + _('Upload file')); + $this->element('input', array('name' => 'design_background-image_file', + 'type' => 'file', + 'id' => 'design_background-image_file')); + $this->element('p', 'form_guide', _('You can upload your personal background image. The maximum file size is 2Mb.')); + $this->element('input', array('name' => 'MAX_FILE_SIZE', + 'type' => 'hidden', + 'id' => 'MAX_FILE_SIZE', + 'value' => ImageFile::maxFileSizeInt())); + $this->elementEnd('li'); + $this->elementEnd('ul'); + $this->elementEnd('fieldset'); + + $this->elementStart('fieldset', array('id' => 'settings_design_color')); + $this->element('legend', null, _('Change colours')); + $this->elementStart('ul', 'form_data'); + + //This is a JSON object in the DB field. Here for testing. Remove later. + $userSwatch = '{"body":{"background-color":"#F0F2F5"}, + "#content":{"background-color":"#FFFFFF"}, + "#aside_primary":{"background-color":"#CEE1E9"}, + "html body":{"color":"#000000"}, + "a":{"color":"#002E6E"}}'; + + //Default theme swatch -- Where should this be stored? + $defaultSwatch = array('body' => array('background-color' => '#F0F2F5'), + '#content' => array('background-color' => '#FFFFFF'), + '#aside_primary' => array('background-color' => '#CEE1E9'), + 'html body' => array('color' => '#000000'), + 'a' => array('color' => '#002E6E')); + + $userSwatch = ($userSwatch) ? json_decode($userSwatch, true) : $defaultSwatch; + + $s = 0; + $labelSwatch = array('Background', + 'Content', + 'Sidebar', + 'Text', + 'Links'); + foreach($userSwatch as $propertyvalue => $value) { + $foo = array_values($value); + $this->elementStart('li'); + $this->element('label', array('for' => 'swatch-'.$s), _($labelSwatch[$s])); + $this->element('input', array('name' => 'swatch-'.$s, //prefer swatch[$s] ? + 'type' => 'text', + 'id' => 'swatch-'.$s, + 'class' => 'swatch', + 'maxlength' => '7', + 'size' => '7', + 'value' => $foo[0])); + $this->elementEnd('li'); + $s++; + } + + $this->elementEnd('ul'); + $this->elementEnd('fieldset'); + + $this->element('input', array('id' => 'settings_design_reset', + 'type' => 'reset', + 'value' => 'Reset', + 'class' => 'submit form_action-primary', + 'title' => _('Reset back to default'))); + $this->submit('save', _('Save'), 'submit form_action-secondary', 'save', _('Save design')); + +/*TODO: Check submitted form values: +json_encode(form values) +if submitted Swatch == DefaultSwatch, don't store in DB. +else store in BD +*/ + $this->elementEnd('fieldset'); + $this->elementEnd('form'); + + } + + /** + * Handle a post + * + * Validate input and save changes. Reload the form with a success + * or error message. + * + * @return void + */ + + function handlePost() + { + /* + // CSRF protection + + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->showForm(_('There was a problem with your session token. '. + 'Try again, please.')); + return; + } + + $user = common_current_user(); + assert(!is_null($user)); // should already be checked + + // FIXME: scrub input + + $newpassword = $this->arg('newpassword'); + $confirm = $this->arg('confirm'); + + # Some validation + + if (strlen($newpassword) < 6) { + $this->showForm(_('Password must be 6 or more characters.')); + return; + } else if (0 != strcmp($newpassword, $confirm)) { + $this->showForm(_('Passwords don\'t match.')); + return; + } + + if ($user->password) { + $oldpassword = $this->arg('oldpassword'); + + if (!common_check_user($user->nickname, $oldpassword)) { + $this->showForm(_('Incorrect old password')); + return; + } + } + + $original = clone($user); + + $user->password = common_munge_password($newpassword, $user->id); + + $val = $user->validate(); + if ($val !== true) { + $this->showForm(_('Error saving user; invalid.')); + return; + } + + if (!$user->update($original)) { + $this->serverError(_('Can\'t save new password.')); + return; + } + + $this->showForm(_('Password saved.'), true); + */ + } + + + /** + * Add the Farbtastic stylesheet + * + * @return void + */ + + function showStylesheets() + { + parent::showStylesheets(); + $farbtasticStyle = + common_path('theme/base/css/farbtastic.css?version='.LACONICA_VERSION); + + $this->element('link', array('rel' => 'stylesheet', + 'type' => 'text/css', + 'href' => $farbtasticStyle, + 'media' => 'screen, projection, tv')); + } + + /** + * Add the Farbtastic scripts + * + * @return void + */ + + function showScripts() + { + parent::showScripts(); + + $farbtasticPack = common_path('js/farbtastic/farbtastic.js'); + $farbtasticGo = common_path('js/farbtastic/farbtastic.go.js'); + + $this->element('script', array('type' => 'text/javascript', + 'src' => $farbtasticPack)); + $this->element('script', array('type' => 'text/javascript', + 'src' => $farbtasticGo)); + } +} diff --git a/actions/facebookhome.php b/actions/facebookhome.php index 4c2b26355..00b35ef68 100644 --- a/actions/facebookhome.php +++ b/actions/facebookhome.php @@ -115,7 +115,7 @@ class FacebookhomeAction extends FacebookAction $flink->foreign_id = $this->fbuid; $flink->service = FACEBOOK_SERVICE; $flink->created = common_sql_now(); - $flink->set_flags(true, false, false); + $flink->set_flags(true, false, false, false); $flink_id = $flink->insert(); diff --git a/actions/facebooksettings.php b/actions/facebooksettings.php index 236460c1c..227e12316 100644 --- a/actions/facebooksettings.php +++ b/actions/facebooksettings.php @@ -55,7 +55,7 @@ class FacebooksettingsAction extends FacebookAction $prefix = $this->trimmed('prefix'); $original = clone($this->flink); - $this->flink->set_flags($noticesync, $replysync, false); + $this->flink->set_flags($noticesync, $replysync, false, false); $result = $this->flink->update($original); $this->facebook->api_client->data_setUserPreference(FACEBOOK_NOTICE_PREFIX, diff --git a/actions/file.php b/actions/file.php new file mode 100644 index 000000000..1179dbe9a --- /dev/null +++ b/actions/file.php @@ -0,0 +1,40 @@ +<?php +/* + * Laconica - a distributed open-source microblogging tool + * Copyright (C) 2008, Controlez-Vous, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +if (!defined('LACONICA')) { exit(1); } + +require_once(INSTALLDIR.'/actions/shownotice.php'); + +class FileAction extends ShowNoticeAction +{ + function showPage() { + $source_url = common_local_url('file', array('notice' => $this->notice->id)); + $query = "select file_redirection.url as url from file join file_redirection on file.id = file_redirection.file_id where file.url = '$source_url'"; + $file = new File_redirection; + $file->query($query); + $file->fetch(); + if (empty($file->url)) { + die('nothing attached here'); + } else { + header("Location: {$file->url}"); + die(); + } + } +} + diff --git a/actions/logout.php b/actions/logout.php index 9f3bfe247..c34b10987 100644 --- a/actions/logout.php +++ b/actions/logout.php @@ -70,10 +70,20 @@ class LogoutAction extends Action if (!common_logged_in()) { $this->clientError(_('Not logged in.')); } else { - common_set_user(null); - common_real_login(false); // not logged in - common_forgetme(); // don't log back in! + if (Event::handle('StartLogout', array($this))) { + $this->logout(); + } + Event::handle('EndLogout', array($this)); + common_redirect(common_local_url('public'), 303); } } + + function logout() + { + common_set_user(null); + common_real_login(false); // not logged in + common_forgetme(); // don't log back in! + } + } diff --git a/actions/newnotice.php b/actions/newnotice.php index cbd04c58b..02976a2ae 100644 --- a/actions/newnotice.php +++ b/actions/newnotice.php @@ -84,20 +84,24 @@ class NewnoticeAction extends Action function handle($args) { - parent::handle($args); - if (!common_logged_in()) { $this->clientError(_('Not logged in.')); } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { + // check for this before token since all POST and FILES data + // is losts when size is exceeded + if (empty($_POST) && $_SERVER['CONTENT_LENGTH']) { + $this->clientError(sprintf(_('The server was unable to handle ' . + 'that much POST data (%s bytes) due to its current configuration.'), + $_SERVER['CONTENT_LENGTH'])); + } + parent::handle($args); // CSRF protection $token = $this->trimmed('token'); if (!$token || $token != common_session_token()) { $this->clientError(_('There was a problem with your session token. '. 'Try again, please.')); - return; } - try { $this->saveNewNotice(); } catch (Exception $e) { @@ -109,6 +113,30 @@ class NewnoticeAction extends Action } } + function getUploadedFileType() { + require_once 'MIME/Type.php'; + + $filetype = MIME_Type::autoDetect($_FILES['attach']['tmp_name']); + if (in_array($filetype, common_config('attachments', 'supported'))) { + return $filetype; + } + $media = MIME_Type::getMedia($filetype); + if ('application' !== $media) { + $hint = sprintf(_(' Try using another %s format.'), $media); + } else { + $hint = ''; + } + $this->clientError(sprintf( + _('%s is not a supported filetype on this server.'), $filetype) . $hint); + } + + function isRespectsQuota($user) { + $file = new File; + $ret = $file->isRespectsQuota($user); + if (true === $ret) return true; + $this->clientError($ret); + } + /** * Save a new notice, based on arguments * @@ -131,7 +159,6 @@ class NewnoticeAction extends Action $this->clientError(_('No content!')); } else { $content_shortened = common_shorten_links($content); - if (mb_strlen($content_shortened) > 140) { $this->clientError(_('That\'s too long. '. 'Max notice size is 140 chars.')); @@ -158,14 +185,53 @@ class NewnoticeAction extends Action $replyto = 'false'; } - $notice = Notice::saveNew($user->id, $content, 'web', 1, + if (isset($_FILES['attach']['error'])) { + switch ($_FILES['attach']['error']) { + case UPLOAD_ERR_NO_FILE: + // no file uploaded, nothing to do + break; + + case UPLOAD_ERR_OK: + $mimetype = $this->getUploadedFileType(); + if (!$this->isRespectsQuota($user)) { + die('clientError() should trigger an exception before reaching here.'); + } + break; + + case UPLOAD_ERR_INI_SIZE: + $this->clientError(_('The uploaded file exceeds the upload_max_filesize directive in php.ini.')); + + case UPLOAD_ERR_FORM_SIZE: + $this->clientError(_('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.')); + + case UPLOAD_ERR_PARTIAL: + $this->clientError(_('The uploaded file was only partially uploaded.')); + + case UPLOAD_ERR_NO_TMP_DIR: + $this->clientError(_('Missing a temporary folder.')); + + case UPLOAD_ERR_CANT_WRITE: + $this->clientError(_('Failed to write file to disk.')); + + case UPLOAD_ERR_EXTENSION: + $this->clientError(_('File upload stopped by extension.')); + + default: + die('Should never reach here.'); + } + } + + $notice = Notice::saveNew($user->id, $content_shortened, 'web', 1, ($replyto == 'false') ? null : $replyto); if (is_string($notice)) { $this->clientError($notice); - return; } + if (isset($mimetype)) { + $this->storeFile($notice, $mimetype); + } + $this->saveUrls($notice); common_broadcast_notice($notice); if ($this->boolean('ajax')) { @@ -191,6 +257,51 @@ class NewnoticeAction extends Action } } + function storeFile($notice, $mimetype) { + $filename = basename($_FILES['attach']['name']); + $destination = "file/{$notice->id}-$filename"; + if (move_uploaded_file($_FILES['attach']['tmp_name'], INSTALLDIR . "/$destination")) { + $file = new File; + $file->url = common_local_url('file', array('notice' => $notice->id)); + $file->size = filesize(INSTALLDIR . "/$destination"); + $file->date = time(); + $file->mimetype = $mimetype; + if ($file_id = $file->insert()) { + $file_redir = new File_redirection; + $file_redir->url = common_path($destination); + $file_redir->file_id = $file_id; + $file_redir->insert(); + + $f2p = new File_to_post; + $f2p->file_id = $file_id; + $f2p->post_id = $notice->id; + $f2p->insert(); + } else { + $this->clientError(_('There was a database error while saving your file. Please try again.')); + } + } else { + $this->clientError(_('File could not be moved to destination directory.')); + } + } + + /** save all urls in the notice to the db + * + * follow redirects and save all available file information + * (mimetype, date, size, oembed, etc.) + * + * @param class $notice Notice to pull URLs from + * + * @return void + */ + function saveUrls($notice, $uploaded = null) { + common_replace_urls_callback($notice->content, array($this, 'saveUrl'), $notice->id); + } + + function saveUrl($data) { + list($url, $notice_id) = $data; + $zzz = File::processNew($url, $notice_id); + } + /** * Show an Ajax-y error message * @@ -295,3 +406,4 @@ class NewnoticeAction extends Action $nli->show(); } } + diff --git a/actions/register.php b/actions/register.php index 033cf557f..dcbbbdb6a 100644 --- a/actions/register.php +++ b/actions/register.php @@ -382,6 +382,19 @@ class RegisterAction extends Action function showFormContent() { + $code = $this->trimmed('code'); + + $invite = null; + + if ($code) { + $invite = Invitation::staticGet($code); + } + + if (common_config('site', 'inviteonly') && !($code && $invite)) { + $this->clientError(_('Sorry, only invited people can register.')); + return; + } + $this->elementStart('form', array('method' => 'post', 'id' => 'form_register', 'class' => 'form_settings', diff --git a/actions/shownotice.php b/actions/shownotice.php index 1be1e2414..b0d973a99 100644 --- a/actions/shownotice.php +++ b/actions/shownotice.php @@ -208,10 +208,10 @@ class ShownoticeAction extends Action function showContent() { - $this->elementStart('ul', array('class' => 'notices')); + $this->elementStart('ol', array('class' => 'notices xoxo')); $nli = new NoticeListItem($this->notice, $this); $nli->show(); - $this->elementEnd('ul'); + $this->elementEnd('ol'); } /** diff --git a/actions/showstream.php b/actions/showstream.php index c1a2c337a..e2f4e24d4 100644 --- a/actions/showstream.php +++ b/actions/showstream.php @@ -68,6 +68,9 @@ class ShowstreamAction extends ProfileAction } else { $base = $this->user->nickname; } + if (!empty($this->tag)) { + $base .= sprintf(_(' tagged %s'), $this->tag); + } if ($this->page == 1) { return $base; @@ -110,6 +113,15 @@ class ShowstreamAction extends ProfileAction function getFeeds() { + if (!empty($this->tag)) { + return array(new Feed(Feed::RSS1, + common_local_url('userrss', + array('nickname' => $this->user->nickname, + 'tag' => $this->tag)), + sprintf(_('Notice feed for %s tagged %s (RSS 1.0)'), + $this->user->nickname, $this->tag))); + } + return array(new Feed(Feed::RSS1, common_local_url('userrss', array('nickname' => $this->user->nickname)), @@ -352,7 +364,9 @@ class ShowstreamAction extends ProfileAction function showNotices() { - $notice = $this->user->getNotices(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1); + $notice = empty($this->tag) + ? $this->user->getNotices(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1) + : $this->user->getTaggedNotices(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1, 0, 0, null, $this->tag); $pnl = new ProfileNoticeList($notice, $this); $cnt = $pnl->show(); diff --git a/actions/twittersettings.php b/actions/twittersettings.php index 0b98eef59..2b742788e 100644 --- a/actions/twittersettings.php +++ b/actions/twittersettings.php @@ -138,7 +138,7 @@ class TwittersettingsAction extends ConnectSettingsAction $this->elementStart('ul', 'form_data'); $this->elementStart('li'); - $this->checkbox('noticesync', + $this->checkbox('noticesend', _('Automatically send my notices to Twitter.'), ($flink) ? ($flink->noticesync & FOREIGN_NOTICE_SEND) : @@ -158,6 +158,22 @@ class TwittersettingsAction extends ConnectSettingsAction ($flink->friendsync & FOREIGN_FRIEND_RECV) : false); $this->elementEnd('li'); + + if (common_config('twitterbridge','enabled')) { + $this->elementStart('li'); + $this->checkbox('noticerecv', + _('Import my Friends Timeline.'), + ($flink) ? + ($flink->noticesync & FOREIGN_NOTICE_RECV) : + false); + $this->elementEnd('li'); + } else { + // preserve setting even if bidrection bridge toggled off + if ($flink && ($flink->noticesync & FOREIGN_NOTICE_RECV)) { + $this->hidden('noticerecv', true, 'noticerecv'); + } + } + $this->elementEnd('ul'); if ($flink) { @@ -320,7 +336,8 @@ class TwittersettingsAction extends ConnectSettingsAction { $screen_name = $this->trimmed('twitter_username'); $password = $this->trimmed('twitter_password'); - $noticesync = $this->boolean('noticesync'); + $noticesend = $this->boolean('noticesend'); + $noticerecv = $this->boolean('noticerecv'); $replysync = $this->boolean('replysync'); $friendsync = $this->boolean('friendsync'); @@ -363,7 +380,7 @@ class TwittersettingsAction extends ConnectSettingsAction $flink->credentials = $password; $flink->created = common_sql_now(); - $flink->set_flags($noticesync, $replysync, $friendsync); + $flink->set_flags($noticesend, $noticerecv, $replysync, $friendsync); $flink_id = $flink->insert(); @@ -421,7 +438,8 @@ class TwittersettingsAction extends ConnectSettingsAction function savePreferences() { - $noticesync = $this->boolean('noticesync'); + $noticesend = $this->boolean('noticesend'); + $noticerecv = $this->boolean('noticerecv'); $friendsync = $this->boolean('friendsync'); $replysync = $this->boolean('replysync'); @@ -450,7 +468,7 @@ class TwittersettingsAction extends ConnectSettingsAction $original = clone($flink); - $flink->set_flags($noticesync, $replysync, $friendsync); + $flink->set_flags($noticesend, $noticerecv, $replysync, $friendsync); $result = $flink->update($original); diff --git a/actions/userrss.php b/actions/userrss.php index 5861d9ee3..2280509b2 100644 --- a/actions/userrss.php +++ b/actions/userrss.php @@ -25,14 +25,15 @@ require_once(INSTALLDIR.'/lib/rssaction.php'); class UserrssAction extends Rss10Action { - var $user = null; + var $tag = null; function prepare($args) { parent::prepare($args); - $nickname = $this->trimmed('nickname'); + $nickname = $this->trimmed('nickname'); $this->user = User::staticGet('nickname', $nickname); + $this->tag = $this->trimmed('tag'); if (!$this->user) { $this->clientError(_('No such user.')); @@ -42,6 +43,25 @@ class UserrssAction extends Rss10Action } } + function getTaggedNotices($tag = null, $limit=0) + { + $user = $this->user; + + if (is_null($user)) { + return null; + } + + $notice = $user->getTaggedNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit, 0, 0, null, $tag); + + $notices = array(); + while ($notice->fetch()) { + $notices[] = clone($notice); + } + + return $notices; + } + + function getNotices($limit=0) { |