diff options
175 files changed, 20838 insertions, 17825 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..c75c07de6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +avatar/* +files/* +_darcs/* +config.php +.htaccess diff --git a/actions/accesstoken.php b/actions/accesstoken.php index 4907749ce..072ce27eb 100644 --- a/actions/accesstoken.php +++ b/actions/accesstoken.php @@ -21,22 +21,24 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/omb.php'); -class AccesstokenAction extends Action { - function handle($args) { - parent::handle($args); - try { - common_debug('getting request from env variables', __FILE__); - common_remove_magic_from_request(); - $req = OAuthRequest::from_request(); - common_debug('getting a server', __FILE__); - $server = omb_oauth_server(); - common_debug('fetching the access token', __FILE__); - $token = $server->fetch_access_token($req); - common_debug('got this token: "'.print_r($token,TRUE).'"', __FILE__); - common_debug('printing the access token', __FILE__); - print $token; - } catch (OAuthException $e) { - common_server_error($e->getMessage()); - } - } +class AccesstokenAction extends Action +{ + function handle($args) + { + parent::handle($args); + try { + common_debug('getting request from env variables', __FILE__); + common_remove_magic_from_request(); + $req = OAuthRequest::from_request(); + common_debug('getting a server', __FILE__); + $server = omb_oauth_server(); + common_debug('fetching the access token', __FILE__); + $token = $server->fetch_access_token($req); + common_debug('got this token: "'.print_r($token,true).'"', __FILE__); + common_debug('printing the access token', __FILE__); + print $token; + } catch (OAuthException $e) { + common_server_error($e->getMessage()); + } + } } diff --git a/actions/all.php b/actions/all.php index 2a26e48d4..526ac5f40 100644 --- a/actions/all.php +++ b/actions/all.php @@ -21,73 +21,78 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/actions/showstream.php'); -class AllAction extends StreamAction { +class AllAction extends StreamAction +{ - function handle($args) { + function handle($args) + { - parent::handle($args); + parent::handle($args); - $nickname = common_canonical_nickname($this->arg('nickname')); - $user = User::staticGet('nickname', $nickname); + $nickname = common_canonical_nickname($this->arg('nickname')); + $user = User::staticGet('nickname', $nickname); - if (!$user) { - $this->client_error(_('No such user.')); - return; - } + if (!$user) { + $this->client_error(_('No such user.')); + return; + } - $profile = $user->getProfile(); + $profile = $user->getProfile(); - if (!$profile) { - common_server_error(_('User has no profile.')); - return; - } + if (!$profile) { + common_server_error(_('User has no profile.')); + return; + } - # Looks like we're good; show the header + # Looks like we're good; show the header - common_show_header(sprintf(_("%s and friends"), $profile->nickname), - array($this, 'show_header'), $user, - array($this, 'show_top')); + common_show_header(sprintf(_("%s and friends"), $profile->nickname), + array($this, 'show_header'), $user, + array($this, 'show_top')); - $this->show_notices($user); + $this->show_notices($user); - common_show_footer(); - } + common_show_footer(); + } - function show_header($user) { - common_element('link', array('rel' => 'alternate', - 'href' => common_local_url('allrss', array('nickname' => - $user->nickname)), - 'type' => 'application/rss+xml', - 'title' => sprintf(_('Feed for friends of %s'), $user->nickname))); - } + function show_header($user) + { + common_element('link', array('rel' => 'alternate', + 'href' => common_local_url('allrss', array('nickname' => + $user->nickname)), + 'type' => 'application/rss+xml', + 'title' => sprintf(_('Feed for friends of %s'), $user->nickname))); + } - function show_top($user) { - $cur = common_current_user(); + function show_top($user) + { + $cur = common_current_user(); - if ($cur && $cur->id == $user->id) { - common_notice_form('all'); - } + if ($cur && $cur->id == $user->id) { + common_notice_form('all'); + } - $this->views_menu(); + $this->views_menu(); - $this->show_feeds_list(array(0=>array('href'=>common_local_url('allrss', array('nickname' => $user->nickname)), - 'type' => 'rss', - 'version' => 'RSS 1.0', - 'item' => 'allrss'))); - } + $this->show_feeds_list(array(0=>array('href'=>common_local_url('allrss', array('nickname' => $user->nickname)), + 'type' => 'rss', + 'version' => 'RSS 1.0', + 'item' => 'allrss'))); + } - function show_notices($user) { + function show_notices($user) + { - $page = $this->trimmed('page'); - if (!$page) { - $page = 1; - } + $page = $this->trimmed('page'); + if (!$page) { + $page = 1; + } - $notice = $user->noticesWithFriends(($page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1); + $notice = $user->noticesWithFriends(($page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1); $cnt = $this->show_notice_list($notice); - common_pagination($page > 1, $cnt > NOTICES_PER_PAGE, - $page, 'all', array('nickname' => $user->nickname)); - } + common_pagination($page > 1, $cnt > NOTICES_PER_PAGE, + $page, 'all', array('nickname' => $user->nickname)); + } } diff --git a/actions/allrss.php b/actions/allrss.php index e49ac5540..660afb9e2 100644 --- a/actions/allrss.php +++ b/actions/allrss.php @@ -23,55 +23,60 @@ require_once(INSTALLDIR.'/lib/rssaction.php'); // Formatting of RSS handled by Rss10Action -class AllrssAction extends Rss10Action { +class AllrssAction extends Rss10Action +{ - var $user = NULL; + var $user = null; - function init() { - $nickname = $this->trimmed('nickname'); - $this->user = User::staticGet('nickname', $nickname); + function init() + { + $nickname = $this->trimmed('nickname'); + $this->user = User::staticGet('nickname', $nickname); - if (!$this->user) { - common_user_error(_('No such user.')); - return false; - } else { - return true; - } - } + if (!$this->user) { + common_user_error(_('No such user.')); + return false; + } else { + return true; + } + } - function get_notices($limit=0) { + function get_notices($limit=0) + { - $user = $this->user; - - $notice = $user->noticesWithFriends(0, $limit); - - while ($notice->fetch()) { - $notices[] = clone($notice); - } + $user = $this->user; + + $notice = $user->noticesWithFriends(0, $limit); + + while ($notice->fetch()) { + $notices[] = clone($notice); + } - return $notices; - } + return $notices; + } - function get_channel() { - $user = $this->user; - $c = array('url' => common_local_url('allrss', - array('nickname' => - $user->nickname)), - 'title' => sprintf(_('%s and friends'), $user->nickname), - 'link' => common_local_url('all', - array('nickname' => - $user->nickname)), - 'description' => sprintf(_('Feed for friends of %s'), $user->nickname)); - return $c; - } + function get_channel() + { + $user = $this->user; + $c = array('url' => common_local_url('allrss', + array('nickname' => + $user->nickname)), + 'title' => sprintf(_('%s and friends'), $user->nickname), + 'link' => common_local_url('all', + array('nickname' => + $user->nickname)), + 'description' => sprintf(_('Feed for friends of %s'), $user->nickname)); + return $c; + } - function get_image() { - $user = $this->user; - $profile = $user->getProfile(); - if (!$profile) { - return NULL; - } - $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); - return ($avatar) ? $avatar->url : NULL; - } + function get_image() + { + $user = $this->user; + $profile = $user->getProfile(); + if (!$profile) { + return null; + } + $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); + return ($avatar) ? $avatar->url : null; + } }
\ No newline at end of file diff --git a/actions/api.php b/actions/api.php index 919a515ce..7a0759831 100644 --- a/actions/api.php +++ b/actions/api.php @@ -10,115 +10,122 @@ * * 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 + * 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/>. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ if (!defined('LACONICA')) { exit(1); } -class ApiAction extends Action { - - var $user; - var $content_type; - var $api_arg; - var $api_method; - var $api_action; - - function handle($args) { - parent::handle($args); - - $this->api_action = $this->arg('apiaction'); - $method = $this->arg('method'); - $argument = $this->arg('argument'); - - if (isset($argument)) { - $cmdext = explode('.', $argument); - $this->api_arg = $cmdext[0]; - $this->api_method = $method; - $this->content_type = strtolower($cmdext[1]); - } else { - - # Requested format / content-type will be an extension on the method - $cmdext = explode('.', $method); - $this->api_method = $cmdext[0]; - $this->content_type = strtolower($cmdext[1]); - } - - if ($this->requires_auth()) { - if (!isset($_SERVER['PHP_AUTH_USER'])) { - - # This header makes basic auth go - header('WWW-Authenticate: Basic realm="Laconica API"'); - - # If the user hits cancel -- bam! - $this->show_basic_auth_error(); - } else { - $nickname = $_SERVER['PHP_AUTH_USER']; - $password = $_SERVER['PHP_AUTH_PW']; - $user = common_check_user($nickname, $password); - - if ($user) { - $this->user = $user; - $this->process_command(); - } else { - # basic authentication failed - $this->show_basic_auth_error(); - } - } - } else { - - # Look for the user in the session - if (common_logged_in()) { - $this->user = common_current_user(); - } - - $this->process_command(); - } - } - - function process_command() { - $action = "twitapi$this->api_action"; - $actionfile = INSTALLDIR."/actions/$action.php"; - - if (file_exists($actionfile)) { - require_once($actionfile); - $action_class = ucfirst($action)."Action"; - $action_obj = new $action_class(); +class ApiAction extends Action +{ + + var $user; + var $content_type; + var $api_arg; + var $api_method; + var $api_action; + + function handle($args) + { + parent::handle($args); + + $this->api_action = $this->arg('apiaction'); + $method = $this->arg('method'); + $argument = $this->arg('argument'); + + if (isset($argument)) { + $cmdext = explode('.', $argument); + $this->api_arg = $cmdext[0]; + $this->api_method = $method; + $this->content_type = strtolower($cmdext[1]); + } else { + + # Requested format / content-type will be an extension on the method + $cmdext = explode('.', $method); + $this->api_method = $cmdext[0]; + $this->content_type = strtolower($cmdext[1]); + } + + if ($this->requires_auth()) { + if (!isset($_SERVER['PHP_AUTH_USER'])) { + + # This header makes basic auth go + header('WWW-Authenticate: Basic realm="Laconica API"'); + + # If the user hits cancel -- bam! + $this->show_basic_auth_error(); + } else { + $nickname = $_SERVER['PHP_AUTH_USER']; + $password = $_SERVER['PHP_AUTH_PW']; + $user = common_check_user($nickname, $password); + + if ($user) { + $this->user = $user; + $this->process_command(); + } else { + # basic authentication failed + $this->show_basic_auth_error(); + } + } + } else { + + # Look for the user in the session + if (common_logged_in()) { + $this->user = common_current_user(); + } + + $this->process_command(); + } + } + + function process_command() + { + $action = "twitapi$this->api_action"; + $actionfile = INSTALLDIR."/actions/$action.php"; + + if (file_exists($actionfile)) { + require_once($actionfile); + $action_class = ucfirst($action)."Action"; + $action_obj = new $action_class(); if (!$action_obj->prepare($this->args)) { return; } - if (method_exists($action_obj, $this->api_method)) { - $apidata = array( 'content-type' => $this->content_type, - 'api_method' => $this->api_method, - 'api_arg' => $this->api_arg, - 'user' => $this->user); - - call_user_func(array($action_obj, $this->api_method), $_REQUEST, $apidata); - } else { - common_user_error("API method not found!", $code=404); - } - } else { - common_user_error("API method not found!", $code=404); - } - } - - # Whitelist of API methods that don't need authentication - function requires_auth() { - static $noauth = array( 'statuses/public_timeline', - 'statuses/show', - 'users/show', - 'help/test', - 'help/downtime_schedule'); - - static $bareauth = array('statuses/user_timeline', - 'statuses/friends', - 'statuses/followers', - 'favorites/favorites'); + if (method_exists($action_obj, $this->api_method)) { + $apidata = array( 'content-type' => $this->content_type, + 'api_method' => $this->api_method, + 'api_arg' => $this->api_arg, + 'user' => $this->user); + + call_user_func(array($action_obj, $this->api_method), $_REQUEST, $apidata); + } else { + common_user_error("API method not found!", $code=404); + } + } else { + common_user_error("API method not found!", $code=404); + } + } + + # Whitelist of API methods that don't need authentication + function requires_auth() + { + static $noauth = array( 'statuses/public_timeline', + 'statuses/show', + 'users/show', + 'help/test', + 'help/downtime_schedule', + 'laconica/version', + 'laconica/config', + 'laconica/wadl'); + + static $bareauth = array('statuses/user_timeline', + 'statuses/friends', + 'statuses/followers', + 'favorites/favorites'); # If the site is "private", all API methods need authentication @@ -126,71 +133,73 @@ class ApiAction extends Action { return true; } - $fullname = "$this->api_action/$this->api_method"; - - if (in_array($fullname, $bareauth)) { - # bareauth: only needs auth if without an argument - if ($this->api_arg) { - return false; - } else { - return true; - } - } else if (in_array($fullname, $noauth)) { - # noauth: never needs auth - return false; - } else { - # everybody else needs auth - return true; - } - } - - function show_basic_auth_error() { - header('HTTP/1.1 401 Unauthorized'); - $msg = 'Could not authenticate you.'; - - if ($this->content_type == 'xml') { - header('Content-Type: application/xml; charset=utf-8'); - common_start_xml(); - common_element_start('hash'); - common_element('error', NULL, $msg); - common_element('request', NULL, $_SERVER['REQUEST_URI']); - common_element_end('hash'); - common_end_xml(); - } else if ($this->content_type == 'json') { - header('Content-Type: application/json; charset=utf-8'); - $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']); - print(json_encode($error_array)); - } else { - header('Content-type: text/plain'); - print "$msg\n"; - } - } - - function is_readonly() { - # NOTE: before handle(), can't use $this->arg - $apiaction = $_REQUEST['apiaction']; - $method = $_REQUEST['method']; - list($cmdtext, $fmt) = explode('.', $method); - - static $write_methods = array( - 'account' => array('update_location', 'update_delivery_device', 'end_session'), - 'blocks' => array('create', 'destroy'), - 'direct_messages' => array('create', 'destroy'), - 'favorites' => array('create', 'destroy'), - 'friendships' => array('create', 'destroy'), - 'help' => array(), - 'notifications' => array('follow', 'leave'), - 'statuses' => array('update', 'destroy'), - 'users' => array() - ); - - if (array_key_exists($apiaction, $write_methods)) { - if (!in_array($cmdtext, $write_methods[$apiaction])) { - return true; - } - } - - return false; - } + $fullname = "$this->api_action/$this->api_method"; + + if (in_array($fullname, $bareauth)) { + # bareauth: only needs auth if without an argument + if ($this->api_arg) { + return false; + } else { + return true; + } + } else if (in_array($fullname, $noauth)) { + # noauth: never needs auth + return false; + } else { + # everybody else needs auth + return true; + } + } + + function show_basic_auth_error() + { + header('HTTP/1.1 401 Unauthorized'); + $msg = 'Could not authenticate you.'; + + if ($this->content_type == 'xml') { + header('Content-Type: application/xml; charset=utf-8'); + common_start_xml(); + common_element_start('hash'); + common_element('error', null, $msg); + common_element('request', null, $_SERVER['REQUEST_URI']); + common_element_end('hash'); + common_end_xml(); + } else if ($this->content_type == 'json') { + header('Content-Type: application/json; charset=utf-8'); + $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']); + print(json_encode($error_array)); + } else { + header('Content-type: text/plain'); + print "$msg\n"; + } + } + + function is_readonly() + { + # NOTE: before handle(), can't use $this->arg + $apiaction = $_REQUEST['apiaction']; + $method = $_REQUEST['method']; + list($cmdtext, $fmt) = explode('.', $method); + + static $write_methods = array( + 'account' => array('update_location', 'update_delivery_device', 'end_session'), + 'blocks' => array('create', 'destroy'), + 'direct_messages' => array('create', 'destroy'), + 'favorites' => array('create', 'destroy'), + 'friendships' => array('create', 'destroy'), + 'help' => array(), + 'notifications' => array('follow', 'leave'), + 'statuses' => array('update', 'destroy'), + 'users' => array() + ); + + if (array_key_exists($apiaction, $write_methods)) { + if (!in_array($cmdtext, $write_methods[$apiaction])) { + return true; + } + } + + return false; + } } diff --git a/actions/avatarbynickname.php b/actions/avatarbynickname.php index b33cababf..666f386f6 100644 --- a/actions/avatarbynickname.php +++ b/actions/avatarbynickname.php @@ -19,50 +19,52 @@ if (!defined('LACONICA')) { exit(1); } -class AvatarbynicknameAction extends Action { - function handle($args) { +class AvatarbynicknameAction extends Action +{ + function handle($args) + { parent::handle($args); $nickname = $this->trimmed('nickname'); if (!$nickname) { - $this->client_error(_('No nickname.')); - return; - } - $size = $this->trimmed('size'); + $this->client_error(_('No nickname.')); + return; + } + $size = $this->trimmed('size'); if (!$size) { - $this->client_error(_('No size.')); - return; - } - $size = strtolower($size); - if (!in_array($size, array('original', '96', '48', '24'))) { - $this->client_error(_('Invalid size.')); - return; - } + $this->client_error(_('No size.')); + return; + } + $size = strtolower($size); + if (!in_array($size, array('original', '96', '48', '24'))) { + $this->client_error(_('Invalid size.')); + return; + } - $user = User::staticGet('nickname', $nickname); - if (!$user) { - $this->client_error(_('No such user.')); - return; - } - $profile = $user->getProfile(); - if (!$profile) { - $this->client_error(_('User has no profile.')); - return; - } - if ($size == 'original') { - $avatar = $profile->getOriginal(); - } else { - $avatar = $profile->getAvatar($size+0); - } + $user = User::staticGet('nickname', $nickname); + if (!$user) { + $this->client_error(_('No such user.')); + return; + } + $profile = $user->getProfile(); + if (!$profile) { + $this->client_error(_('User has no profile.')); + return; + } + if ($size == 'original') { + $avatar = $profile->getOriginal(); + } else { + $avatar = $profile->getAvatar($size+0); + } - if ($avatar) { - $url = $avatar->url; - } else { - if ($size == 'original') { - $url = common_default_avatar(AVATAR_PROFILE_SIZE); - } else { - $url = common_default_avatar($size+0); - } - } - common_redirect($url, 302); - } + if ($avatar) { + $url = $avatar->url; + } else { + if ($size == 'original') { + $url = common_default_avatar(AVATAR_PROFILE_SIZE); + } else { + $url = common_default_avatar($size+0); + } + } + common_redirect($url, 302); + } } diff --git a/actions/block.php b/actions/block.php index e6d2b7e49..c1ff7c044 100644 --- a/actions/block.php +++ b/actions/block.php @@ -19,11 +19,13 @@ if (!defined('LACONICA')) { exit(1); } -class BlockAction extends Action { +class BlockAction extends Action +{ - var $profile = NULL; + var $profile = null; - function prepare($args) { + function prepare($args) + { parent::prepare($args); @@ -32,12 +34,12 @@ class BlockAction extends Action { return false; } - $token = $this->trimmed('token'); + $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->client_error(_('There was a problem with your session token. Try again, please.')); - return; - } + if (!$token || $token != common_session_token()) { + $this->client_error(_('There was a problem with your session token. Try again, please.')); + return; + } $id = $this->trimmed('blockto'); @@ -56,7 +58,8 @@ class BlockAction extends Action { return true; } - function handle($args) { + function handle($args) + { parent::handle($args); if ($_SERVER['REQUEST_METHOD'] == 'POST') { if ($this->arg('block')) { @@ -71,13 +74,14 @@ class BlockAction extends Action { } } - function are_you_sure_form() { + function are_you_sure_form() + { $id = $this->profile->id; - common_show_header(_('Block user')); + common_show_header(_('Block user')); - common_element('p', NULL, + common_element('p', null, _('Are you sure you want to block this user? '. 'Afterwards, they will be unsubscribed from you, '. 'unable to subscribe to you in the future, and '. @@ -109,7 +113,8 @@ class BlockAction extends Action { common_show_footer(); } - function block_profile() { + function block_profile() + { $cur = common_current_user(); diff --git a/actions/confirmaddress.php b/actions/confirmaddress.php index 44280e08a..1d5c53ff2 100644 --- a/actions/confirmaddress.php +++ b/actions/confirmaddress.php @@ -19,9 +19,11 @@ if (!defined('LACONICA')) { exit(1); } -class ConfirmaddressAction extends Action { +class ConfirmaddressAction extends Action +{ - function handle($args) { + function handle($args) + { parent::handle($args); if (!common_logged_in()) { common_set_returnto($this->self_url()); @@ -43,44 +45,44 @@ class ConfirmaddressAction extends Action { $this->client_error(_('That confirmation code is not for you!')); return; } - $type = $confirm->address_type; - if (!in_array($type, array('email', 'jabber', 'sms'))) { - $this->server_error(sprintf(_('Unrecognized address type %s'), $type)); - return; - } + $type = $confirm->address_type; + if (!in_array($type, array('email', 'jabber', 'sms'))) { + $this->server_error(sprintf(_('Unrecognized address type %s'), $type)); + return; + } if ($cur->$type == $confirm->address) { $this->client_error(_('That address has already been confirmed.')); - return; - } + return; + } $cur->query('BEGIN'); $orig_user = clone($cur); - $cur->$type = $confirm->address; + $cur->$type = $confirm->address; - if ($type == 'sms') { - $cur->carrier = ($confirm->address_extra)+0; - $carrier = Sms_carrier::staticGet($cur->carrier); - $cur->smsemail = $carrier->toEmailAddress($cur->sms); - } + if ($type == 'sms') { + $cur->carrier = ($confirm->address_extra)+0; + $carrier = Sms_carrier::staticGet($cur->carrier); + $cur->smsemail = $carrier->toEmailAddress($cur->sms); + } - $result = $cur->updateKeys($orig_user); + $result = $cur->updateKeys($orig_user); if (!$result) { - common_log_db_error($cur, 'UPDATE', __FILE__); + common_log_db_error($cur, 'UPDATE', __FILE__); $this->server_error(_('Couldn\'t update user.')); return; } - if ($type == 'email') { - $cur->emailChanged(); - } + if ($type == 'email') { + $cur->emailChanged(); + } $result = $confirm->delete(); if (!$result) { - common_log_db_error($confirm, 'DELETE', __FILE__); + common_log_db_error($confirm, 'DELETE', __FILE__); $this->server_error(_('Couldn\'t delete email confirmation.')); return; } @@ -88,7 +90,7 @@ class ConfirmaddressAction extends Action { $cur->query('COMMIT'); common_show_header(_('Confirm Address')); - common_element('p', NULL, + common_element('p', null, sprintf(_('The address "%s" has been confirmed for your account.'), $cur->$type)); common_show_footer(); } diff --git a/actions/deletenotice.php b/actions/deletenotice.php index 64746283a..e9b4b3254 100644 --- a/actions/deletenotice.php +++ b/actions/deletenotice.php @@ -21,81 +21,87 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/deleteaction.php'); -class DeletenoticeAction extends DeleteAction { - function handle($args) { - parent::handle($args); - # XXX: Ajax! +class DeletenoticeAction extends DeleteAction +{ + function handle($args) + { + parent::handle($args); + # XXX: Ajax! - if ($_SERVER['REQUEST_METHOD'] == 'POST') { - $this->delete_notice(); - } else if ($_SERVER['REQUEST_METHOD'] == 'GET') { - $this->show_form(); - } - } + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $this->delete_notice(); + } else if ($_SERVER['REQUEST_METHOD'] == 'GET') { + $this->show_form(); + } + } - function get_instructions() { - return _('You are about to permanently delete a notice. Once this is done, it cannot be undone.'); - } + function get_instructions() + { + return _('You are about to permanently delete a notice. Once this is done, it cannot be undone.'); + } - function get_title() { - return _('Delete notice'); - } + function get_title() + { + return _('Delete notice'); + } - function show_form($error=NULL) { - $user = common_current_user(); + function show_form($error=null) + { + $user = common_current_user(); - common_show_header($this->get_title(), array($this, 'show_header'), $error, - array($this, 'show_top')); - common_element_start('form', array('id' => 'notice_delete_form', - 'method' => 'post', - 'action' => common_local_url('deletenotice'))); - common_hidden('token', common_session_token()); - common_hidden('notice', $this->trimmed('notice')); - common_element_start('p'); - common_element('span', array('id' => 'confirmation_text'), _('Are you sure you want to delete this notice?')); + common_show_header($this->get_title(), array($this, 'show_header'), $error, + array($this, 'show_top')); + common_element_start('form', array('id' => 'notice_delete_form', + 'method' => 'post', + 'action' => common_local_url('deletenotice'))); + common_hidden('token', common_session_token()); + common_hidden('notice', $this->trimmed('notice')); + common_element_start('p'); + common_element('span', array('id' => 'confirmation_text'), _('Are you sure you want to delete this notice?')); - common_element('input', array('id' => 'submit_no', - 'name' => 'submit', - 'type' => 'submit', - 'value' => _('No'))); - common_element('input', array('id' => 'submit_yes', - 'name' => 'submit', - 'type' => 'submit', - 'value' => _('Yes'))); - common_element_end('p'); - common_element_end('form'); - common_show_footer(); - } + common_element('input', array('id' => 'submit_no', + 'name' => 'submit', + 'type' => 'submit', + 'value' => _('No'))); + common_element('input', array('id' => 'submit_yes', + 'name' => 'submit', + 'type' => 'submit', + 'value' => _('Yes'))); + common_element_end('p'); + common_element_end('form'); + common_show_footer(); + } - function delete_notice() { - # CSRF protection - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->show_form(_('There was a problem with your session token. Try again, please.')); - return; - } - $url = common_get_returnto(); - $confirmed = $this->trimmed('submit'); - if ($confirmed == _('Yes')) { - $user = common_current_user(); - $notice_id = $this->trimmed('notice'); - $notice = Notice::staticGet($notice_id); - $replies = new Reply; - $replies->get('notice_id', $notice_id); + function delete_notice() + { + # CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->show_form(_('There was a problem with your session token. Try again, please.')); + return; + } + $url = common_get_returnto(); + $confirmed = $this->trimmed('submit'); + if ($confirmed == _('Yes')) { + $user = common_current_user(); + $notice_id = $this->trimmed('notice'); + $notice = Notice::staticGet($notice_id); + $replies = new Reply; + $replies->get('notice_id', $notice_id); - common_dequeue_notice($notice); - if (common_config('memcached', 'enabled')) { - $notice->blowSubsCache(); - } - $replies->delete(); - $notice->delete(); - } else { - if ($url) { - common_set_returnto(NULL); - } else { - $url = common_local_url('public'); - } - } - common_redirect($url); - } + common_dequeue_notice($notice); + if (common_config('memcached', 'enabled')) { + $notice->blowSubsCache(); + } + $replies->delete(); + $notice->delete(); + } else { + if ($url) { + common_set_returnto(null); + } else { + $url = common_local_url('public'); + } + } + common_redirect($url); + } } diff --git a/actions/deleteprofile.php b/actions/deleteprofile.php index 418ac998d..e12fe131a 100644 --- a/actions/deleteprofile.php +++ b/actions/deleteprofile.php @@ -19,8 +19,10 @@ if (!defined('LACONICA')) { exit(1); } -class DeleteprofileAction extends Action { - function handle($args) { +class DeleteprofileAction extends Action +{ + function handle($args) + { parent::handle($args); $this->server_error(_('Code not yet ready.')); return; @@ -32,101 +34,108 @@ class DeleteprofileAction extends Action { } } - function get_instructions() { - return _('Export and delete your user information.'); - } - - function form_header($title, $msg=NULL, $success=false) { - common_show_header($title, - NULL, - array($msg, $success), - array($this, 'show_top')); - } - - function show_feeds_list($feeds) { - common_element_start('div', array('class' => 'feedsdel')); - common_element('p', null, 'Feeds:'); - common_element_start('ul', array('class' => 'xoxo')); - - foreach ($feeds as $key => $value) { - $this->common_feed_item($feeds[$key]); - } - common_element_end('ul'); - common_element_end('div'); - } + function get_instructions() + { + return _('Export and delete your user information.'); + } + + function form_header($title, $msg=null, $success=false) + { + common_show_header($title, + null, + array($msg, $success), + array($this, 'show_top')); + } + + function show_feeds_list($feeds) + { + common_element_start('div', array('class' => 'feedsdel')); + common_element('p', null, 'Feeds:'); + common_element_start('ul', array('class' => 'xoxo')); + + foreach ($feeds as $key => $value) { + $this->common_feed_item($feeds[$key]); + } + common_element_end('ul'); + common_element_end('div'); + } //TODO move to common.php (and retrace its origin) - function common_feed_item($feed) { + function common_feed_item($feed) + { + $user = common_current_user(); + $nickname = $user->nickname; + + switch($feed['item']) { + case 'notices': default: + $feed_classname = $feed['type']; + $feed_mimetype = "application/".$feed['type']."+xml"; + $feed_title = "$nickname's ".$feed['version']." notice feed"; + $feed['textContent'] = "RSS"; + break; + + case 'foaf': + $feed_classname = "foaf"; + $feed_mimetype = "application/".$feed['type']."+xml"; + $feed_title = "$nickname's FOAF file"; + $feed['textContent'] = "FOAF"; + break; + } + common_element_start('li'); + common_element('a', array('href' => $feed['href'], + 'class' => $feed_classname, + 'type' => $feed_mimetype, + 'title' => $feed_title), + $feed['textContent']); + common_element_end('li'); + } + + function show_form($msg=null, $success=false) + { + $this->form_header(_('Delete my account'), $msg, $success); + common_element('h2', null, _('Delete my account confirmation')); + $this->show_confirm_delete_form(); + common_show_footer(); + } + + function show_confirm_delete_form() + { $user = common_current_user(); - $nickname = $user->nickname; - - switch($feed['item']) { - case 'notices': default: - $feed_classname = $feed['type']; - $feed_mimetype = "application/".$feed['type']."+xml"; - $feed_title = "$nickname's ".$feed['version']." notice feed"; - $feed['textContent'] = "RSS"; - break; - - case 'foaf': - $feed_classname = "foaf"; - $feed_mimetype = "application/".$feed['type']."+xml"; - $feed_title = "$nickname's FOAF file"; - $feed['textContent'] = "FOAF"; - break; - } - common_element_start('li'); - common_element('a', array('href' => $feed['href'], - 'class' => $feed_classname, - 'type' => $feed_mimetype, - 'title' => $feed_title), - $feed['textContent']); - common_element_end('li'); - } - - function show_form($msg=NULL, $success=false) { - $this->form_header(_('Delete my account'), $msg, $success); - common_element('h2', NULL, _('Delete my account confirmation')); - $this->show_confirm_delete_form(); - common_show_footer(); - } - - function show_confirm_delete_form() { - $user = common_current_user(); $notices = DB_DataObject::factory('notice'); $notices->profile_id = $user->id; $notice_count = (int) $notices->count(); - common_element_start('form', array('method' => 'POST', - 'id' => 'delete', - 'action' => - common_local_url('deleteprofile'))); + common_element_start('form', array('method' => 'POST', + 'id' => 'delete', + 'action' => + common_local_url('deleteprofile'))); - common_hidden('token', common_session_token()); + common_hidden('token', common_session_token()); common_element('p', null, "Last chance to copy your notices and contacts by saving the two links below before deleting your account. Be careful, this operation cannot be undone."); - $this->show_feeds_list(array(0=>array('href'=>common_local_url('userrss', array('limit' => $notice_count, 'nickname' => $user->nickname)), - 'type' => 'rss', - 'version' => 'RSS 1.0', - 'item' => 'notices'), - 1=>array('href'=>common_local_url('foaf',array('nickname' => $user->nickname)), - 'type' => 'rdf', - 'version' => 'FOAF', - 'item' => 'foaf'))); + $this->show_feeds_list(array(0=>array('href'=>common_local_url('userrss', array('limit' => $notice_count, 'nickname' => $user->nickname)), + 'type' => 'rss', + 'version' => 'RSS 1.0', + 'item' => 'notices'), + 1=>array('href'=>common_local_url('foaf',array('nickname' => $user->nickname)), + 'type' => 'rdf', + 'version' => 'FOAF', + 'item' => 'foaf'))); common_checkbox('confirmation', _('Check if you are sure you want to delete your account.')); - common_submit('deleteaccount', _('Delete my account')); - common_element_end('form'); + common_submit('deleteaccount', _('Delete my account')); + common_element_end('form'); } - function handle_post() { - # CSRF protection - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->show_form(_('There was a problem with your session token. Try again, please.')); - return; - } + function handle_post() + { + # CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->show_form(_('There was a problem with your session token. Try again, please.')); + return; + } if ($this->arg('deleteaccount') && $this->arg('confirmation')) { $this->delete_account(); @@ -134,9 +143,10 @@ class DeleteprofileAction extends Action { $this->show_form(); } - function delete_account() { - $user = common_current_user(); - assert(!is_null($user)); # should already be checked + function delete_account() + { + $user = common_current_user(); + assert(!is_null($user)); # should already be checked // deleted later through the profile /* @@ -213,59 +223,61 @@ class DeleteprofileAction extends Action { $n_users_deleted = $user->delete(); // logout and redirect to public - common_set_user(NULL); + common_set_user(null); common_real_login(false); # not logged in common_forgetme(); # don't log back in! common_redirect(common_local_url('public')); } - function show_top($arr) { - $msg = $arr[0]; - $success = $arr[1]; - if ($msg) { - $this->message($msg, $success); - } else { - $inst = $this->get_instructions(); - $output = common_markup_to_html($inst); - common_element_start('div', 'instructions'); - common_raw($output); - common_element_end('div'); - } - $this->settings_menu(); - } - - function settings_menu() { + function show_top($arr) + { + $msg = $arr[0]; + $success = $arr[1]; + if ($msg) { + $this->message($msg, $success); + } else { + $inst = $this->get_instructions(); + $output = common_markup_to_html($inst); + common_element_start('div', 'instructions'); + common_raw($output); + common_element_end('div'); + } + $this->settings_menu(); + } + + function settings_menu() + { # action => array('prompt', 'title') - $menu = - array('profilesettings' => - array(_('Profile'), - _('Change your profile settings')), - 'emailsettings' => - array(_('Email'), - _('Change email handling')), - 'openidsettings' => - array(_('OpenID'), - _('Add or remove OpenIDs')), - 'smssettings' => - array(_('SMS'), - _('Updates by SMS')), - 'imsettings' => - array(_('IM'), - _('Updates by instant messenger (IM)')), - 'twittersettings' => - array(_('Twitter'), - _('Twitter integration options')), - 'othersettings' => - array(_('Other'), - _('Other options'))); + $menu = + array('profilesettings' => + array(_('Profile'), + _('Change your profile settings')), + 'emailsettings' => + array(_('Email'), + _('Change email handling')), + 'openidsettings' => + array(_('OpenID'), + _('Add or remove OpenIDs')), + 'smssettings' => + array(_('SMS'), + _('Updates by SMS')), + 'imsettings' => + array(_('IM'), + _('Updates by instant messenger (IM)')), + 'twittersettings' => + array(_('Twitter'), + _('Twitter integration options')), + 'othersettings' => + array(_('Other'), + _('Other options'))); $action = $this->trimmed('action'); common_element_start('ul', array('id' => 'nav_views')); foreach ($menu as $menuaction => $menudesc) { - if ($menuaction == 'imsettings' && - !common_config('xmpp', 'enabled')) { - continue; - } + if ($menuaction == 'imsettings' && + !common_config('xmpp', 'enabled')) { + continue; + } common_menu_item(common_local_url($menuaction), $menudesc[0], $menudesc[1], diff --git a/actions/disfavor.php b/actions/disfavor.php index be208f65a..74aae86cc 100644 --- a/actions/disfavor.php +++ b/actions/disfavor.php @@ -19,65 +19,67 @@ if (!defined('LACONICA')) { exit(1); } -class DisfavorAction extends Action { +class DisfavorAction extends Action +{ - function handle($args) { + function handle($args) + { - parent::handle($args); + parent::handle($args); - if (!common_logged_in()) { - common_user_error(_('Not logged in.')); - return; - } + if (!common_logged_in()) { + common_user_error(_('Not logged in.')); + return; + } - $user = common_current_user(); + $user = common_current_user(); - if ($_SERVER['REQUEST_METHOD'] != 'POST') { - common_redirect(common_local_url('showfavorites', array('nickname' => $user->nickname))); - return; - } + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + common_redirect(common_local_url('showfavorites', array('nickname' => $user->nickname))); + return; + } - $id = $this->trimmed('notice'); + $id = $this->trimmed('notice'); - $notice = Notice::staticGet($id); + $notice = Notice::staticGet($id); - $token = $this->trimmed('token-'.$notice->id); + $token = $this->trimmed('token-'.$notice->id); - if (!$token || $token != common_session_token()) { - $this->client_error(_("There was a problem with your session token. Try again, please.")); - return; - } + if (!$token || $token != common_session_token()) { + $this->client_error(_("There was a problem with your session token. Try again, please.")); + return; + } - $fave = new Fave(); - $fave->user_id = $this->id; - $fave->notice_id = $notice->id; - if (!$fave->find(true)) { - $this->client_error(_('This notice is not a favorite!')); - return; - } + $fave = new Fave(); + $fave->user_id = $this->id; + $fave->notice_id = $notice->id; + if (!$fave->find(true)) { + $this->client_error(_('This notice is not a favorite!')); + return; + } - $result = $fave->delete(); + $result = $fave->delete(); - if (!$result) { - common_log_db_error($fave, 'DELETE', __FILE__); - $this->server_error(_('Could not delete favorite.')); - return; - } - - $user->blowFavesCache(); + if (!$result) { + common_log_db_error($fave, 'DELETE', __FILE__); + $this->server_error(_('Could not delete favorite.')); + return; + } + + $user->blowFavesCache(); - if ($this->boolean('ajax')) { - common_start_html('text/xml;charset=utf-8', true); - common_element_start('head'); - common_element('title', null, _('Add to favorites')); - common_element_end('head'); - common_element_start('body'); - common_favor_form($notice); - common_element_end('body'); - common_element_end('html'); - } else { - common_redirect(common_local_url('showfavorites', - array('nickname' => $user->nickname))); - } - } + if ($this->boolean('ajax')) { + common_start_html('text/xml;charset=utf-8', true); + common_element_start('head'); + common_element('title', null, _('Add to favorites')); + common_element_end('head'); + common_element_start('body'); + common_favor_form($notice); + common_element_end('body'); + common_element_end('html'); + } else { + common_redirect(common_local_url('showfavorites', + array('nickname' => $user->nickname))); + } + } } diff --git a/actions/doc.php b/actions/doc.php index f3327048f..856025e66 100644 --- a/actions/doc.php +++ b/actions/doc.php @@ -19,20 +19,22 @@ if (!defined('LACONICA')) { exit(1); } -class DocAction extends Action { +class DocAction extends Action +{ - function handle($args) { - parent::handle($args); - $title = $this->trimmed('title'); - $filename = INSTALLDIR.'/doc/'.$title; - if (!file_exists($filename)) { - common_user_error(_('No such document.')); - return; - } - $c = file_get_contents($filename); - $output = common_markup_to_html($c); - common_show_header(_(ucfirst($title))); - common_raw($output); - common_show_footer(); - } + function handle($args) + { + parent::handle($args); + $title = $this->trimmed('title'); + $filename = INSTALLDIR.'/doc/'.$title; + if (!file_exists($filename)) { + common_user_error(_('No such document.')); + return; + } + $c = file_get_contents($filename); + $output = common_markup_to_html($c); + common_show_header(_(ucfirst($title))); + common_raw($output); + common_show_footer(); + } } diff --git a/actions/emailsettings.php b/actions/emailsettings.php index b35b4d28e..3fa8ce296 100644 --- a/actions/emailsettings.php +++ b/actions/emailsettings.php @@ -21,310 +21,322 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/settingsaction.php'); -class EmailsettingsAction extends SettingsAction { - - function get_instructions() { - return _('Manage how you get email from %%site.name%%.'); - } - - function show_form($msg=NULL, $success=false) { - $user = common_current_user(); - $this->form_header(_('Email Settings'), $msg, $success); - common_element_start('form', array('method' => 'post', - 'id' => 'emailsettings', - 'action' => - common_local_url('emailsettings'))); - common_hidden('token', common_session_token()); - - common_element('h2', NULL, _('Address')); - - if ($user->email) { - common_element_start('p'); - common_element('span', 'address confirmed', $user->email); - common_element('span', 'input_instructions', - _('Current confirmed email address.')); - common_hidden('email', $user->email); - common_element_end('p'); - common_submit('remove', _('Remove')); - } else { - $confirm = $this->get_confirmation(); - if ($confirm) { - common_element_start('p'); - common_element('span', 'address unconfirmed', $confirm->address); - common_element('span', 'input_instructions', - _('Awaiting confirmation on this address. Check your inbox (and spam box!) for a message with further instructions.')); - common_hidden('email', $confirm->address); - common_element_end('p'); - common_submit('cancel', _('Cancel')); - } else { - common_input('email', _('Email Address'), - ($this->arg('email')) ? $this->arg('email') : NULL, - _('Email address, like "UserName@example.org"')); - common_submit('add', _('Add')); - } - } - - if ($user->email) { - common_element('h2', NULL, _('Incoming email')); - - if ($user->incomingemail) { - common_element_start('p'); - common_element('span', 'address', $user->incomingemail); - common_element('span', 'input_instructions', - _('Send email to this address to post new notices.')); - common_element_end('p'); - common_submit('removeincoming', _('Remove')); - } - - common_element_start('p'); - common_element('span', 'input_instructions', - _('Make a new email address for posting to; cancels the old one.')); - common_element_end('p'); - common_submit('newincoming', _('New')); - } - - common_element('h2', NULL, _('Preferences')); - - common_checkbox('emailnotifysub', - _('Send me notices of new subscriptions through email.'), - $user->emailnotifysub); - common_checkbox('emailnotifyfav', - _('Send me email when someone adds my notice as a favorite.'), - $user->emailnotifyfav); - common_checkbox('emailnotifymsg', - _('Send me email when someone sends me a private message.'), - $user->emailnotifymsg); - common_checkbox('emailnotifynudge', - _('Allow friends to nudge me and send me an email.'), - $user->emailnotifynudge); - common_checkbox('emailpost', - _('I want to post notices by email.'), - $user->emailpost); - common_checkbox('emailmicroid', - _('Publish a MicroID for my email address.'), - $user->emailmicroid); - - common_submit('save', _('Save')); - - common_element_end('form'); - common_show_footer(); - } - - function get_confirmation() { - $user = common_current_user(); - $confirm = new Confirm_address(); - $confirm->user_id = $user->id; - $confirm->address_type = 'email'; - if ($confirm->find(TRUE)) { - return $confirm; - } else { - return NULL; - } - } - - function handle_post() { - - # CSRF protection - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->show_form(_('There was a problem with your session token. Try again, please.')); - return; - } - - if ($this->arg('save')) { - $this->save_preferences(); - } else if ($this->arg('add')) { - $this->add_address(); - } else if ($this->arg('cancel')) { - $this->cancel_confirmation(); - } else if ($this->arg('remove')) { - $this->remove_address(); - } else if ($this->arg('removeincoming')) { - $this->remove_incoming(); - } else if ($this->arg('newincoming')) { - $this->new_incoming(); - } else { - $this->show_form(_('Unexpected form submission.')); - } - } - - function save_preferences() { - - $emailnotifysub = $this->boolean('emailnotifysub'); - $emailnotifyfav = $this->boolean('emailnotifyfav'); - $emailnotifymsg = $this->boolean('emailnotifymsg'); - $emailnotifynudge = $this->boolean('emailnotifynudge'); - $emailmicroid = $this->boolean('emailmicroid'); - $emailpost = $this->boolean('emailpost'); - - $user = common_current_user(); - - assert(!is_null($user)); # should already be checked - - $user->query('BEGIN'); - - $original = clone($user); - - $user->emailnotifysub = $emailnotifysub; - $user->emailnotifyfav = $emailnotifyfav; - $user->emailnotifymsg = $emailnotifymsg; - $user->emailnotifynudge = $emailnotifynudge; - $user->emailmicroid = $emailmicroid; - $user->emailpost = $emailpost; - - $result = $user->update($original); - - if ($result === FALSE) { - common_log_db_error($user, 'UPDATE', __FILE__); - common_server_error(_('Couldn\'t update user.')); - return; - } - - $user->query('COMMIT'); - - $this->show_form(_('Preferences saved.'), true); - } - - function add_address() { - - $user = common_current_user(); - - $email = $this->trimmed('email'); - - # Some validation - - if (!$email) { - $this->show_form(_('No email address.')); - return; - } - - $email = common_canonical_email($email); - - if (!$email) { - $this->show_form(_('Cannot normalize that email address')); - return; - } - if (!Validate::email($email, true)) { - $this->show_form(_('Not a valid email address')); - return; - } else if ($user->email == $email) { - $this->show_form(_('That is already your email address.')); - return; - } else if ($this->email_exists($email)) { - $this->show_form(_('That email address already belongs to another user.')); - return; - } - - $confirm = new Confirm_address(); - $confirm->address = $email; - $confirm->address_type = 'email'; - $confirm->user_id = $user->id; - $confirm->code = common_confirmation_code(64); - - $result = $confirm->insert(); - - if ($result === FALSE) { - common_log_db_error($confirm, 'INSERT', __FILE__); - common_server_error(_('Couldn\'t insert confirmation code.')); - return; - } - - mail_confirm_address($user, $confirm->code, $user->nickname, $email); - - $msg = _('A confirmation code was sent to the email address you added. Check your inbox (and spam box!) for the code and instructions on how to use it.'); - - $this->show_form($msg, TRUE); - } - - function cancel_confirmation() { - $email = $this->arg('email'); - $confirm = $this->get_confirmation(); - if (!$confirm) { - $this->show_form(_('No pending confirmation to cancel.')); - return; - } - if ($confirm->address != $email) { - $this->show_form(_('That is the wrong IM address.')); - return; - } +class EmailsettingsAction extends SettingsAction +{ + + function get_instructions() + { + return _('Manage how you get email from %%site.name%%.'); + } + + function show_form($msg=null, $success=false) + { + $user = common_current_user(); + $this->form_header(_('Email Settings'), $msg, $success); + common_element_start('form', array('method' => 'post', + 'id' => 'emailsettings', + 'action' => + common_local_url('emailsettings'))); + common_hidden('token', common_session_token()); + + common_element('h2', null, _('Address')); + + if ($user->email) { + common_element_start('p'); + common_element('span', 'address confirmed', $user->email); + common_element('span', 'input_instructions', + _('Current confirmed email address.')); + common_hidden('email', $user->email); + common_element_end('p'); + common_submit('remove', _('Remove')); + } else { + $confirm = $this->get_confirmation(); + if ($confirm) { + common_element_start('p'); + common_element('span', 'address unconfirmed', $confirm->address); + common_element('span', 'input_instructions', + _('Awaiting confirmation on this address. Check your inbox (and spam box!) for a message with further instructions.')); + common_hidden('email', $confirm->address); + common_element_end('p'); + common_submit('cancel', _('Cancel')); + } else { + common_input('email', _('Email Address'), + ($this->arg('email')) ? $this->arg('email') : null, + _('Email address, like "UserName@example.org"')); + common_submit('add', _('Add')); + } + } + + if ($user->email) { + common_element('h2', null, _('Incoming email')); + + if ($user->incomingemail) { + common_element_start('p'); + common_element('span', 'address', $user->incomingemail); + common_element('span', 'input_instructions', + _('Send email to this address to post new notices.')); + common_element_end('p'); + common_submit('removeincoming', _('Remove')); + } + + common_element_start('p'); + common_element('span', 'input_instructions', + _('Make a new email address for posting to; cancels the old one.')); + common_element_end('p'); + common_submit('newincoming', _('New')); + } + + common_element('h2', null, _('Preferences')); + + common_checkbox('emailnotifysub', + _('Send me notices of new subscriptions through email.'), + $user->emailnotifysub); + common_checkbox('emailnotifyfav', + _('Send me email when someone adds my notice as a favorite.'), + $user->emailnotifyfav); + common_checkbox('emailnotifymsg', + _('Send me email when someone sends me a private message.'), + $user->emailnotifymsg); + common_checkbox('emailnotifynudge', + _('Allow friends to nudge me and send me an email.'), + $user->emailnotifynudge); + common_checkbox('emailpost', + _('I want to post notices by email.'), + $user->emailpost); + common_checkbox('emailmicroid', + _('Publish a MicroID for my email address.'), + $user->emailmicroid); + + common_submit('save', _('Save')); + + common_element_end('form'); + common_show_footer(); + } + + function get_confirmation() + { + $user = common_current_user(); + $confirm = new Confirm_address(); + $confirm->user_id = $user->id; + $confirm->address_type = 'email'; + if ($confirm->find(true)) { + return $confirm; + } else { + return null; + } + } + + function handle_post() + { + + # CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->show_form(_('There was a problem with your session token. Try again, please.')); + return; + } + + if ($this->arg('save')) { + $this->save_preferences(); + } else if ($this->arg('add')) { + $this->add_address(); + } else if ($this->arg('cancel')) { + $this->cancel_confirmation(); + } else if ($this->arg('remove')) { + $this->remove_address(); + } else if ($this->arg('removeincoming')) { + $this->remove_incoming(); + } else if ($this->arg('newincoming')) { + $this->new_incoming(); + } else { + $this->show_form(_('Unexpected form submission.')); + } + } + + function save_preferences() + { + + $emailnotifysub = $this->boolean('emailnotifysub'); + $emailnotifyfav = $this->boolean('emailnotifyfav'); + $emailnotifymsg = $this->boolean('emailnotifymsg'); + $emailnotifynudge = $this->boolean('emailnotifynudge'); + $emailmicroid = $this->boolean('emailmicroid'); + $emailpost = $this->boolean('emailpost'); + + $user = common_current_user(); + + assert(!is_null($user)); # should already be checked + + $user->query('BEGIN'); + + $original = clone($user); + + $user->emailnotifysub = $emailnotifysub; + $user->emailnotifyfav = $emailnotifyfav; + $user->emailnotifymsg = $emailnotifymsg; + $user->emailnotifynudge = $emailnotifynudge; + $user->emailmicroid = $emailmicroid; + $user->emailpost = $emailpost; + + $result = $user->update($original); + + if ($result === false) { + common_log_db_error($user, 'UPDATE', __FILE__); + common_server_error(_('Couldn\'t update user.')); + return; + } + + $user->query('COMMIT'); + + $this->show_form(_('Preferences saved.'), true); + } + + function add_address() + { + + $user = common_current_user(); + + $email = $this->trimmed('email'); + + # Some validation + + if (!$email) { + $this->show_form(_('No email address.')); + return; + } + + $email = common_canonical_email($email); + + if (!$email) { + $this->show_form(_('Cannot normalize that email address')); + return; + } + if (!Validate::email($email, true)) { + $this->show_form(_('Not a valid email address')); + return; + } else if ($user->email == $email) { + $this->show_form(_('That is already your email address.')); + return; + } else if ($this->email_exists($email)) { + $this->show_form(_('That email address already belongs to another user.')); + return; + } + + $confirm = new Confirm_address(); + $confirm->address = $email; + $confirm->address_type = 'email'; + $confirm->user_id = $user->id; + $confirm->code = common_confirmation_code(64); + + $result = $confirm->insert(); + + if ($result === false) { + common_log_db_error($confirm, 'INSERT', __FILE__); + common_server_error(_('Couldn\'t insert confirmation code.')); + return; + } + + mail_confirm_address($user, $confirm->code, $user->nickname, $email); + + $msg = _('A confirmation code was sent to the email address you added. Check your inbox (and spam box!) for the code and instructions on how to use it.'); + + $this->show_form($msg, true); + } + + function cancel_confirmation() + { + $email = $this->arg('email'); + $confirm = $this->get_confirmation(); + if (!$confirm) { + $this->show_form(_('No pending confirmation to cancel.')); + return; + } + if ($confirm->address != $email) { + $this->show_form(_('That is the wrong IM address.')); + return; + } $result = $confirm->delete(); if (!$result) { - common_log_db_error($confirm, 'DELETE', __FILE__); + common_log_db_error($confirm, 'DELETE', __FILE__); $this->server_error(_('Couldn\'t delete email confirmation.')); return; } - $this->show_form(_('Confirmation cancelled.'), TRUE); - } - - function remove_address() { - - $user = common_current_user(); - $email = $this->arg('email'); - - # Maybe an old tab open...? - - if ($user->email != $email) { - $this->show_form(_('That is not your email address.')); - return; - } - - $user->query('BEGIN'); - $original = clone($user); - $user->email = NULL; - $result = $user->updateKeys($original); - if (!$result) { - common_log_db_error($user, 'UPDATE', __FILE__); - common_server_error(_('Couldn\'t update user.')); - return; - } - $user->query('COMMIT'); - - $this->show_form(_('The address was removed.'), TRUE); - } - - function remove_incoming() { - $user = common_current_user(); - - if (!$user->incomingemail) { - $this->show_form(_('No incoming email address.')); - return; - } - - $orig = clone($user); - $user->incomingemail = NULL; - - if (!$user->updateKeys($orig)) { - common_log_db_error($user, 'UPDATE', __FILE__); - $this->server_error(_("Couldn't update user record.")); - } - - $this->show_form(_('Incoming email address removed.'), TRUE); - } - - function new_incoming() { - $user = common_current_user(); - - $orig = clone($user); - $user->incomingemail = mail_new_incoming_address(); - - if (!$user->updateKeys($orig)) { - common_log_db_error($user, 'UPDATE', __FILE__); - $this->server_error(_("Couldn't update user record.")); - } - - $this->show_form(_('New incoming email address added.'), TRUE); - } - - function email_exists($email) { - $user = common_current_user(); - $other = User::staticGet('email', $email); - if (!$other) { - return false; - } else { - return $other->id != $user->id; - } - } + $this->show_form(_('Confirmation cancelled.'), true); + } + + function remove_address() + { + + $user = common_current_user(); + $email = $this->arg('email'); + + # Maybe an old tab open...? + + if ($user->email != $email) { + $this->show_form(_('That is not your email address.')); + return; + } + + $user->query('BEGIN'); + $original = clone($user); + $user->email = null; + $result = $user->updateKeys($original); + if (!$result) { + common_log_db_error($user, 'UPDATE', __FILE__); + common_server_error(_('Couldn\'t update user.')); + return; + } + $user->query('COMMIT'); + + $this->show_form(_('The address was removed.'), true); + } + + function remove_incoming() + { + $user = common_current_user(); + + if (!$user->incomingemail) { + $this->show_form(_('No incoming email address.')); + return; + } + + $orig = clone($user); + $user->incomingemail = null; + + if (!$user->updateKeys($orig)) { + common_log_db_error($user, 'UPDATE', __FILE__); + $this->server_error(_("Couldn't update user record.")); + } + + $this->show_form(_('Incoming email address removed.'), true); + } + + function new_incoming() + { + $user = common_current_user(); + + $orig = clone($user); + $user->incomingemail = mail_new_incoming_address(); + + if (!$user->updateKeys($orig)) { + common_log_db_error($user, 'UPDATE', __FILE__); + $this->server_error(_("Couldn't update user record.")); + } + + $this->show_form(_('New incoming email address added.'), true); + } + + function email_exists($email) + { + $user = common_current_user(); + $other = User::staticGet('email', $email); + if (!$other) { + return false; + } else { + return $other->id != $user->id; + } + } } diff --git a/actions/facebookhome.php b/actions/facebookhome.php index 8ee2d4cd3..6206fb6c2 100644 --- a/actions/facebookhome.php +++ b/actions/facebookhome.php @@ -10,123 +10,127 @@ * * 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 + * 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/>. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/facebookaction.php'); -class FacebookhomeAction extends FacebookAction { +class FacebookhomeAction extends FacebookAction +{ - function handle($args) { - parent::handle($args); + function handle($args) + { + parent::handle($args); - $this->login(); - } + $this->login(); + } - function login() { + function login() + { - $user = null; + $user = null; - $facebook = $this->get_facebook(); - $fbuid = $facebook->require_login(); + $facebook = get_facebook(); + $fbuid = $facebook->require_login(); - # check to see whether there's already a Facebook link for this user - $flink = Foreign_link::getByForeignID($fbuid, 2); // 2 == Facebook + # check to see whether there's already a Facebook link for this user + $flink = Foreign_link::getByForeignID($fbuid, 2); // 2 == Facebook - if ($flink) { + if ($flink) { - $user = $flink->getUser(); - $this->show_home($facebook, $fbuid, $user); + $user = $flink->getUser(); + $this->show_home($facebook, $fbuid, $user); - } else { + } else { - # Make the user put in her Laconica creds - $nickname = common_canonical_nickname($this->trimmed('nickname')); - $password = $this->arg('password'); + # Make the user put in her Laconica creds + $nickname = common_canonical_nickname($this->trimmed('nickname')); + $password = $this->arg('password'); - if ($nickname) { + if ($nickname) { - if (common_check_user($nickname, $password)) { + if (common_check_user($nickname, $password)) { - $user = User::staticGet('nickname', $nickname); + $user = User::staticGet('nickname', $nickname); - if (!$user) { - echo '<fb:error message="Coudln\'t get user!" />'; - $this->show_login_form(); - } + if (!$user) { + echo '<fb:error message="Coudln\'t get user!" />'; + $this->show_login_form(); + } - $flink = DB_DataObject::factory('foreign_link'); - $flink->user_id = $user->id; - $flink->foreign_id = $fbuid; - $flink->service = 2; # Facebook - $flink->created = common_sql_now(); + $flink = DB_DataObject::factory('foreign_link'); + $flink->user_id = $user->id; + $flink->foreign_id = $fbuid; + $flink->service = 2; # Facebook + $flink->created = common_sql_now(); + $flink->set_flags(true, false, false); - # $this->set_flags($flink, $noticesync, $replysync, $friendsync); + $flink_id = $flink->insert(); - $flink_id = $flink->insert(); + if ($flink_id) { + echo '<fb:success message="You can now use Identi.ca from Facebook!" />'; + } - if ($flink_id) { - echo '<fb:success message="You can now use the Identi.ca from Facebook!" />'; - } + $this->show_home($facebook, $fbuid, $user); - $this->show_home($facebook, $fbuid, $user); + return; + } else { + echo '<fb:error message="Incorrect username or password." />'; + } + } - return; - } else { - echo '<fb:error message="Incorrect username or password." />'; - } - } + $this->show_login_form(); + } - $this->show_login_form(); - } + } - } + function show_home($facebook, $fbuid, $user) + { - function show_home($facebook, $fbuid, $user) { + $this->show_header('Home'); - $this->show_header('Home'); + echo $this->show_notices($user); + $this->update_profile_box($facebook, $fbuid, $user); - echo $this->show_notices($user); - $this->update_profile_box($facebook, $fbuid, $user); + $this->show_footer(); + } - $this->show_footer(); - } + function show_notices($user) + { - function show_notices($user) { + $page = $this->trimmed('page'); + if (!$page) { + $page = 1; + } - $page = $this->trimmed('page'); - if (!$page) { - $page = 1; - } + $notice = $user->noticesWithFriends(($page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1); - $notice = $user->noticesWithFriends(($page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1); + echo '<ul id="notices">'; - echo '<ul id="notices">'; + $cnt = 0; - $cnt = 0; + while ($notice->fetch() && $cnt <= NOTICES_PER_PAGE) { + $cnt++; - while ($notice->fetch() && $cnt <= NOTICES_PER_PAGE) { - $cnt++; + if ($cnt > NOTICES_PER_PAGE) { + break; + } - if ($cnt > NOTICES_PER_PAGE) { - break; - } + echo $this->render_notice($notice); + } - echo $this->render_notice($notice); - } + echo '<ul>'; - echo '<ul>'; + $this->pagination($page > 1, $cnt > NOTICES_PER_PAGE, + $page, 'index.php', array('nickname' => $user->nickname)); - $this->pagination($page > 1, $cnt > NOTICES_PER_PAGE, - $page, 'index.php', array('nickname' => $user->nickname)); - - } + } } diff --git a/actions/facebookinvite.php b/actions/facebookinvite.php index 68b351fb9..00efa654b 100644 --- a/actions/facebookinvite.php +++ b/actions/facebookinvite.php @@ -10,37 +10,40 @@ * * 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 + * 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/>. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/facebookaction.php'); -class FacebookinviteAction extends FacebookAction { +class FacebookinviteAction extends FacebookAction +{ - function handle($args) { - parent::handle($args); + function handle($args) + { + parent::handle($args); - $this->display(); - } + $this->display(); + } - function display() { + function display() + { - $facebook = $this->get_facebook(); + $facebook = get_facebook(); - $fbuid = $facebook->require_login(); + $fbuid = $facebook->require_login(); - $this->show_header('Invite'); + $this->show_header('Invite'); - echo '<h2>Coming soon...</h2>'; + echo '<h2>Coming soon...</h2>'; - $this->show_footer(); + $this->show_footer(); - } + } } diff --git a/actions/facebookremove.php b/actions/facebookremove.php index 2a7bdd03e..a200fefbf 100644 --- a/actions/facebookremove.php +++ b/actions/facebookremove.php @@ -10,56 +10,58 @@ * * 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 + * 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/>. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/facebookaction.php'); -class FacebookremoveAction extends FacebookAction { +class FacebookremoveAction extends FacebookAction +{ - function handle($args) { - parent::handle($args); + function handle($args) + { + parent::handle($args); - $secret = common_config('facebook', 'secret'); + $secret = common_config('facebook', 'secret'); - $sig = ''; + $sig = ''; - ksort($_POST); + ksort($_POST); - foreach ($_POST as $key => $val) { - if (substr($key, 0, 7) == 'fb_sig_') { - $sig .= substr($key, 7) . '=' . $val; - } - } + foreach ($_POST as $key => $val) { + if (substr($key, 0, 7) == 'fb_sig_') { + $sig .= substr($key, 7) . '=' . $val; + } + } - $sig .= $secret; - $verify = md5($sig); + $sig .= $secret; + $verify = md5($sig); - if ($verify == $this->arg('fb_sig')) { + if ($verify == $this->arg('fb_sig')) { - $flink = Foreign_link::getByForeignID($this->arg('fb_sig_user'), 2); + $flink = Foreign_link::getByForeignID($this->arg('fb_sig_user'), 2); - common_debug("Removing foreign link to Facebook - local user ID: $flink->user_id, Facebook ID: $flink->foreign_id"); + common_debug("Removing foreign link to Facebook - local user ID: $flink->user_id, Facebook ID: $flink->foreign_id"); - $result = $flink->delete(); + $result = $flink->delete(); - if (!$result) { - common_log_db_error($flink, 'DELETE', __FILE__); - common_server_error(_('Couldn\'t remove Facebook user.')); - return; - } + if (!$result) { + common_log_db_error($flink, 'DELETE', __FILE__); + common_server_error(_('Couldn\'t remove Facebook user.')); + return; + } - } else { - # Someone bad tried to remove facebook link? - common_log(LOG_ERR, "Someone from $_SERVER[REMOTE_ADDR] " . - 'unsuccessfully tried to remove a foreign link to Facebook!'); - } - } + } else { + # Someone bad tried to remove facebook link? + common_log(LOG_ERR, "Someone from $_SERVER[REMOTE_ADDR] " . + 'unsuccessfully tried to remove a foreign link to Facebook!'); + } + } } diff --git a/actions/facebooksettings.php b/actions/facebooksettings.php index 4d7000d60..d28b1aa5c 100644 --- a/actions/facebooksettings.php +++ b/actions/facebooksettings.php @@ -10,43 +10,104 @@ * * 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 + * 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/>. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/facebookaction.php'); -class FacebooksettingsAction extends FacebookAction { +class FacebooksettingsAction extends FacebookAction +{ - function handle($args) { - parent::handle($args); + function handle($args) + { + parent::handle($args); - $this->display(); - } + if ($this->arg('save')) { + $this->save_settings(); + } else { + $this->show_form(); + } + } - function display() { - $facebook = $this->get_facebook(); + function save_settings() { - $fbuid = $facebook->require_login(); + $noticesync = $this->arg('noticesync'); + $replysync = $this->arg('replysync'); - $fbml = '<fb:if-section-not-added section="profile">' - .'<h2>Add an Identi.ca box to your profile!</h2>' - .'<fb:add-section-button section="profile"/>' - .'</fb:if-section-not-added>'; + $facebook = get_facebook(); + $fbuid = $facebook->require_login(); + $flink = Foreign_link::getByForeignID($fbuid, 2); // 2 == Facebook - $this->show_header('Settings'); + $original = clone($flink); + $flink->set_flags($noticesync, $replysync, false); + $result = $flink->update($original); - echo $fbml; + if ($result) { + echo '<fb:success message="Sync preferences saved." />'; + } - $this->show_footer(); + $this->show_form(); - } + } + + function show_form() { + + $facebook = get_facebook(); + $fbuid = $facebook->require_login(); + + $flink = Foreign_link::getByForeignID($fbuid, 2); // 2 == Facebook + + $this->show_header('Settings'); + + $fbml = '<fb:if-section-not-added section="profile">' + .'<h2>Add an Identi.ca box to my profile</h2>' + .'<p><fb:add-section-button section="profile"/></p>' + .'</fb:if-section-not-added>'; + + $fbml .= '<p><fb:prompt-permission perms="status_update"><h2>Allow Identi.ca to update my Facebook status</h2></fb:prompt-permission></p>'; + + if ($facebook->api_client->users_hasAppPermission('status_update')) { + + $fbml .= '<form method="post" id="facebook_settings">' + .'<h2>Sync preferences</h2>' + .'<p>'; + + if ($flink->noticesync & FOREIGN_NOTICE_SEND) { + $fbml .= '<input name="noticesync" type="checkbox" class="checkbox" id="noticesync" checked="checked"/>'; + } else { + $fbml .= '<input name="noticesync" type="checkbox" class="checkbox" id="noticesync">'; + } + + $fbml .= '<label class="checkbox_label" for="noticesync">Automatically update my Facebook status with my notices.</label>' + .'</p>' + .'<p>'; + + if ($flink->noticesync & FOREIGN_NOTICE_SEND_REPLY) { + $fbml .= '<input name="replysync" type="checkbox" class="checkbox" id="replysync" checked="checked"/>'; + } else { + $fbml .= '<input name="replysync" type="checkbox" class="checkbox" id="replysync"/>'; + } + + $fbml .= '<label class="checkbox_label" for="replysync">Send "@" replies to Facebook.</label>' + .'</p>' + .'<p>' + .'<input type="submit" id="save" name="save" class="submit" value="Save"/>' + .'</p>' + .'</form>'; + + } + + echo $fbml; + + $this->show_footer(); + } } diff --git a/actions/favor.php b/actions/favor.php index aede32902..8103f8181 100644 --- a/actions/favor.php +++ b/actions/favor.php @@ -21,74 +21,77 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/mail.php'); -class FavorAction extends Action { - - function handle($args) { - parent::handle($args); - - if (!common_logged_in()) { - common_user_error(_('Not logged in.')); - return; - } - - $user = common_current_user(); - - if ($_SERVER['REQUEST_METHOD'] != 'POST') { - common_redirect(common_local_url('showfavorites', array('nickname' => $user->nickname))); - return; - } - - $id = $this->trimmed('notice'); - - $notice = Notice::staticGet($id); - - # CSRF protection - - $token = $this->trimmed('token-'.$notice->id); - if (!$token || $token != common_session_token()) { - $this->client_error(_("There was a problem with your session token. Try again, please.")); - return; - } - - if ($user->hasFave($notice)) { - $this->client_error(_('This notice is already a favorite!')); - return; - } - - $fave = Fave::addNew($user, $notice); - - if (!$fave) { - $this->server_error(_('Could not create favorite.')); - return; - } - - $this->notify($fave, $notice, $user); - $user->blowFavesCache(); - - if ($this->boolean('ajax')) { - common_start_html('text/xml;charset=utf-8', true); - common_element_start('head'); - common_element('title', null, _('Disfavor favorite')); - common_element_end('head'); - common_element_start('body'); - common_disfavor_form($notice); - common_element_end('body'); - common_element_end('html'); - } else { - common_redirect(common_local_url('showfavorites', - array('nickname' => $user->nickname))); - } - } - - function notify($fave, $notice, $user) { - $other = User::staticGet('id', $notice->profile_id); - if ($other && $other->id != $user->id) { - if ($other->email && $other->emailnotifyfav) { - mail_notify_fave($other, $user, $notice); - } - # XXX: notify by IM - # XXX: notify by SMS - } - } +class FavorAction extends Action +{ + + function handle($args) + { + parent::handle($args); + + if (!common_logged_in()) { + common_user_error(_('Not logged in.')); + return; + } + + $user = common_current_user(); + + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + common_redirect(common_local_url('showfavorites', array('nickname' => $user->nickname))); + return; + } + + $id = $this->trimmed('notice'); + + $notice = Notice::staticGet($id); + + # CSRF protection + + $token = $this->trimmed('token-'.$notice->id); + if (!$token || $token != common_session_token()) { + $this->client_error(_("There was a problem with your session token. Try again, please.")); + return; + } + + if ($user->hasFave($notice)) { + $this->client_error(_('This notice is already a favorite!')); + return; + } + + $fave = Fave::addNew($user, $notice); + + if (!$fave) { + $this->server_error(_('Could not create favorite.')); + return; + } + + $this->notify($fave, $notice, $user); + $user->blowFavesCache(); + + if ($this->boolean('ajax')) { + common_start_html('text/xml;charset=utf-8', true); + common_element_start('head'); + common_element('title', null, _('Disfavor favorite')); + common_element_end('head'); + common_element_start('body'); + common_disfavor_form($notice); + common_element_end('body'); + common_element_end('html'); + } else { + common_redirect(common_local_url('showfavorites', + array('nickname' => $user->nickname))); + } + } + + function notify($fave, $notice, $user) + { + $other = User::staticGet('id', $notice->profile_id); + if ($other && $other->id != $user->id) { + if ($other->email && $other->emailnotifyfav) { + mail_notify_fave($other, $user, $notice); + } + # XXX: notify by IM + # XXX: notify by SMS + } + } } diff --git a/actions/favorited.php b/actions/favorited.php index dc8070d06..71a9e026e 100644 --- a/actions/favorited.php +++ b/actions/favorited.php @@ -10,90 +10,96 @@ * * 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 + * 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/>. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/stream.php'); -class FavoritedAction extends StreamAction { +class FavoritedAction extends StreamAction +{ - function handle($args) { - parent::handle($args); + function handle($args) + { + parent::handle($args); - $page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; + $page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; - common_show_header(_('Popular notices'), - array($this, 'show_header'), NULL, - array($this, 'show_top')); + common_show_header(_('Popular notices'), + array($this, 'show_header'), null, + array($this, 'show_top')); - $this->show_notices($page); + $this->show_notices($page); - common_show_footer(); - } + common_show_footer(); + } - function show_top() { - $instr = $this->get_instructions(); - $output = common_markup_to_html($instr); - common_element_start('div', 'instructions'); - common_raw($output); - common_element_end('div'); - $this->public_views_menu(); - } + function show_top() + { + $instr = $this->get_instructions(); + $output = common_markup_to_html($instr); + common_element_start('div', 'instructions'); + common_raw($output); + common_element_end('div'); + $this->public_views_menu(); + } - function show_header() { + function show_header() + { return; - } + } - function get_instructions() { - return _('Showing recently popular notices'); - } + function get_instructions() + { + return _('Showing recently popular notices'); + } - function show_notices($page) { + function show_notices($page) + { - $qry = 'SELECT notice.*, sum(exp(-(now() - fave.modified) / %s)) as weight ' . - 'FROM notice JOIN fave ON notice.id = fave.notice_id ' . - 'GROUP BY fave.notice_id ' . - 'ORDER BY weight DESC'; + $qry = 'SELECT notice.*, sum(exp(-(now() - fave.modified) / %s)) as weight ' . + 'FROM notice JOIN fave ON notice.id = fave.notice_id ' . + 'GROUP BY fave.notice_id ' . + 'ORDER BY weight DESC'; - $offset = ($page - 1) * NOTICES_PER_PAGE; - $limit = NOTICES_PER_PAGE + 1; + $offset = ($page - 1) * NOTICES_PER_PAGE; + $limit = NOTICES_PER_PAGE + 1; - if (common_config('db','type') == 'pgsql') { - $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; - } else { - $qry .= ' LIMIT ' . $offset . ', ' . $limit; - } + if (common_config('db','type') == 'pgsql') { + $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $qry .= ' LIMIT ' . $offset . ', ' . $limit; + } - # Figure out how to cache this query + # Figure out how to cache this query - $notice = new Notice; - $notice->query(sprintf($qry, common_config('popular', 'dropoff'))); + $notice = new Notice; + $notice->query(sprintf($qry, common_config('popular', 'dropoff'))); - common_element_start('ul', array('id' => 'notices')); + common_element_start('ul', array('id' => 'notices')); - $cnt = 0; + $cnt = 0; - while ($notice->fetch() && $cnt <= NOTICES_PER_PAGE) { - $cnt++; + while ($notice->fetch() && $cnt <= NOTICES_PER_PAGE) { + $cnt++; - if ($cnt > NOTICES_PER_PAGE) { - break; - } + if ($cnt > NOTICES_PER_PAGE) { + break; + } $item = new NoticeListItem($notice); $item->show(); - } + } - common_element_end('ul'); + common_element_end('ul'); - common_pagination($page > 1, $cnt > NOTICES_PER_PAGE, - $page, 'favorited'); - } + common_pagination($page > 1, $cnt > NOTICES_PER_PAGE, + $page, 'favorited'); + } } diff --git a/actions/favoritesrss.php b/actions/favoritesrss.php index 25dd3861f..8c7ce52bf 100644 --- a/actions/favoritesrss.php +++ b/actions/favoritesrss.php @@ -23,51 +23,56 @@ require_once(INSTALLDIR.'/lib/rssaction.php'); // Formatting of RSS handled by Rss10Action -class FavoritesrssAction extends Rss10Action { +class FavoritesrssAction extends Rss10Action +{ - var $user = NULL; - - function init() { - $nickname = $this->trimmed('nickname'); - $this->user = User::staticGet('nickname', $nickname); + var $user = null; + + function init() + { + $nickname = $this->trimmed('nickname'); + $this->user = User::staticGet('nickname', $nickname); - if (!$this->user) { - common_user_error(_('No such user.')); - return false; - } else { - return true; - } - } + if (!$this->user) { + common_user_error(_('No such user.')); + return false; + } else { + return true; + } + } - function get_notices($limit=0) { + function get_notices($limit=0) + { - $user = $this->user; + $user = $this->user; - $notice = $user->favoriteNotices(0, $limit); + $notice = $user->favoriteNotices(0, $limit); - $notices = array(); + $notices = array(); - while ($notice->fetch()) { - $notices[] = clone($notice); - } + while ($notice->fetch()) { + $notices[] = clone($notice); + } - return $notices; - } + return $notices; + } - function get_channel() { - $user = $this->user; - $c = array('url' => common_local_url('favoritesrss', - array('nickname' => - $user->nickname)), - 'title' => sprintf(_("%s favorite notices"), $user->nickname), - 'link' => common_local_url('showfavorites', - array('nickname' => - $user->nickname)), - 'description' => sprintf(_('Feed of favorite notices of %s'), $user->nickname)); - return $c; - } + function get_channel() + { + $user = $this->user; + $c = array('url' => common_local_url('favoritesrss', + array('nickname' => + $user->nickname)), + 'title' => sprintf(_("%s favorite notices"), $user->nickname), + 'link' => common_local_url('showfavorites', + array('nickname' => + $user->nickname)), + 'description' => sprintf(_('Feed of favorite notices of %s'), $user->nickname)); + return $c; + } - function get_image() { - return NULL; - } + function get_image() + { + return null; + } }
\ No newline at end of file diff --git a/actions/featured.php b/actions/featured.php index 96fbd89ab..2bf8b0b81 100644 --- a/actions/featured.php +++ b/actions/featured.php @@ -10,11 +10,11 @@ * * 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 + * 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/>. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ if (!defined('LACONICA')) { exit(1); } @@ -22,81 +22,87 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/stream.php'); require_once(INSTALLDIR.'/lib/profilelist.php'); -class FeaturedAction extends StreamAction { +class FeaturedAction extends StreamAction +{ - function handle($args) { - parent::handle($args); + function handle($args) + { + parent::handle($args); - $page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; + $page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; - common_show_header(_('Featured users'), - array($this, 'show_header'), NULL, - array($this, 'show_top')); + common_show_header(_('Featured users'), + array($this, 'show_header'), null, + array($this, 'show_top')); - $this->show_notices($page); + $this->show_notices($page); - common_show_footer(); - } + common_show_footer(); + } - function show_top() { - $instr = $this->get_instructions(); - $output = common_markup_to_html($instr); - common_element_start('div', 'instructions'); - common_raw($output); - common_element_end('div'); - $this->public_views_menu(); - } + function show_top() + { + $instr = $this->get_instructions(); + $output = common_markup_to_html($instr); + common_element_start('div', 'instructions'); + common_raw($output); + common_element_end('div'); + $this->public_views_menu(); + } - function show_header() { - } + function show_header() + { + } - function get_instructions() { - return _('Featured users'); - } + function get_instructions() + { + return _('Featured users'); + } - function show_notices($page) { + function show_notices($page) + { - // XXX: Note I'm doing it this two-stage way because a raw query - // with a JOIN was *not* working. --Zach + // XXX: Note I'm doing it this two-stage way because a raw query + // with a JOIN was *not* working. --Zach - $featured_nicks = common_config('nickname', 'featured'); + $featured_nicks = common_config('nickname', 'featured'); - if (count($featured_nicks) > 0) { + if (count($featured_nicks) > 0) { - $quoted = array(); + $quoted = array(); - foreach ($featured_nicks as $nick) { - $quoted[] = "'$nick'"; - } + foreach ($featured_nicks as $nick) { + $quoted[] = "'$nick'"; + } - $user = new User; - $user->whereAdd(sprintf('nickname IN (%s)', implode(',', $quoted))); - $user->limit(($page - 1) * PROFILES_PER_PAGE, PROFILES_PER_PAGE + 1); - $user->orderBy('user.nickname ASC'); + $user = new User; + $user->whereAdd(sprintf('nickname IN (%s)', implode(',', $quoted))); + $user->limit(($page - 1) * PROFILES_PER_PAGE, PROFILES_PER_PAGE + 1); + $user->orderBy('user.nickname ASC'); - $user->find(); + $user->find(); - $profile_ids = array(); + $profile_ids = array(); - while ($user->fetch()) { - $profile_ids[] = $user->id; - } + while ($user->fetch()) { + $profile_ids[] = $user->id; + } - $profile = new Profile; - $profile->whereAdd(sprintf('profile.id IN (%s)', implode(',', $profile_ids))); - $profile->orderBy('nickname ASC'); + $profile = new Profile; + $profile->whereAdd(sprintf('profile.id IN (%s)', implode(',', $profile_ids))); + $profile->orderBy('nickname ASC'); - $cnt = $profile->find(); + $cnt = $profile->find(); - if ($cnt > 0) { - $featured = new ProfileList($profile); - $featured->show_list(); - } + if ($cnt > 0) { + $featured = new ProfileList($profile); + $featured->show_list(); + } - $profile->free(); + $profile->free(); - common_pagination($page > 1, $cnt > PROFILES_PER_PAGE, $page, 'featured'); - } - } + common_pagination($page > 1, $cnt > PROFILES_PER_PAGE, $page, 'featured'); + } + } }
\ No newline at end of file diff --git a/actions/finishaddopenid.php b/actions/finishaddopenid.php index 54d81b0b4..0ce1680aa 100644 --- a/actions/finishaddopenid.php +++ b/actions/finishaddopenid.php @@ -21,83 +21,87 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/openid.php'); -class FinishaddopenidAction extends Action { - - function handle($args) { - parent::handle($args); - if (!common_logged_in()) { - common_user_error(_('Not logged in.')); - } else { - $this->try_login(); - } - } - - function try_login() { - - $consumer =& oid_consumer(); - - $response = $consumer->complete(common_local_url('finishaddopenid')); - - if ($response->status == Auth_OpenID_CANCEL) { - $this->message(_('OpenID authentication cancelled.')); - return; - } else if ($response->status == Auth_OpenID_FAILURE) { - // Authentication failed; display the error message. - $this->message(sprintf(_('OpenID authentication failed: %s'), $response->message)); - } else if ($response->status == Auth_OpenID_SUCCESS) { - - $display = $response->getDisplayIdentifier(); - $canonical = ($response->endpoint && $response->endpoint->canonicalID) ? - $response->endpoint->canonicalID : $display; - - $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response); - - if ($sreg_resp) { - $sreg = $sreg_resp->contents(); - } - - $cur =& common_current_user(); - $other = oid_get_user($canonical); - - if ($other) { - if ($other->id == $cur->id) { - $this->message(_('You already have this OpenID!')); - } else { - $this->message(_('Someone else already has this OpenID.')); - } - return; - } - - # start a transaction - - $cur->query('BEGIN'); - - $result = oid_link_user($cur->id, $canonical, $display); - - if (!$result) { - $this->message(_('Error connecting user.')); - return; - } - if ($sreg) { - if (!oid_update_user($cur, $sreg)) { - $this->message(_('Error updating profile')); - return; - } - } - - # success! - - $cur->query('COMMIT'); - - oid_set_last($display); - - common_redirect(common_local_url('openidsettings')); - } - } - - function message($msg) { - common_show_header(_('OpenID Login')); - common_element('p', NULL, $msg); - common_show_footer(); - } +class FinishaddopenidAction extends Action +{ + + function handle($args) + { + parent::handle($args); + if (!common_logged_in()) { + common_user_error(_('Not logged in.')); + } else { + $this->try_login(); + } + } + + function try_login() + { + + $consumer =& oid_consumer(); + + $response = $consumer->complete(common_local_url('finishaddopenid')); + + if ($response->status == Auth_OpenID_CANCEL) { + $this->message(_('OpenID authentication cancelled.')); + return; + } else if ($response->status == Auth_OpenID_FAILURE) { + // Authentication failed; display the error message. + $this->message(sprintf(_('OpenID authentication failed: %s'), $response->message)); + } else if ($response->status == Auth_OpenID_SUCCESS) { + + $display = $response->getDisplayIdentifier(); + $canonical = ($response->endpoint && $response->endpoint->canonicalID) ? + $response->endpoint->canonicalID : $display; + + $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response); + + if ($sreg_resp) { + $sreg = $sreg_resp->contents(); + } + + $cur =& common_current_user(); + $other = oid_get_user($canonical); + + if ($other) { + if ($other->id == $cur->id) { + $this->message(_('You already have this OpenID!')); + } else { + $this->message(_('Someone else already has this OpenID.')); + } + return; + } + + # start a transaction + + $cur->query('BEGIN'); + + $result = oid_link_user($cur->id, $canonical, $display); + + if (!$result) { + $this->message(_('Error connecting user.')); + return; + } + if ($sreg) { + if (!oid_update_user($cur, $sreg)) { + $this->message(_('Error updating profile')); + return; + } + } + + # success! + + $cur->query('COMMIT'); + + oid_set_last($display); + + common_redirect(common_local_url('openidsettings')); + } + } + + function message($msg) + { + common_show_header(_('OpenID Login')); + common_element('p', null, $msg); + common_show_footer(); + } } diff --git a/actions/finishimmediate.php b/actions/finishimmediate.php index 6dbaa3d1c..0964c39f4 100644 --- a/actions/finishimmediate.php +++ b/actions/finishimmediate.php @@ -21,45 +21,48 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/openid.php'); -class FinishimmediateAction extends Action { +class FinishimmediateAction extends Action +{ - function handle($args) { - parent::handle($args); + function handle($args) + { + parent::handle($args); - $consumer = oid_consumer(); + $consumer = oid_consumer(); - $response = $consumer->complete(common_local_url('finishimmediate')); + $response = $consumer->complete(common_local_url('finishimmediate')); - if ($response->status == Auth_OpenID_SUCCESS) { - $display = $response->getDisplayIdentifier(); - $canonical = ($response->endpoint->canonicalID) ? - $response->endpoint->canonicalID : $response->getDisplayIdentifier(); + if ($response->status == Auth_OpenID_SUCCESS) { + $display = $response->getDisplayIdentifier(); + $canonical = ($response->endpoint->canonicalID) ? + $response->endpoint->canonicalID : $response->getDisplayIdentifier(); - $user = oid_get_user($canonical); + $user = oid_get_user($canonical); - if ($user) { - oid_update_user($user, $sreg); - oid_set_last($display); # refresh for another year - common_set_user($user->nickname); - $this->go_backto(); - return; - } - } + if ($user) { + oid_update_user($user, $sreg); + oid_set_last($display); # refresh for another year + common_set_user($user->nickname); + $this->go_backto(); + return; + } + } - # Failure! Clear openid so we don't try it again + # Failure! Clear openid so we don't try it again - oid_clear_last(); - $this->go_backto(); - return; - } + oid_clear_last(); + $this->go_backto(); + return; + } - function go_backto() { - common_ensure_session(); - $backto = $_SESSION['openid_immediate_backto']; - if (!$backto) { - # gar. Well, push them to the public page - $backto = common_local_url('public'); - } - common_redirect($backto); - } + function go_backto() + { + common_ensure_session(); + $backto = $_SESSION['openid_immediate_backto']; + if (!$backto) { + # gar. Well, push them to the public page + $backto = common_local_url('public'); + } + common_redirect($backto); + } } diff --git a/actions/finishopenidlogin.php b/actions/finishopenidlogin.php index 766a08b20..bdb8516a3 100644 --- a/actions/finishopenidlogin.php +++ b/actions/finishopenidlogin.php @@ -21,416 +21,434 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/openid.php'); -class FinishopenidloginAction extends Action { - - function handle($args) { - parent::handle($args); - if (common_logged_in()) { - common_user_error(_('Already logged in.')); - } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->show_form(_('There was a problem with your session token. Try again, please.')); - return; - } - if ($this->arg('create')) { - if (!$this->boolean('license')) { - $this->show_form(_('You can\'t register if you don\'t agree to the license.'), - $this->trimmed('newname')); - return; - } - $this->create_new_user(); - } else if ($this->arg('connect')) { - $this->connect_user(); - } else { - common_debug(print_r($this->args, true), __FILE__); - $this->show_form(_('Something weird happened.'), - $this->trimmed('newname')); - } - } else { - $this->try_login(); - } - } - - function show_top($error=NULL) { - if ($error) { - common_element('div', array('class' => 'error'), $error); - } else { - global $config; - common_element('div', 'instructions', - sprintf(_('This is the first time you\'ve logged into %s so we must connect your OpenID to a local account. You can either create a new account, or connect with your existing account, if you have one.'), $config['site']['name'])); - } - } - - function show_form($error=NULL, $username=NULL) { - common_show_header(_('OpenID Account Setup'), NULL, $error, - array($this, 'show_top')); - - common_element_start('form', array('method' => 'post', - 'id' => 'account_connect', - 'action' => common_local_url('finishopenidlogin'))); - common_hidden('token', common_session_token()); - common_element('h2', NULL, - _('Create new account')); - common_element('p', NULL, - _('Create a new user with this nickname.')); - common_input('newname', _('New nickname'), - ($username) ? $username : '', - _('1-64 lowercase letters or numbers, no punctuation or spaces')); - common_element_start('p'); - common_element('input', array('type' => 'checkbox', - 'id' => 'license', - 'name' => 'license', - 'value' => 'true')); - common_text(_('My text and files are available under ')); - common_element('a', array(href => common_config('license', 'url')), - common_config('license', 'title')); - common_text(_(' except this private data: password, email address, IM address, phone number.')); - common_element_end('p'); - common_submit('create', _('Create')); - common_element('h2', NULL, - _('Connect existing account')); - common_element('p', NULL, - _('If you already have an account, login with your username and password to connect it to your OpenID.')); - common_input('nickname', _('Existing nickname')); - common_password('password', _('Password')); - common_submit('connect', _('Connect')); - common_element_end('form'); - common_show_footer(); - } - - function try_login() { - - $consumer = oid_consumer(); - - $response = $consumer->complete(common_local_url('finishopenidlogin')); - - if ($response->status == Auth_OpenID_CANCEL) { - $this->message(_('OpenID authentication cancelled.')); - return; - } else if ($response->status == Auth_OpenID_FAILURE) { - // Authentication failed; display the error message. - $this->message(sprintf(_('OpenID authentication failed: %s'), $response->message)); - } else if ($response->status == Auth_OpenID_SUCCESS) { - // This means the authentication succeeded; extract the - // identity URL and Simple Registration data (if it was - // returned). - $display = $response->getDisplayIdentifier(); - $canonical = ($response->endpoint->canonicalID) ? - $response->endpoint->canonicalID : $response->getDisplayIdentifier(); - - $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response); - - if ($sreg_resp) { - $sreg = $sreg_resp->contents(); - } - - $user = oid_get_user($canonical); - - if ($user) { - oid_set_last($display); - # XXX: commented out at @edd's request until better - # control over how data flows from OpenID provider. - # oid_update_user($user, $sreg); - common_set_user($user); - common_real_login(true); - if (isset($_SESSION['openid_rememberme']) && $_SESSION['openid_rememberme']) { - common_rememberme($user); - } +class FinishopenidloginAction extends Action +{ + + function handle($args) + { + parent::handle($args); + if (common_logged_in()) { + common_user_error(_('Already logged in.')); + } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->show_form(_('There was a problem with your session token. Try again, please.')); + return; + } + if ($this->arg('create')) { + if (!$this->boolean('license')) { + $this->show_form(_('You can\'t register if you don\'t agree to the license.'), + $this->trimmed('newname')); + return; + } + $this->create_new_user(); + } else if ($this->arg('connect')) { + $this->connect_user(); + } else { + common_debug(print_r($this->args, true), __FILE__); + $this->show_form(_('Something weird happened.'), + $this->trimmed('newname')); + } + } else { + $this->try_login(); + } + } + + function show_top($error=null) + { + if ($error) { + common_element('div', array('class' => 'error'), $error); + } else { + global $config; + common_element('div', 'instructions', + sprintf(_('This is the first time you\'ve logged into %s so we must connect your OpenID to a local account. You can either create a new account, or connect with your existing account, if you have one.'), $config['site']['name'])); + } + } + + function show_form($error=null, $username=null) + { + common_show_header(_('OpenID Account Setup'), null, $error, + array($this, 'show_top')); + + common_element_start('form', array('method' => 'post', + 'id' => 'account_connect', + 'action' => common_local_url('finishopenidlogin'))); + common_hidden('token', common_session_token()); + common_element('h2', null, + _('Create new account')); + common_element('p', null, + _('Create a new user with this nickname.')); + common_input('newname', _('New nickname'), + ($username) ? $username : '', + _('1-64 lowercase letters or numbers, no punctuation or spaces')); + common_element_start('p'); + common_element('input', array('type' => 'checkbox', + 'id' => 'license', + 'name' => 'license', + 'value' => 'true')); + common_text(_('My text and files are available under ')); + common_element('a', array(href => common_config('license', 'url')), + common_config('license', 'title')); + common_text(_(' except this private data: password, email address, IM address, phone number.')); + common_element_end('p'); + common_submit('create', _('Create')); + common_element('h2', null, + _('Connect existing account')); + common_element('p', null, + _('If you already have an account, login with your username and password to connect it to your OpenID.')); + common_input('nickname', _('Existing nickname')); + common_password('password', _('Password')); + common_submit('connect', _('Connect')); + common_element_end('form'); + common_show_footer(); + } + + function try_login() + { + + $consumer = oid_consumer(); + + $response = $consumer->complete(common_local_url('finishopenidlogin')); + + if ($response->status == Auth_OpenID_CANCEL) { + $this->message(_('OpenID authentication cancelled.')); + return; + } else if ($response->status == Auth_OpenID_FAILURE) { + // Authentication failed; display the error message. + $this->message(sprintf(_('OpenID authentication failed: %s'), $response->message)); + } else if ($response->status == Auth_OpenID_SUCCESS) { + // This means the authentication succeeded; extract the + // identity URL and Simple Registration data (if it was + // returned). + $display = $response->getDisplayIdentifier(); + $canonical = ($response->endpoint->canonicalID) ? + $response->endpoint->canonicalID : $response->getDisplayIdentifier(); + + $sreg_resp = Auth_OpenID_SRegResponse::fromSuccessResponse($response); + + if ($sreg_resp) { + $sreg = $sreg_resp->contents(); + } + + $user = oid_get_user($canonical); + + if ($user) { + oid_set_last($display); + # XXX: commented out at @edd's request until better + # control over how data flows from OpenID provider. + # oid_update_user($user, $sreg); + common_set_user($user); + common_real_login(true); + if (isset($_SESSION['openid_rememberme']) && $_SESSION['openid_rememberme']) { + common_rememberme($user); + } unset($_SESSION['openid_rememberme']); - $this->go_home($user->nickname); - } else { - $this->save_values($display, $canonical, $sreg); - $this->show_form(NULL, $this->best_new_nickname($display, $sreg)); - } - } - } - - function message($msg) { - common_show_header(_('OpenID Login')); - common_element('p', NULL, $msg); - common_show_footer(); - } - - function save_values($display, $canonical, $sreg) { - common_ensure_session(); - $_SESSION['openid_display'] = $display; - $_SESSION['openid_canonical'] = $canonical; - $_SESSION['openid_sreg'] = $sreg; - } - - function get_saved_values() { - return array($_SESSION['openid_display'], - $_SESSION['openid_canonical'], - $_SESSION['openid_sreg']); - } - - function create_new_user() { + $this->go_home($user->nickname); + } else { + $this->save_values($display, $canonical, $sreg); + $this->show_form(null, $this->best_new_nickname($display, $sreg)); + } + } + } + + function message($msg) + { + common_show_header(_('OpenID Login')); + common_element('p', null, $msg); + common_show_footer(); + } + + function save_values($display, $canonical, $sreg) + { + common_ensure_session(); + $_SESSION['openid_display'] = $display; + $_SESSION['openid_canonical'] = $canonical; + $_SESSION['openid_sreg'] = $sreg; + } + + function get_saved_values() + { + return array($_SESSION['openid_display'], + $_SESSION['openid_canonical'], + $_SESSION['openid_sreg']); + } + + function create_new_user() + { # FIXME: save invite code before redirect, and check here - if (common_config('site', 'closed') || common_config('site', 'inviteonly')) { - common_user_error(_('Registration not allowed.')); + if (common_config('site', 'closed') || common_config('site', 'inviteonly')) { + common_user_error(_('Registration not allowed.')); return; } - $nickname = $this->trimmed('newname'); + $nickname = $this->trimmed('newname'); - if (!Validate::string($nickname, array('min_length' => 1, - 'max_length' => 64, - 'format' => VALIDATE_NUM . VALIDATE_ALPHA_LOWER))) { - $this->show_form(_('Nickname must have only lowercase letters and numbers and no spaces.')); - return; - } + if (!Validate::string($nickname, array('min_length' => 1, + 'max_length' => 64, + 'format' => VALIDATE_NUM . VALIDATE_ALPHA_LOWER))) { + $this->show_form(_('Nickname must have only lowercase letters and numbers and no spaces.')); + return; + } - if (!User::allowed_nickname($nickname)) { - $this->show_form(_('Nickname not allowed.')); - return; - } + if (!User::allowed_nickname($nickname)) { + $this->show_form(_('Nickname not allowed.')); + return; + } - if (User::staticGet('nickname', $nickname)) { - $this->show_form(_('Nickname already in use. Try another one.')); - return; - } + if (User::staticGet('nickname', $nickname)) { + $this->show_form(_('Nickname already in use. Try another one.')); + return; + } - list($display, $canonical, $sreg) = $this->get_saved_values(); + list($display, $canonical, $sreg) = $this->get_saved_values(); - if (!$display || !$canonical) { - common_server_error(_('Stored OpenID not found.')); - return; - } + if (!$display || !$canonical) { + common_server_error(_('Stored OpenID not found.')); + return; + } - # Possible race condition... let's be paranoid + # Possible race condition... let's be paranoid - $other = oid_get_user($canonical); + $other = oid_get_user($canonical); - if ($other) { - common_server_error(_('Creating new account for OpenID that already has a user.')); - return; - } + if ($other) { + common_server_error(_('Creating new account for OpenID that already has a user.')); + return; + } - if ($sreg['country']) { - if ($sreg['postcode']) { - # XXX: use postcode to get city and region - # XXX: also, store postcode somewhere -- it's valuable! - $location = $sreg['postcode'] . ', ' . $sreg['country']; - } else { - $location = $sreg['country']; - } - } + if ($sreg['country']) { + if ($sreg['postcode']) { + # XXX: use postcode to get city and region + # XXX: also, store postcode somewhere -- it's valuable! + $location = $sreg['postcode'] . ', ' . $sreg['country']; + } else { + $location = $sreg['country']; + } + } - if ($sreg['fullname'] && strlen($sreg['fullname']) <= 255) { - $fullname = $sreg['fullname']; - } + if ($sreg['fullname'] && strlen($sreg['fullname']) <= 255) { + $fullname = $sreg['fullname']; + } - if ($sreg['email'] && Validate::email($sreg['email'], true)) { - $email = $sreg['email']; - } + if ($sreg['email'] && Validate::email($sreg['email'], true)) { + $email = $sreg['email']; + } - # XXX: add language - # XXX: add timezone + # XXX: add language + # XXX: add timezone - $user = User::register(array('nickname' => $nickname, - 'email' => $email, - 'fullname' => $fullname, - 'location' => $location)); + $user = User::register(array('nickname' => $nickname, + 'email' => $email, + 'fullname' => $fullname, + 'location' => $location)); - $result = oid_link_user($user->id, $canonical, $display); + $result = oid_link_user($user->id, $canonical, $display); - oid_set_last($display); - common_set_user($user); - common_real_login(true); + oid_set_last($display); + common_set_user($user); + common_real_login(true); if (isset($_SESSION['openid_rememberme']) && $_SESSION['openid_rememberme']) { - common_rememberme($user); - } + common_rememberme($user); + } unset($_SESSION['openid_rememberme']); - common_redirect(common_local_url('showstream', array('nickname' => $user->nickname))); - } + common_redirect(common_local_url('showstream', array('nickname' => $user->nickname))); + } - function connect_user() { + function connect_user() + { - $nickname = $this->trimmed('nickname'); - $password = $this->trimmed('password'); + $nickname = $this->trimmed('nickname'); + $password = $this->trimmed('password'); - if (!common_check_user($nickname, $password)) { - $this->show_form(_('Invalid username or password.')); - return; - } + if (!common_check_user($nickname, $password)) { + $this->show_form(_('Invalid username or password.')); + return; + } - # They're legit! + # They're legit! - $user = User::staticGet('nickname', $nickname); + $user = User::staticGet('nickname', $nickname); - list($display, $canonical, $sreg) = $this->get_saved_values(); + list($display, $canonical, $sreg) = $this->get_saved_values(); - if (!$display || !$canonical) { - common_server_error(_('Stored OpenID not found.')); - return; - } + if (!$display || !$canonical) { + common_server_error(_('Stored OpenID not found.')); + return; + } - $result = oid_link_user($user->id, $canonical, $display); + $result = oid_link_user($user->id, $canonical, $display); - if (!$result) { - common_server_error(_('Error connecting user to OpenID.')); - return; - } + if (!$result) { + common_server_error(_('Error connecting user to OpenID.')); + return; + } - oid_update_user($user, $sreg); - oid_set_last($display); - common_set_user($user); - common_real_login(true); + oid_update_user($user, $sreg); + oid_set_last($display); + common_set_user($user); + common_real_login(true); if (isset($_SESSION['openid_rememberme']) && $_SESSION['openid_rememberme']) { - common_rememberme($user); - } - unset($_SESSION['openid_rememberme']); - $this->go_home($user->nickname); - } - - function go_home($nickname) { - $url = common_get_returnto(); - if ($url) { - # We don't have to return to it again - common_set_returnto(NULL); - } else { - $url = common_local_url('all', - array('nickname' => - $nickname)); - } - common_redirect($url); - } - - function best_new_nickname($display, $sreg) { - - # Try the passed-in nickname - - if ($sreg['nickname']) { - $nickname = $this->nicknamize($sreg['nickname']); - if ($this->is_new_nickname($nickname)) { - return $nickname; - } - } - - # Try the full name - - if ($sreg['fullname']) { - $fullname = $this->nicknamize($sreg['fullname']); - if ($this->is_new_nickname($fullname)) { - return $fullname; - } - } - - # Try the URL - - $from_url = $this->openid_to_nickname($display); - - if ($from_url && $this->is_new_nickname($from_url)) { - return $from_url; - } - - # XXX: others? - - return NULL; - } - - function is_new_nickname($str) { - if (!Validate::string($str, array('min_length' => 1, - 'max_length' => 64, - 'format' => VALIDATE_NUM . VALIDATE_ALPHA_LOWER))) { - return false; - } - if (!User::allowed_nickname($str)) { - return false; - } - if (User::staticGet('nickname', $str)) { - return false; - } - return true; - } - - function openid_to_nickname($openid) { + common_rememberme($user); + } + unset($_SESSION['openid_rememberme']); + $this->go_home($user->nickname); + } + + function go_home($nickname) + { + $url = common_get_returnto(); + if ($url) { + # We don't have to return to it again + common_set_returnto(null); + } else { + $url = common_local_url('all', + array('nickname' => + $nickname)); + } + common_redirect($url); + } + + function best_new_nickname($display, $sreg) + { + + # Try the passed-in nickname + + if ($sreg['nickname']) { + $nickname = $this->nicknamize($sreg['nickname']); + if ($this->is_new_nickname($nickname)) { + return $nickname; + } + } + + # Try the full name + + if ($sreg['fullname']) { + $fullname = $this->nicknamize($sreg['fullname']); + if ($this->is_new_nickname($fullname)) { + return $fullname; + } + } + + # Try the URL + + $from_url = $this->openid_to_nickname($display); + + if ($from_url && $this->is_new_nickname($from_url)) { + return $from_url; + } + + # XXX: others? + + return null; + } + + function is_new_nickname($str) + { + if (!Validate::string($str, array('min_length' => 1, + 'max_length' => 64, + 'format' => VALIDATE_NUM . VALIDATE_ALPHA_LOWER))) { + return false; + } + if (!User::allowed_nickname($str)) { + return false; + } + if (User::staticGet('nickname', $str)) { + return false; + } + return true; + } + + function openid_to_nickname($openid) + { if (Auth_Yadis_identifierScheme($openid) == 'XRI') { - return $this->xri_to_nickname($openid); - } else { - return $this->url_to_nickname($openid); - } - } - - # We try to use an OpenID URL as a legal Laconica user name in this order - # 1. Plain hostname, like http://evanp.myopenid.com/ - # 2. One element in path, like http://profile.typekey.com/EvanProdromou/ - # or http://getopenid.com/evanprodromou - - function url_to_nickname($openid) { - static $bad = array('query', 'user', 'password', 'port', 'fragment'); - - $parts = parse_url($openid); - - # If any of these parts exist, this won't work - - foreach ($bad as $badpart) { - if (array_key_exists($badpart, $parts)) { - return NULL; - } - } - - # We just have host and/or path - - # If it's just a host... - if (array_key_exists('host', $parts) && - (!array_key_exists('path', $parts) || strcmp($parts['path'], '/') == 0)) - { - $hostparts = explode('.', $parts['host']); - - # Try to catch common idiom of nickname.service.tld - - if ((count($hostparts) > 2) && - (strlen($hostparts[count($hostparts) - 2]) > 3) && # try to skip .co.uk, .com.au - (strcmp($hostparts[0], 'www') != 0)) - { - return $this->nicknamize($hostparts[0]); - } else { - # Do the whole hostname - return $this->nicknamize($parts['host']); - } - } else { - if (array_key_exists('path', $parts)) { - # Strip starting, ending slashes - $path = preg_replace('@/$@', '', $parts['path']); - $path = preg_replace('@^/@', '', $path); - if (strpos($path, '/') === false) { - return $this->nicknamize($path); - } - } - } - - return NULL; - } - - function xri_to_nickname($xri) { - $base = $this->xri_base($xri); - - if (!$base) { - return NULL; - } else { - # =evan.prodromou - # or @gratis*evan.prodromou - $parts = explode('*', substr($base, 1)); - return $this->nicknamize(array_pop($parts)); - } - } - - function xri_base($xri) { - if (substr($xri, 0, 6) == 'xri://') { - return substr($xri, 6); - } else { - return $xri; - } - } - - # Given a string, try to make it work as a nickname - - function nicknamize($str) { - $str = preg_replace('/\W/', '', $str); - return strtolower($str); - } + return $this->xri_to_nickname($openid); + } else { + return $this->url_to_nickname($openid); + } + } + + # We try to use an OpenID URL as a legal Laconica user name in this order + # 1. Plain hostname, like http://evanp.myopenid.com/ + # 2. One element in path, like http://profile.typekey.com/EvanProdromou/ + # or http://getopenid.com/evanprodromou + + function url_to_nickname($openid) + { + static $bad = array('query', 'user', 'password', 'port', 'fragment'); + + $parts = parse_url($openid); + + # If any of these parts exist, this won't work + + foreach ($bad as $badpart) { + if (array_key_exists($badpart, $parts)) { + return null; + } + } + + # We just have host and/or path + + # If it's just a host... + if (array_key_exists('host', $parts) && + (!array_key_exists('path', $parts) || strcmp($parts['path'], '/') == 0)) + { + $hostparts = explode('.', $parts['host']); + + # Try to catch common idiom of nickname.service.tld + + if ((count($hostparts) > 2) && + (strlen($hostparts[count($hostparts) - 2]) > 3) && # try to skip .co.uk, .com.au + (strcmp($hostparts[0], 'www') != 0)) + { + return $this->nicknamize($hostparts[0]); + } else { + # Do the whole hostname + return $this->nicknamize($parts['host']); + } + } else { + if (array_key_exists('path', $parts)) { + # Strip starting, ending slashes + $path = preg_replace('@/$@', '', $parts['path']); + $path = preg_replace('@^/@', '', $path); + if (strpos($path, '/') === false) { + return $this->nicknamize($path); + } + } + } + + return null; + } + + function xri_to_nickname($xri) + { + $base = $this->xri_base($xri); + + if (!$base) { + return null; + } else { + # =evan.prodromou + # or @gratis*evan.prodromou + $parts = explode('*', substr($base, 1)); + return $this->nicknamize(array_pop($parts)); + } + } + + function xri_base($xri) + { + if (substr($xri, 0, 6) == 'xri://') { + return substr($xri, 6); + } else { + return $xri; + } + } + + # Given a string, try to make it work as a nickname + + function nicknamize($str) + { + $str = preg_replace('/\W/', '', $str); + return strtolower($str); + } } diff --git a/actions/finishremotesubscribe.php b/actions/finishremotesubscribe.php index 58040683f..cee3a1818 100644 --- a/actions/finishremotesubscribe.php +++ b/actions/finishremotesubscribe.php @@ -21,176 +21,178 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/omb.php'); -class FinishremotesubscribeAction extends Action { +class FinishremotesubscribeAction extends Action +{ - function handle($args) { + function handle($args) + { - parent::handle($args); + parent::handle($args); - if (common_logged_in()) { - common_user_error(_('You can use the local subscription!')); - return; - } + if (common_logged_in()) { + common_user_error(_('You can use the local subscription!')); + return; + } + + $omb = $_SESSION['oauth_authorization_request']; + + if (!$omb) { + common_user_error(_('Not expecting this response!')); + return; + } + + common_debug('stored request: '.print_r($omb,true), __FILE__); + + common_remove_magic_from_request(); + $req = OAuthRequest::from_request(); - $omb = $_SESSION['oauth_authorization_request']; + $token = $req->get_parameter('oauth_token'); - if (!$omb) { - common_user_error(_('Not expecting this response!')); - return; - } + # I think this is the success metric - common_debug('stored request: '.print_r($omb,true), __FILE__); + if ($token != $omb['token']) { + common_user_error(_('Not authorized.')); + return; + } - common_remove_magic_from_request(); - $req = OAuthRequest::from_request(); + $version = $req->get_parameter('omb_version'); + + if ($version != OMB_VERSION_01) { + common_user_error(_('Unknown version of OMB protocol.')); + return; + } - $token = $req->get_parameter('oauth_token'); + $nickname = $req->get_parameter('omb_listener_nickname'); - # I think this is the success metric + if (!$nickname) { + common_user_error(_('No nickname provided by remote server.')); + return; + } - if ($token != $omb['token']) { - common_user_error(_('Not authorized.')); - return; - } - - $version = $req->get_parameter('omb_version'); - - if ($version != OMB_VERSION_01) { - common_user_error(_('Unknown version of OMB protocol.')); - return; - } - - $nickname = $req->get_parameter('omb_listener_nickname'); - - if (!$nickname) { - common_user_error(_('No nickname provided by remote server.')); - return; - } - - $profile_url = $req->get_parameter('omb_listener_profile'); - - if (!$profile_url) { - common_user_error(_('No profile URL returned by server.')); - return; - } - - if (!Validate::uri($profile_url, array('allowed_schemes' => array('http', 'https')))) { - common_user_error(_('Invalid profile URL returned by server.')); - return; - } - - if ($profile_url == common_local_url('showstream', array('nickname' => $nickname))) { - common_user_error(_('You can use the local subscription!')); - return; - } - - common_debug('listenee: "'.$omb['listenee'].'"', __FILE__); - - $user = User::staticGet('nickname', $omb['listenee']); - - if (!$user) { - common_user_error(_('User being listened to doesn\'t exist.')); - return; - } - - $other = User::staticGet('uri', $omb['listener']); - - if ($other) { - common_user_error(_('You can use the local subscription!')); - return; - } - - $fullname = $req->get_parameter('omb_listener_fullname'); - $homepage = $req->get_parameter('omb_listener_homepage'); - $bio = $req->get_parameter('omb_listener_bio'); - $location = $req->get_parameter('omb_listener_location'); - $avatar_url = $req->get_parameter('omb_listener_avatar'); - - list($newtok, $newsecret) = $this->access_token($omb); - - if (!$newtok || !$newsecret) { - common_user_error(_('Couldn\'t convert request tokens to access tokens.')); - return; - } - - # XXX: possible attack point; subscribe and return someone else's profile URI - - $remote = Remote_profile::staticGet('uri', $omb['listener']); - - if ($remote) { - $exists = true; - $profile = Profile::staticGet($remote->id); - $orig_remote = clone($remote); - $orig_profile = clone($profile); - # XXX: compare current postNotice and updateProfile URLs to the ones - # stored in the DB to avoid (possibly...) above attack - } else { - $exists = false; - $remote = new Remote_profile(); - $remote->uri = $omb['listener']; - $profile = new Profile(); - } - - $profile->nickname = $nickname; - $profile->profileurl = $profile_url; - - if ($fullname) { - $profile->fullname = $fullname; - } - if ($homepage) { - $profile->homepage = $homepage; - } - if ($bio) { - $profile->bio = $bio; - } - if ($location) { - $profile->location = $location; - } - - if ($exists) { - $profile->update($orig_profile); - } else { - $profile->created = DB_DataObject_Cast::dateTime(); # current time - $id = $profile->insert(); - if (!$id) { - common_server_error(_('Error inserting new profile')); - return; - } - $remote->id = $id; - } - - if ($avatar_url) { - if (!$this->add_avatar($profile, $avatar_url)) { - common_server_error(_('Error inserting avatar')); - return; - } - } - - $remote->postnoticeurl = $omb['post_notice_url']; - $remote->updateprofileurl = $omb['update_profile_url']; - - if ($exists) { - if (!$remote->update($orig_remote)) { - common_server_error(_('Error updating remote profile')); - return; - } - } else { - $remote->created = DB_DataObject_Cast::dateTime(); # current time - if (!$remote->insert()) { - common_server_error(_('Error inserting remote profile')); - return; - } - } + $profile_url = $req->get_parameter('omb_listener_profile'); + + if (!$profile_url) { + common_user_error(_('No profile URL returned by server.')); + return; + } + + if (!Validate::uri($profile_url, array('allowed_schemes' => array('http', 'https')))) { + common_user_error(_('Invalid profile URL returned by server.')); + return; + } + + if ($profile_url == common_local_url('showstream', array('nickname' => $nickname))) { + common_user_error(_('You can use the local subscription!')); + return; + } + + common_debug('listenee: "'.$omb['listenee'].'"', __FILE__); + + $user = User::staticGet('nickname', $omb['listenee']); + + if (!$user) { + common_user_error(_('User being listened to doesn\'t exist.')); + return; + } + + $other = User::staticGet('uri', $omb['listener']); + + if ($other) { + common_user_error(_('You can use the local subscription!')); + return; + } + + $fullname = $req->get_parameter('omb_listener_fullname'); + $homepage = $req->get_parameter('omb_listener_homepage'); + $bio = $req->get_parameter('omb_listener_bio'); + $location = $req->get_parameter('omb_listener_location'); + $avatar_url = $req->get_parameter('omb_listener_avatar'); + + list($newtok, $newsecret) = $this->access_token($omb); + + if (!$newtok || !$newsecret) { + common_user_error(_('Couldn\'t convert request tokens to access tokens.')); + return; + } + + # XXX: possible attack point; subscribe and return someone else's profile URI + + $remote = Remote_profile::staticGet('uri', $omb['listener']); + + if ($remote) { + $exists = true; + $profile = Profile::staticGet($remote->id); + $orig_remote = clone($remote); + $orig_profile = clone($profile); + # XXX: compare current postNotice and updateProfile URLs to the ones + # stored in the DB to avoid (possibly...) above attack + } else { + $exists = false; + $remote = new Remote_profile(); + $remote->uri = $omb['listener']; + $profile = new Profile(); + } + + $profile->nickname = $nickname; + $profile->profileurl = $profile_url; + + if ($fullname) { + $profile->fullname = $fullname; + } + if ($homepage) { + $profile->homepage = $homepage; + } + if ($bio) { + $profile->bio = $bio; + } + if ($location) { + $profile->location = $location; + } + + if ($exists) { + $profile->update($orig_profile); + } else { + $profile->created = DB_DataObject_Cast::dateTime(); # current time + $id = $profile->insert(); + if (!$id) { + common_server_error(_('Error inserting new profile')); + return; + } + $remote->id = $id; + } + + if ($avatar_url) { + if (!$this->add_avatar($profile, $avatar_url)) { + common_server_error(_('Error inserting avatar')); + return; + } + } + + $remote->postnoticeurl = $omb['post_notice_url']; + $remote->updateprofileurl = $omb['update_profile_url']; + + if ($exists) { + if (!$remote->update($orig_remote)) { + common_server_error(_('Error updating remote profile')); + return; + } + } else { + $remote->created = DB_DataObject_Cast::dateTime(); # current time + if (!$remote->insert()) { + common_server_error(_('Error inserting remote profile')); + return; + } + } if ($user->hasBlocked($profile)) { $this->client_error(_('That user has blocked you from subscribing.')); return; } - $sub = new Subscription(); + $sub = new Subscription(); - $sub->subscriber = $remote->id; - $sub->subscribed = $user->id; + $sub->subscriber = $remote->id; + $sub->subscribed = $user->id; $sub_exists = false; @@ -202,8 +204,8 @@ class FinishremotesubscribeAction extends Action { $sub->created = DB_DataObject_Cast::dateTime(); # current time } - $sub->token = $newtok; - $sub->secret = $newsecret; + $sub->token = $newtok; + $sub->secret = $newsecret; if ($sub_exists) { $result = $sub->update($orig_sub); @@ -211,78 +213,80 @@ class FinishremotesubscribeAction extends Action { $result = $sub->insert(); } - if (!$result) { + if (!$result) { common_log_db_error($sub, ($sub_exists) ? 'UPDATE' : 'INSERT', __FILE__); - common_user_error(_('Couldn\'t insert new subscription.')); - return; - } + common_user_error(_('Couldn\'t insert new subscription.')); + return; + } - # Notify user, if necessary + # Notify user, if necessary - mail_subscribe_notify_profile($user, $profile); + mail_subscribe_notify_profile($user, $profile); - # Clear the data - unset($_SESSION['oauth_authorization_request']); + # Clear the data + unset($_SESSION['oauth_authorization_request']); - # If we show subscriptions in reverse chron order, this should - # show up close to the top of the page + # If we show subscriptions in reverse chron order, this should + # show up close to the top of the page - common_redirect(common_local_url('subscribers', array('nickname' => - $user->nickname))); - } + common_redirect(common_local_url('subscribers', array('nickname' => + $user->nickname))); + } - function add_avatar($profile, $url) { - $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar'); - copy($url, $temp_filename); - return $profile->setOriginal($temp_filename); - } + function add_avatar($profile, $url) + { + $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar'); + copy($url, $temp_filename); + return $profile->setOriginal($temp_filename); + } - function access_token($omb) { + function access_token($omb) + { - common_debug('starting request for access token', __FILE__); + common_debug('starting request for access token', __FILE__); - $con = omb_oauth_consumer(); - $tok = new OAuthToken($omb['token'], $omb['secret']); + $con = omb_oauth_consumer(); + $tok = new OAuthToken($omb['token'], $omb['secret']); - common_debug('using request token "'.$tok.'"', __FILE__); + common_debug('using request token "'.$tok.'"', __FILE__); - $url = $omb['access_token_url']; + $url = $omb['access_token_url']; - common_debug('using access token url "'.$url.'"', __FILE__); + common_debug('using access token url "'.$url.'"', __FILE__); - # XXX: Is this the right thing to do? Strip off GET params and make them - # POST params? Seems wrong to me. + # XXX: Is this the right thing to do? Strip off GET params and make them + # POST params? Seems wrong to me. - $parsed = parse_url($url); - $params = array(); - parse_str($parsed['query'], $params); + $parsed = parse_url($url); + $params = array(); + parse_str($parsed['query'], $params); - $req = OAuthRequest::from_consumer_and_token($con, $tok, "POST", $url, $params); + $req = OAuthRequest::from_consumer_and_token($con, $tok, "POST", $url, $params); - $req->set_parameter('omb_version', OMB_VERSION_01); + $req->set_parameter('omb_version', OMB_VERSION_01); - # XXX: test to see if endpoint accepts this signature method + # XXX: test to see if endpoint accepts this signature method - $req->sign_request(omb_hmac_sha1(), $con, $tok); + $req->sign_request(omb_hmac_sha1(), $con, $tok); - # We re-use this tool's fetcher, since it's pretty good + # We re-use this tool's fetcher, since it's pretty good - common_debug('posting to access token url "'.$req->get_normalized_http_url().'"', __FILE__); - common_debug('posting request data "'.$req->to_postdata().'"', __FILE__); + common_debug('posting to access token url "'.$req->get_normalized_http_url().'"', __FILE__); + common_debug('posting request data "'.$req->to_postdata().'"', __FILE__); - $fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); - $result = $fetcher->post($req->get_normalized_http_url(), - $req->to_postdata(), + $fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); + $result = $fetcher->post($req->get_normalized_http_url(), + $req->to_postdata(), array('User-Agent' => 'Laconica/' . LACONICA_VERSION)); - common_debug('got result: "'.print_r($result,TRUE).'"', __FILE__); + common_debug('got result: "'.print_r($result,true).'"', __FILE__); - if ($result->status != 200) { - return NULL; - } + if ($result->status != 200) { + return null; + } - parse_str($result->body, $return); + parse_str($result->body, $return); - return array($return['oauth_token'], $return['oauth_token_secret']); - } + return array($return['oauth_token'], $return['oauth_token_secret']); + } } diff --git a/actions/foaf.php b/actions/foaf.php index 6811fc05a..30e98960c 100644 --- a/actions/foaf.php +++ b/actions/foaf.php @@ -23,180 +23,185 @@ define('LISTENER', 1); define('LISTENEE', -1); define('BOTH', 0); -class FoafAction extends Action { - - function is_readonly() { - return true; - } - - function handle($args) { - parent::handle($args); - - $nickname = $this->trimmed('nickname'); - - $user = User::staticGet('nickname', $nickname); - - if (!$user) { - common_user_error(_('No such user.'), 404); - return; - } - - $profile = $user->getProfile(); - - if (!$profile) { - common_server_error(_('User has no profile.'), 500); - return; - } - - header('Content-Type: application/rdf+xml'); - - common_start_xml(); - common_element_start('rdf:RDF', array('xmlns:rdf' => - 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', - 'xmlns:rdfs' => - 'http://www.w3.org/2000/01/rdf-schema#', - 'xmlns:geo' => - 'http://www.w3.org/2003/01/geo/wgs84_pos#', - 'xmlns' => 'http://xmlns.com/foaf/0.1/')); - - # This is the document about the user - - $this->show_ppd('', $user->uri); - - # XXX: might not be a person - common_element_start('Person', array('rdf:about' => - $user->uri)); - common_element('mbox_sha1sum', NULL, sha1('mailto:' . $user->email)); - if ($profile->fullname) { - common_element('name', NULL, $profile->fullname); - } - if ($profile->homepage) { - common_element('homepage', array('rdf:resource' => $profile->homepage)); - } - if ($profile->bio) { - common_element('rdfs:comment', NULL, $profile->bio); - } - # XXX: more structured location data - if ($profile->location) { - common_element_start('based_near'); - common_element_start('geo:SpatialThing'); - common_element('name', NULL, $profile->location); - common_element_end('geo:SpatialThing'); - common_element_end('based_near'); - } - - $this->show_microblogging_account($profile, common_root_url()); - - $avatar = $profile->getOriginalAvatar(); - - if ($avatar) { - common_element_start('img'); - common_element_start('Image', array('rdf:about' => $avatar->url)); - foreach (array(AVATAR_PROFILE_SIZE, AVATAR_STREAM_SIZE, AVATAR_MINI_SIZE) as $size) { - $scaled = $profile->getAvatar($size); - if (!$scaled->original) { # sometimes the original has one of our scaled sizes - common_element_start('thumbnail'); - common_element('Image', array('rdf:about' => $scaled->url)); - common_element_end('thumbnail'); - } - } - common_element_end('Image'); - common_element_end('img'); - } - - # Get people user is subscribed to - - $person = array(); - - $sub = new Subscription(); - $sub->subscriber = $profile->id; - $sub->whereAdd('subscriber != subscribed'); - - if ($sub->find()) { - while ($sub->fetch()) { - if ($sub->token) { - $other = Remote_profile::staticGet('id', $sub->subscribed); - } else { - $other = User::staticGet('id', $sub->subscribed); - } - if (!$other) { - common_debug('Got a bad subscription: '.print_r($sub,TRUE)); - continue; - } - common_element('knows', array('rdf:resource' => $other->uri)); - $person[$other->uri] = array(LISTENEE, $other); - } - } - - # Get people who subscribe to user - - $sub = new Subscription(); - $sub->subscribed = $profile->id; - $sub->whereAdd('subscriber != subscribed'); - - if ($sub->find()) { - while ($sub->fetch()) { - if ($sub->token) { - $other = Remote_profile::staticGet('id', $sub->subscriber); - } else { - $other = User::staticGet('id', $sub->subscriber); - } - if (!$other) { - common_debug('Got a bad subscription: '.print_r($sub,TRUE)); - continue; - } - if (array_key_exists($other->uri, $person)) { - $person[$other->uri][0] = BOTH; - } else { - $person[$other->uri] = array(LISTENER, $other); - } - } - } - - common_element_end('Person'); - - foreach ($person as $uri => $p) { - $foaf_url = NULL; - if ($p[1] instanceof User) { - $foaf_url = common_local_url('foaf', array('nickname' => $p[1]->nickname)); - } - $profile = Profile::staticGet($p[1]->id); - common_element_start('Person', array('rdf:about' => $uri)); - if ($p[0] == LISTENER || $p[0] == BOTH) { - common_element('knows', array('rdf:resource' => $user->uri)); - } - $this->show_microblogging_account($profile, ($p[1] instanceof User) ? - common_root_url() : NULL); - if ($foaf_url) { - common_element('rdfs:seeAlso', array('rdf:resource' => $foaf_url)); - } - common_element_end('Person'); - if ($foaf_url) { - $this->show_ppd($foaf_url, $uri); - } - } - - common_element_end('rdf:RDF'); - } - - function show_ppd($foaf_url, $person_uri) { - common_element_start('PersonalProfileDocument', array('rdf:about' => $foaf_url)); - common_element('maker', array('rdf:resource' => $person_uri)); - common_element('primaryTopic', array('rdf:resource' => $person_uri)); - common_element_end('PersonalProfileDocument'); - } - - function show_microblogging_account($profile, $service=NULL) { - # Their account - common_element_start('holdsAccount'); - common_element_start('OnlineAccount'); - if ($service) { - common_element('accountServiceHomepage', array('rdf:resource' => - $service)); - } - common_element('accountName', NULL, $profile->nickname); - common_element('homepage', array('rdf:resource' => $profile->profileurl)); - common_element_end('OnlineAccount'); - common_element_end('holdsAccount'); - } +class FoafAction extends Action +{ + + function is_readonly() + { + return true; + } + + function handle($args) + { + parent::handle($args); + + $nickname = $this->trimmed('nickname'); + + $user = User::staticGet('nickname', $nickname); + + if (!$user) { + common_user_error(_('No such user.'), 404); + return; + } + + $profile = $user->getProfile(); + + if (!$profile) { + common_server_error(_('User has no profile.'), 500); + return; + } + + header('Content-Type: application/rdf+xml'); + + common_start_xml(); + common_element_start('rdf:RDF', array('xmlns:rdf' => + 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', + 'xmlns:rdfs' => + 'http://www.w3.org/2000/01/rdf-schema#', + 'xmlns:geo' => + 'http://www.w3.org/2003/01/geo/wgs84_pos#', + 'xmlns' => 'http://xmlns.com/foaf/0.1/')); + + # This is the document about the user + + $this->show_ppd('', $user->uri); + + # XXX: might not be a person + common_element_start('Person', array('rdf:about' => + $user->uri)); + common_element('mbox_sha1sum', null, sha1('mailto:' . $user->email)); + if ($profile->fullname) { + common_element('name', null, $profile->fullname); + } + if ($profile->homepage) { + common_element('homepage', array('rdf:resource' => $profile->homepage)); + } + if ($profile->bio) { + common_element('rdfs:comment', null, $profile->bio); + } + # XXX: more structured location data + if ($profile->location) { + common_element_start('based_near'); + common_element_start('geo:SpatialThing'); + common_element('name', null, $profile->location); + common_element_end('geo:SpatialThing'); + common_element_end('based_near'); + } + + $this->show_microblogging_account($profile, common_root_url()); + + $avatar = $profile->getOriginalAvatar(); + + if ($avatar) { + common_element_start('img'); + common_element_start('Image', array('rdf:about' => $avatar->url)); + foreach (array(AVATAR_PROFILE_SIZE, AVATAR_STREAM_SIZE, AVATAR_MINI_SIZE) as $size) { + $scaled = $profile->getAvatar($size); + if (!$scaled->original) { # sometimes the original has one of our scaled sizes + common_element_start('thumbnail'); + common_element('Image', array('rdf:about' => $scaled->url)); + common_element_end('thumbnail'); + } + } + common_element_end('Image'); + common_element_end('img'); + } + + # Get people user is subscribed to + + $person = array(); + + $sub = new Subscription(); + $sub->subscriber = $profile->id; + $sub->whereAdd('subscriber != subscribed'); + + if ($sub->find()) { + while ($sub->fetch()) { + if ($sub->token) { + $other = Remote_profile::staticGet('id', $sub->subscribed); + } else { + $other = User::staticGet('id', $sub->subscribed); + } + if (!$other) { + common_debug('Got a bad subscription: '.print_r($sub,true)); + continue; + } + common_element('knows', array('rdf:resource' => $other->uri)); + $person[$other->uri] = array(LISTENEE, $other); + } + } + + # Get people who subscribe to user + + $sub = new Subscription(); + $sub->subscribed = $profile->id; + $sub->whereAdd('subscriber != subscribed'); + + if ($sub->find()) { + while ($sub->fetch()) { + if ($sub->token) { + $other = Remote_profile::staticGet('id', $sub->subscriber); + } else { + $other = User::staticGet('id', $sub->subscriber); + } + if (!$other) { + common_debug('Got a bad subscription: '.print_r($sub,true)); + continue; + } + if (array_key_exists($other->uri, $person)) { + $person[$other->uri][0] = BOTH; + } else { + $person[$other->uri] = array(LISTENER, $other); + } + } + } + + common_element_end('Person'); + + foreach ($person as $uri => $p) { + $foaf_url = null; + if ($p[1] instanceof User) { + $foaf_url = common_local_url('foaf', array('nickname' => $p[1]->nickname)); + } + $profile = Profile::staticGet($p[1]->id); + common_element_start('Person', array('rdf:about' => $uri)); + if ($p[0] == LISTENER || $p[0] == BOTH) { + common_element('knows', array('rdf:resource' => $user->uri)); + } + $this->show_microblogging_account($profile, ($p[1] instanceof User) ? + common_root_url() : null); + if ($foaf_url) { + common_element('rdfs:seeAlso', array('rdf:resource' => $foaf_url)); + } + common_element_end('Person'); + if ($foaf_url) { + $this->show_ppd($foaf_url, $uri); + } + } + + common_element_end('rdf:RDF'); + } + + function show_ppd($foaf_url, $person_uri) + { + common_element_start('PersonalProfileDocument', array('rdf:about' => $foaf_url)); + common_element('maker', array('rdf:resource' => $person_uri)); + common_element('primaryTopic', array('rdf:resource' => $person_uri)); + common_element_end('PersonalProfileDocument'); + } + + function show_microblogging_account($profile, $service=null) + { + # Their account + common_element_start('holdsAccount'); + common_element_start('OnlineAccount'); + if ($service) { + common_element('accountServiceHomepage', array('rdf:resource' => + $service)); + } + common_element('accountName', null, $profile->nickname); + common_element('homepage', array('rdf:resource' => $profile->profileurl)); + common_element_end('OnlineAccount'); + common_element_end('holdsAccount'); + } } diff --git a/actions/imsettings.php b/actions/imsettings.php index 0aa7631dc..8ecf200ec 100644 --- a/actions/imsettings.php +++ b/actions/imsettings.php @@ -22,249 +22,259 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/settingsaction.php'); require_once(INSTALLDIR.'/lib/jabber.php'); -class ImsettingsAction extends SettingsAction { - - function get_instructions() { - return _('You can send and receive notices through Jabber/GTalk [instant messages](%%doc.im%%). Configure your address and settings below.'); - } - - function show_form($msg=NULL, $success=false) { - $user = common_current_user(); - $this->form_header(_('IM Settings'), $msg, $success); - common_element_start('form', array('method' => 'post', - 'id' => 'imsettings', - 'action' => - common_local_url('imsettings'))); - common_hidden('token', common_session_token()); - - common_element('h2', NULL, _('Address')); - - if ($user->jabber) { - common_element_start('p'); - common_element('span', 'address confirmed', $user->jabber); - common_element('span', 'input_instructions', - _('Current confirmed Jabber/GTalk address.')); - common_hidden('jabber', $user->jabber); - common_element_end('p'); - common_submit('remove', _('Remove')); - } else { - $confirm = $this->get_confirmation(); - if ($confirm) { - common_element_start('p'); - common_element('span', 'address unconfirmed', $confirm->address); - common_element('span', 'input_instructions', - sprintf(_('Awaiting confirmation on this address. Check your Jabber/GTalk account for a message with further instructions. (Did you add %s to your buddy list?)'), jabber_daemon_address())); - common_hidden('jabber', $confirm->address); - common_element_end('p'); - common_submit('cancel', _('Cancel')); - } else { - common_input('jabber', _('IM Address'), - ($this->arg('jabber')) ? $this->arg('jabber') : NULL, - sprintf(_('Jabber or GTalk address, like "UserName@example.org". First, make sure to add %s to your buddy list in your IM client or on GTalk.'), jabber_daemon_address())); - common_submit('add', _('Add')); - } - } - - common_element('h2', NULL, _('Preferences')); - - common_checkbox('jabbernotify', - _('Send me notices through Jabber/GTalk.'), - $user->jabbernotify); - common_checkbox('updatefrompresence', - _('Post a notice when my Jabber/GTalk status changes.'), - $user->updatefrompresence); - common_checkbox('jabberreplies', - _('Send me replies through Jabber/GTalk from people I\'m not subscribed to.'), - $user->jabberreplies); - common_checkbox('jabbermicroid', - _('Publish a MicroID for my Jabber/GTalk address.'), - $user->jabbermicroid); - common_submit('save', _('Save')); - - common_element_end('form'); - common_show_footer(); - } - - function get_confirmation() { - $user = common_current_user(); - $confirm = new Confirm_address(); - $confirm->user_id = $user->id; - $confirm->address_type = 'jabber'; - if ($confirm->find(TRUE)) { - return $confirm; - } else { - return NULL; - } - } - - function handle_post() { - - # CSRF protection - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->show_form(_('There was a problem with your session token. Try again, please.')); - return; - } - - if ($this->arg('save')) { - $this->save_preferences(); - } else if ($this->arg('add')) { - $this->add_address(); - } else if ($this->arg('cancel')) { - $this->cancel_confirmation(); - } else if ($this->arg('remove')) { - $this->remove_address(); - } else { - $this->show_form(_('Unexpected form submission.')); - } - } - - function save_preferences() { - - $jabbernotify = $this->boolean('jabbernotify'); - $updatefrompresence = $this->boolean('updatefrompresence'); - $jabberreplies = $this->boolean('jabberreplies'); - $jabbermicroid = $this->boolean('jabbermicroid'); - - $user = common_current_user(); - - assert(!is_null($user)); # should already be checked - - $user->query('BEGIN'); - - $original = clone($user); - - $user->jabbernotify = $jabbernotify; - $user->updatefrompresence = $updatefrompresence; - $user->jabberreplies = $jabberreplies; - $user->jabbermicroid = $jabbermicroid; - - $result = $user->update($original); - - if ($result === FALSE) { - common_log_db_error($user, 'UPDATE', __FILE__); - common_server_error(_('Couldn\'t update user.')); - return; - } - - $user->query('COMMIT'); - - $this->show_form(_('Preferences saved.'), true); - } - - function add_address() { - - $user = common_current_user(); - - $jabber = $this->trimmed('jabber'); - - # Some validation - - if (!$jabber) { - $this->show_form(_('No Jabber ID.')); - return; - } - - $jabber = jabber_normalize_jid($jabber); - - if (!$jabber) { - $this->show_form(_('Cannot normalize that Jabber ID')); - return; - } - if (!jabber_valid_base_jid($jabber)) { - $this->show_form(_('Not a valid Jabber ID')); - return; - } else if ($user->jabber == $jabber) { - $this->show_form(_('That is already your Jabber ID.')); - return; - } else if ($this->jabber_exists($jabber)) { - $this->show_form(_('Jabber ID already belongs to another user.')); - return; - } - - $confirm = new Confirm_address(); - $confirm->address = $jabber; - $confirm->address_type = 'jabber'; - $confirm->user_id = $user->id; - $confirm->code = common_confirmation_code(64); - - $result = $confirm->insert(); - - if ($result === FALSE) { - common_log_db_error($confirm, 'INSERT', __FILE__); - common_server_error(_('Couldn\'t insert confirmation code.')); - return; - } - - if (!common_config('queue', 'enabled')) { - jabber_confirm_address($confirm->code, - $user->nickname, - $jabber); - } - - $msg = sprintf(_('A confirmation code was sent to the IM address you added. You must approve %s for sending messages to you.'), jabber_daemon_address()); - - $this->show_form($msg, TRUE); - } - - function cancel_confirmation() { - $jabber = $this->arg('jabber'); - $confirm = $this->get_confirmation(); - if (!$confirm) { - $this->show_form(_('No pending confirmation to cancel.')); - return; - } - if ($confirm->address != $jabber) { - $this->show_form(_('That is the wrong IM address.')); - return; - } +class ImsettingsAction extends SettingsAction +{ + + function get_instructions() + { + return _('You can send and receive notices through Jabber/GTalk [instant messages](%%doc.im%%). Configure your address and settings below.'); + } + + function show_form($msg=null, $success=false) + { + $user = common_current_user(); + $this->form_header(_('IM Settings'), $msg, $success); + common_element_start('form', array('method' => 'post', + 'id' => 'imsettings', + 'action' => + common_local_url('imsettings'))); + common_hidden('token', common_session_token()); + + common_element('h2', null, _('Address')); + + if ($user->jabber) { + common_element_start('p'); + common_element('span', 'address confirmed', $user->jabber); + common_element('span', 'input_instructions', + _('Current confirmed Jabber/GTalk address.')); + common_hidden('jabber', $user->jabber); + common_element_end('p'); + common_submit('remove', _('Remove')); + } else { + $confirm = $this->get_confirmation(); + if ($confirm) { + common_element_start('p'); + common_element('span', 'address unconfirmed', $confirm->address); + common_element('span', 'input_instructions', + sprintf(_('Awaiting confirmation on this address. Check your Jabber/GTalk account for a message with further instructions. (Did you add %s to your buddy list?)'), jabber_daemon_address())); + common_hidden('jabber', $confirm->address); + common_element_end('p'); + common_submit('cancel', _('Cancel')); + } else { + common_input('jabber', _('IM Address'), + ($this->arg('jabber')) ? $this->arg('jabber') : null, + sprintf(_('Jabber or GTalk address, like "UserName@example.org". First, make sure to add %s to your buddy list in your IM client or on GTalk.'), jabber_daemon_address())); + common_submit('add', _('Add')); + } + } + + common_element('h2', null, _('Preferences')); + + common_checkbox('jabbernotify', + _('Send me notices through Jabber/GTalk.'), + $user->jabbernotify); + common_checkbox('updatefrompresence', + _('Post a notice when my Jabber/GTalk status changes.'), + $user->updatefrompresence); + common_checkbox('jabberreplies', + _('Send me replies through Jabber/GTalk from people I\'m not subscribed to.'), + $user->jabberreplies); + common_checkbox('jabbermicroid', + _('Publish a MicroID for my Jabber/GTalk address.'), + $user->jabbermicroid); + common_submit('save', _('Save')); + + common_element_end('form'); + common_show_footer(); + } + + function get_confirmation() + { + $user = common_current_user(); + $confirm = new Confirm_address(); + $confirm->user_id = $user->id; + $confirm->address_type = 'jabber'; + if ($confirm->find(true)) { + return $confirm; + } else { + return null; + } + } + + function handle_post() + { + + # CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->show_form(_('There was a problem with your session token. Try again, please.')); + return; + } + + if ($this->arg('save')) { + $this->save_preferences(); + } else if ($this->arg('add')) { + $this->add_address(); + } else if ($this->arg('cancel')) { + $this->cancel_confirmation(); + } else if ($this->arg('remove')) { + $this->remove_address(); + } else { + $this->show_form(_('Unexpected form submission.')); + } + } + + function save_preferences() + { + + $jabbernotify = $this->boolean('jabbernotify'); + $updatefrompresence = $this->boolean('updatefrompresence'); + $jabberreplies = $this->boolean('jabberreplies'); + $jabbermicroid = $this->boolean('jabbermicroid'); + + $user = common_current_user(); + + assert(!is_null($user)); # should already be checked + + $user->query('BEGIN'); + + $original = clone($user); + + $user->jabbernotify = $jabbernotify; + $user->updatefrompresence = $updatefrompresence; + $user->jabberreplies = $jabberreplies; + $user->jabbermicroid = $jabbermicroid; + + $result = $user->update($original); + + if ($result === false) { + common_log_db_error($user, 'UPDATE', __FILE__); + common_server_error(_('Couldn\'t update user.')); + return; + } + + $user->query('COMMIT'); + + $this->show_form(_('Preferences saved.'), true); + } + + function add_address() + { + + $user = common_current_user(); + + $jabber = $this->trimmed('jabber'); + + # Some validation + + if (!$jabber) { + $this->show_form(_('No Jabber ID.')); + return; + } + + $jabber = jabber_normalize_jid($jabber); + + if (!$jabber) { + $this->show_form(_('Cannot normalize that Jabber ID')); + return; + } + if (!jabber_valid_base_jid($jabber)) { + $this->show_form(_('Not a valid Jabber ID')); + return; + } else if ($user->jabber == $jabber) { + $this->show_form(_('That is already your Jabber ID.')); + return; + } else if ($this->jabber_exists($jabber)) { + $this->show_form(_('Jabber ID already belongs to another user.')); + return; + } + + $confirm = new Confirm_address(); + $confirm->address = $jabber; + $confirm->address_type = 'jabber'; + $confirm->user_id = $user->id; + $confirm->code = common_confirmation_code(64); + + $result = $confirm->insert(); + + if ($result === false) { + common_log_db_error($confirm, 'INSERT', __FILE__); + common_server_error(_('Couldn\'t insert confirmation code.')); + return; + } + + if (!common_config('queue', 'enabled')) { + jabber_confirm_address($confirm->code, + $user->nickname, + $jabber); + } + + $msg = sprintf(_('A confirmation code was sent to the IM address you added. You must approve %s for sending messages to you.'), jabber_daemon_address()); + + $this->show_form($msg, true); + } + + function cancel_confirmation() + { + $jabber = $this->arg('jabber'); + $confirm = $this->get_confirmation(); + if (!$confirm) { + $this->show_form(_('No pending confirmation to cancel.')); + return; + } + if ($confirm->address != $jabber) { + $this->show_form(_('That is the wrong IM address.')); + return; + } $result = $confirm->delete(); if (!$result) { - common_log_db_error($confirm, 'DELETE', __FILE__); + common_log_db_error($confirm, 'DELETE', __FILE__); $this->server_error(_('Couldn\'t delete email confirmation.')); return; } - $this->show_form(_('Confirmation cancelled.'), TRUE); - } - - function remove_address() { - - $user = common_current_user(); - $jabber = $this->arg('jabber'); - - # Maybe an old tab open...? - - if ($user->jabber != $jabber) { - $this->show_form(_('That is not your Jabber ID.')); - return; - } - - $user->query('BEGIN'); - $original = clone($user); - $user->jabber = NULL; - $result = $user->updateKeys($original); - if (!$result) { - common_log_db_error($user, 'UPDATE', __FILE__); - common_server_error(_('Couldn\'t update user.')); - return; - } - $user->query('COMMIT'); - - # XXX: unsubscribe to the old address - - $this->show_form(_('The address was removed.'), TRUE); - } - - function jabber_exists($jabber) { - $user = common_current_user(); - $other = User::staticGet('jabber', $jabber); - if (!$other) { - return false; - } else { - return $other->id != $user->id; - } - } + $this->show_form(_('Confirmation cancelled.'), true); + } + + function remove_address() + { + + $user = common_current_user(); + $jabber = $this->arg('jabber'); + + # Maybe an old tab open...? + + if ($user->jabber != $jabber) { + $this->show_form(_('That is not your Jabber ID.')); + return; + } + + $user->query('BEGIN'); + $original = clone($user); + $user->jabber = null; + $result = $user->updateKeys($original); + if (!$result) { + common_log_db_error($user, 'UPDATE', __FILE__); + common_server_error(_('Couldn\'t update user.')); + return; + } + $user->query('COMMIT'); + + # XXX: unsubscribe to the old address + + $this->show_form(_('The address was removed.'), true); + } + + function jabber_exists($jabber) + { + $user = common_current_user(); + $other = User::staticGet('jabber', $jabber); + if (!$other) { + return false; + } else { + return $other->id != $user->id; + } + } } diff --git a/actions/inbox.php b/actions/inbox.php index c752e404e..da27814a6 100644 --- a/actions/inbox.php +++ b/actions/inbox.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 + * action handler for message inbox + * + * 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,41 +18,109 @@ * * 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 Message + * @package Laconica + * @author Evan Prodromou <evan@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); +} + +require_once INSTALLDIR.'/lib/mailbox.php'; + +/** + * action handler for message inbox + * + * @category Message + * @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/ + * @see MailboxAction */ -if (!defined('LACONICA')) { exit(1); } - -require_once(INSTALLDIR.'/lib/mailbox.php'); - -class InboxAction extends MailboxAction { - - function get_title($user, $page) { - if ($page > 1) { - $title = sprintf(_("Inbox for %s - page %d"), $user->nickname, $page); - } else { - $title = sprintf(_("Inbox for %s"), $user->nickname); - } - return $title; - } - - function get_messages($user, $page) { - $message = new Message(); - $message->to_profile = $user->id; - $message->orderBy('created DESC, id DESC'); - $message->limit((($page-1)*MESSAGES_PER_PAGE), MESSAGES_PER_PAGE + 1); - - if ($message->find()) { - return $message; - } else { - return NULL; - } - } - - function get_message_profile($message) { - return $message->getFrom(); - } - - function get_instructions() { - return _('This is your inbox, which lists your incoming private messages.'); - } +class InboxAction extends MailboxAction +{ + /** + * returns the title of the page + * + * @param User $user current user + * @param int $page current page + * + * @return string localised title of the page + * + * @see MailboxAction::getTitle() + */ + + function getTitle($user, $page) + { + if ($page > 1) { + $title = sprintf(_("Inbox for %s - page %d"), $user->nickname, $page); + } else { + $title = sprintf(_("Inbox for %s"), $user->nickname); + } + return $title; + } + + /** + * retrieve the messages for this user and this page + * + * Does a query for the right messages + * + * @param User $user The current user + * @param int $page The page the user is on + * + * @return Message data object with stream for messages + * + * @see MailboxAction::getMessages() + */ + + function getMessages($user, $page) + { + $message = new Message(); + + $message->to_profile = $user->id; + + $message->orderBy('created DESC, id DESC'); + $message->limit((($page-1)*MESSAGES_PER_PAGE), MESSAGES_PER_PAGE + 1); + + if ($message->find()) { + return $message; + } else { + return null; + } + } + + /** + * returns the profile we want to show with the message + * + * For inboxes, we show the sender. + * + * @param Message $message The message to get the profile for + * + * @return Profile The profile of the message sender + * + * @see MailboxAction::getMessageProfile() + */ + + function getMessageProfile($message) + { + return $message->getFrom(); + } + + /** + * instructions for using this page + * + * @return string localised instructions for using the page + */ + + function getInstructions() + { + return _('This is your inbox, which lists your incoming private messages.'); + } } diff --git a/actions/invite.php b/actions/invite.php index c7d92085c..80e022a3d 100644 --- a/actions/invite.php +++ b/actions/invite.php @@ -19,181 +19,188 @@ if (!defined('LACONICA')) { exit(1); } -class InviteAction extends Action { +class InviteAction extends Action +{ - function is_readonly() { - return false; - } + function is_readonly() + { + return false; + } - function handle($args) { + function handle($args) + { parent::handle($args); - if (!common_logged_in()) { - $this->client_error(sprintf(_('You must be logged in to invite other users to use %s'), - common_config('site', 'name'))); - return; - } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { - $this->send_invitations(); - } else { - $this->show_form(); - } - } - - function send_invitations() { - - # CSRF protection - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->show_form(_('There was a problem with your session token. Try again, please.')); - return; - } - - $user = common_current_user(); - $profile = $user->getProfile(); - - $bestname = $profile->getBestName(); - $sitename = common_config('site', 'name'); - $personal = $this->trimmed('personal'); - - $addresses = explode("\n", $this->trimmed('addresses')); - - foreach ($addresses as $email) { - $email = trim($email); - if (!Validate::email($email, true)) { - $this->show_form(sprintf(_('Invalid email address: %s'), $email)); - return; - } - } - - $already = array(); - $subbed = array(); - - foreach ($addresses as $email) { - $email = common_canonical_email($email); - $other = User::staticGet('email', $email); - if ($other) { - if ($user->isSubscribed($other)) { - $already[] = $other; - } else { - subs_subscribe_to($user, $other); - $subbed[] = $other; - } - } else { - $sent[] = $email; - $this->send_invitation($email, $user, $personal); - } - } - - common_show_header(_('Invitation(s) sent')); - if ($already) { - common_element('p', NULL, _('You are already subscribed to these users:')); - common_element_start('ul'); - foreach ($already as $other) { - common_element('li', NULL, sprintf(_('%s (%s)'), $other->nickname, $other->email)); - } - common_element_end('ul'); - } - if ($subbed) { - common_element('p', NULL, _('These people are already users and you were automatically subscribed to them:')); - common_element_start('ul'); - foreach ($subbed as $other) { - common_element('li', NULL, sprintf(_('%s (%s)'), $other->nickname, $other->email)); - } - common_element_end('ul'); - } - if ($sent) { - common_element('p', NULL, _('Invitation(s) sent to the following people:')); - common_element_start('ul'); - foreach ($sent as $other) { - common_element('li', NULL, $other); - } - common_element_end('ul'); - common_element('p', NULL, _('You will be notified when your invitees accept the invitation and register on the site. Thanks for growing the community!')); - } - common_show_footer(); - } - - function show_top($error=NULL) { - if ($error) { - common_element('p', 'error', $error); - } else { - common_element_start('div', 'instructions'); - common_element('p', NULL, - _('Use this form to invite your friends and colleagues to use this service.')); - common_element_end('div'); - } - } - - function show_form($error=NULL) { - - global $config; - - common_show_header(_('Invite new users'), NULL, $error, array($this, 'show_top')); - - common_element_start('form', array('method' => 'post', - 'id' => 'invite', - 'action' => common_local_url('invite'))); - common_hidden('token', common_session_token()); - - common_textarea('addresses', _('Email addresses'), - $this->trimmed('addresses'), - _('Addresses of friends to invite (one per line)')); - - common_textarea('personal', _('Personal message'), - $this->trimmed('personal'), - _('Optionally add a personal message to the invitation.')); - - common_submit('send', _('Send')); - - common_element_end('form'); - - common_show_footer(); - } - - function send_invitation($email, $user, $personal) { - - $profile = $user->getProfile(); - $bestname = $profile->getBestName(); - - $sitename = common_config('site', 'name'); - - $invite = new Invitation(); - - $invite->address = $email; - $invite->address_type = 'email'; - $invite->code = common_confirmation_code(128); - $invite->user_id = $user->id; - $invite->created = common_sql_now(); - - if (!$invite->insert()) { - common_log_db_error($invite, 'INSERT', __FILE__); - return false; - } - - $recipients = array($email); - - $headers['From'] = mail_notify_from(); - $headers['To'] = $email; - $headers['Subject'] = sprintf(_('%1$s has invited you to join them on %2$s'), $bestname, $sitename); - - $body = sprintf(_("%1\$s has invited you to join them on %2\$s (%3\$s).\n\n". - "%2\$s is a micro-blogging service that lets you keep up-to-date with people you know and people who interest you.\n\n". - "You can also share news about yourself, your thoughts, or your life online with people who know about you. ". - "It's also great for meeting new people who share your interests.\n\n". - "%1\$s said:\n\n%4\$s\n\n". - "You can see %1\$s's profile page on %2\$s here:\n\n". - "%5\$s\n\n". - "If you'd like to try the service, click on the link below to accept the invitation.\n\n". - "%6\$s\n\n". - "If not, you can ignore this message. Thanks for your patience and your time.\n\n". - "Sincerely, %2\$s\n"), - $bestname, - $sitename, - common_root_url(), - $personal, - common_local_url('showstream', array('nickname' => $user->nickname)), - common_local_url('register', array('code' => $invite->code))); - - mail_send($recipients, $headers, $body); - } + if (!common_logged_in()) { + $this->client_error(sprintf(_('You must be logged in to invite other users to use %s'), + common_config('site', 'name'))); + return; + } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $this->send_invitations(); + } else { + $this->show_form(); + } + } + + function send_invitations() + { + + # CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->show_form(_('There was a problem with your session token. Try again, please.')); + return; + } + + $user = common_current_user(); + $profile = $user->getProfile(); + + $bestname = $profile->getBestName(); + $sitename = common_config('site', 'name'); + $personal = $this->trimmed('personal'); + + $addresses = explode("\n", $this->trimmed('addresses')); + + foreach ($addresses as $email) { + $email = trim($email); + if (!Validate::email($email, true)) { + $this->show_form(sprintf(_('Invalid email address: %s'), $email)); + return; + } + } + + $already = array(); + $subbed = array(); + + foreach ($addresses as $email) { + $email = common_canonical_email($email); + $other = User::staticGet('email', $email); + if ($other) { + if ($user->isSubscribed($other)) { + $already[] = $other; + } else { + subs_subscribe_to($user, $other); + $subbed[] = $other; + } + } else { + $sent[] = $email; + $this->send_invitation($email, $user, $personal); + } + } + + common_show_header(_('Invitation(s) sent')); + if ($already) { + common_element('p', null, _('You are already subscribed to these users:')); + common_element_start('ul'); + foreach ($already as $other) { + common_element('li', null, sprintf(_('%s (%s)'), $other->nickname, $other->email)); + } + common_element_end('ul'); + } + if ($subbed) { + common_element('p', null, _('These people are already users and you were automatically subscribed to them:')); + common_element_start('ul'); + foreach ($subbed as $other) { + common_element('li', null, sprintf(_('%s (%s)'), $other->nickname, $other->email)); + } + common_element_end('ul'); + } + if ($sent) { + common_element('p', null, _('Invitation(s) sent to the following people:')); + common_element_start('ul'); + foreach ($sent as $other) { + common_element('li', null, $other); + } + common_element_end('ul'); + common_element('p', null, _('You will be notified when your invitees accept the invitation and register on the site. Thanks for growing the community!')); + } + common_show_footer(); + } + + function show_top($error=null) + { + if ($error) { + common_element('p', 'error', $error); + } else { + common_element_start('div', 'instructions'); + common_element('p', null, + _('Use this form to invite your friends and colleagues to use this service.')); + common_element_end('div'); + } + } + + function show_form($error=null) + { + + global $config; + + common_show_header(_('Invite new users'), null, $error, array($this, 'show_top')); + + common_element_start('form', array('method' => 'post', + 'id' => 'invite', + 'action' => common_local_url('invite'))); + common_hidden('token', common_session_token()); + + common_textarea('addresses', _('Email addresses'), + $this->trimmed('addresses'), + _('Addresses of friends to invite (one per line)')); + + common_textarea('personal', _('Personal message'), + $this->trimmed('personal'), + _('Optionally add a personal message to the invitation.')); + + common_submit('send', _('Send')); + + common_element_end('form'); + + common_show_footer(); + } + + function send_invitation($email, $user, $personal) + { + + $profile = $user->getProfile(); + $bestname = $profile->getBestName(); + + $sitename = common_config('site', 'name'); + + $invite = new Invitation(); + + $invite->address = $email; + $invite->address_type = 'email'; + $invite->code = common_confirmation_code(128); + $invite->user_id = $user->id; + $invite->created = common_sql_now(); + + if (!$invite->insert()) { + common_log_db_error($invite, 'INSERT', __FILE__); + return false; + } + + $recipients = array($email); + + $headers['From'] = mail_notify_from(); + $headers['To'] = $email; + $headers['Subject'] = sprintf(_('%1$s has invited you to join them on %2$s'), $bestname, $sitename); + + $body = sprintf(_("%1\$s has invited you to join them on %2\$s (%3\$s).\n\n". + "%2\$s is a micro-blogging service that lets you keep up-to-date with people you know and people who interest you.\n\n". + "You can also share news about yourself, your thoughts, or your life online with people who know about you. ". + "It's also great for meeting new people who share your interests.\n\n". + "%1\$s said:\n\n%4\$s\n\n". + "You can see %1\$s's profile page on %2\$s here:\n\n". + "%5\$s\n\n". + "If you'd like to try the service, click on the link below to accept the invitation.\n\n". + "%6\$s\n\n". + "If not, you can ignore this message. Thanks for your patience and your time.\n\n". + "Sincerely, %2\$s\n"), + $bestname, + $sitename, + common_root_url(), + $personal, + common_local_url('showstream', array('nickname' => $user->nickname)), + common_local_url('register', array('code' => $invite->code))); + + mail_send($recipients, $headers, $body); + } } diff --git a/actions/login.php b/actions/login.php index ccec9cf8a..8600d44fd 100644 --- a/actions/login.php +++ b/actions/login.php @@ -19,134 +19,141 @@ if (!defined('LACONICA')) { exit(1); } -class LoginAction extends Action { +class LoginAction extends Action +{ - function is_readonly() { - return true; - } + function is_readonly() + { + return true; + } - function handle($args) { - parent::handle($args); - if (common_is_real_login()) { - common_user_error(_('Already logged in.')); - } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { - $this->check_login(); - } else { - $this->show_form(); - } - } + function handle($args) + { + parent::handle($args); + if (common_is_real_login()) { + common_user_error(_('Already logged in.')); + } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $this->check_login(); + } else { + $this->show_form(); + } + } - function check_login() { - # XXX: login throttle + function check_login() + { + # XXX: login throttle - # CSRF protection - token set in common_notice_form() - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->client_error(_('There was a problem with your session token. Try again, please.')); - return; - } + # CSRF protection - token set in common_notice_form() + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->client_error(_('There was a problem with your session token. Try again, please.')); + return; + } - $nickname = common_canonical_nickname($this->trimmed('nickname')); - $password = $this->arg('password'); - if (common_check_user($nickname, $password)) { - # success! - if (!common_set_user($nickname)) { - common_server_error(_('Error setting user.')); - return; - } - common_real_login(true); - if ($this->boolean('rememberme')) { - common_debug('Adding rememberme cookie for ' . $nickname); - common_rememberme(); - } - # success! - $url = common_get_returnto(); - if ($url) { - # We don't have to return to it again - common_set_returnto(NULL); - } else { - $url = common_local_url('all', - array('nickname' => - $nickname)); - } - common_redirect($url); - } else { - $this->show_form(_('Incorrect username or password.')); - return; - } + $nickname = common_canonical_nickname($this->trimmed('nickname')); + $password = $this->arg('password'); + if (common_check_user($nickname, $password)) { + # success! + if (!common_set_user($nickname)) { + common_server_error(_('Error setting user.')); + return; + } + common_real_login(true); + if ($this->boolean('rememberme')) { + common_debug('Adding rememberme cookie for ' . $nickname); + common_rememberme(); + } + # success! + $url = common_get_returnto(); + if ($url) { + # We don't have to return to it again + common_set_returnto(null); + } else { + $url = common_local_url('all', + array('nickname' => + $nickname)); + } + common_redirect($url); + } else { + $this->show_form(_('Incorrect username or password.')); + return; + } - # success! - if (!common_set_user($user)) { - common_server_error(_('Error setting user.')); - return; - } + # success! + if (!common_set_user($user)) { + common_server_error(_('Error setting user.')); + return; + } - common_real_login(true); + common_real_login(true); - if ($this->boolean('rememberme')) { - common_debug('Adding rememberme cookie for ' . $nickname); - common_rememberme($user); - } - # success! - $url = common_get_returnto(); - if ($url) { - # We don't have to return to it again - common_set_returnto(NULL); - } else { - $url = common_local_url('all', - array('nickname' => - $nickname)); - } - common_redirect($url); - } + if ($this->boolean('rememberme')) { + common_debug('Adding rememberme cookie for ' . $nickname); + common_rememberme($user); + } + # success! + $url = common_get_returnto(); + if ($url) { + # We don't have to return to it again + common_set_returnto(null); + } else { + $url = common_local_url('all', + array('nickname' => + $nickname)); + } + common_redirect($url); + } - function show_form($error=NULL) { - common_show_header(_('Login'), NULL, $error, array($this, 'show_top')); - common_element_start('form', array('method' => 'post', - 'id' => 'login', - 'action' => common_local_url('login'))); - common_input('nickname', _('Nickname')); - common_password('password', _('Password')); - common_checkbox('rememberme', _('Remember me'), false, - _('Automatically login in the future; ' . - 'not for shared computers!')); - common_submit('submit', _('Login')); - common_hidden('token', common_session_token()); - common_element_end('form'); - common_element_start('p'); - common_element('a', array('href' => common_local_url('recoverpassword')), - _('Lost or forgotten password?')); - common_element_end('p'); - common_show_footer(); - } + function show_form($error=null) + { + common_show_header(_('Login'), null, $error, array($this, 'show_top')); + common_element_start('form', array('method' => 'post', + 'id' => 'login', + 'action' => common_local_url('login'))); + common_input('nickname', _('Nickname')); + common_password('password', _('Password')); + common_checkbox('rememberme', _('Remember me'), false, + _('Automatically login in the future; ' . + 'not for shared computers!')); + common_submit('submit', _('Login')); + common_hidden('token', common_session_token()); + common_element_end('form'); + common_element_start('p'); + common_element('a', array('href' => common_local_url('recoverpassword')), + _('Lost or forgotten password?')); + common_element_end('p'); + common_show_footer(); + } - function get_instructions() { - if (common_logged_in() && - !common_is_real_login() && - common_get_returnto()) - { - # rememberme logins have to reauthenticate before - # changing any profile settings (cookie-stealing protection) - return _('For security reasons, please re-enter your ' . - 'user name and password ' . - 'before changing your settings.'); - } else { - return _('Login with your username and password. ' . - 'Don\'t have a username yet? ' . - '[Register](%%action.register%%) a new account, or ' . - 'try [OpenID](%%action.openidlogin%%). '); - } - } + function get_instructions() + { + if (common_logged_in() && + !common_is_real_login() && + common_get_returnto()) + { + # rememberme logins have to reauthenticate before + # changing any profile settings (cookie-stealing protection) + return _('For security reasons, please re-enter your ' . + 'user name and password ' . + 'before changing your settings.'); + } else { + return _('Login with your username and password. ' . + 'Don\'t have a username yet? ' . + '[Register](%%action.register%%) a new account, or ' . + 'try [OpenID](%%action.openidlogin%%). '); + } + } - function show_top($error=NULL) { - if ($error) { - common_element('p', 'error', $error); - } else { - $instr = $this->get_instructions(); - $output = common_markup_to_html($instr); - common_element_start('div', 'instructions'); - common_raw($output); - common_element_end('div'); - } - } + function show_top($error=null) + { + if ($error) { + common_element('p', 'error', $error); + } else { + $instr = $this->get_instructions(); + $output = common_markup_to_html($instr); + common_element_start('div', 'instructions'); + common_raw($output); + common_element_end('div'); + } + } } diff --git a/actions/logout.php b/actions/logout.php index f00fa0ba7..201378730 100644 --- a/actions/logout.php +++ b/actions/logout.php @@ -21,21 +21,24 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/openid.php'); -class LogoutAction extends Action { - - function is_readonly() { - return true; - } - - function handle($args) { - parent::handle($args); - if (!common_logged_in()) { - common_user_error(_('Not logged in.')); - } else { - common_set_user(NULL); - common_real_login(false); # not logged in - common_forgetme(); # don't log back in! - common_redirect(common_local_url('public')); - } - } +class LogoutAction extends Action +{ + + function is_readonly() + { + return true; + } + + function handle($args) + { + parent::handle($args); + if (!common_logged_in()) { + common_user_error(_('Not logged in.')); + } else { + common_set_user(null); + common_real_login(false); # not logged in + common_forgetme(); # don't log back in! + common_redirect(common_local_url('public')); + } + } } diff --git a/actions/microsummary.php b/actions/microsummary.php index 104467d29..13ddc4e3e 100644 --- a/actions/microsummary.php +++ b/actions/microsummary.php @@ -19,28 +19,30 @@ if (!defined('LACONICA')) { exit(1); } -class MicrosummaryAction extends Action { +class MicrosummaryAction extends Action +{ - function handle($args) { + function handle($args) + { - parent::handle($args); + parent::handle($args); - $nickname = common_canonical_nickname($this->arg('nickname')); - $user = User::staticGet('nickname', $nickname); + $nickname = common_canonical_nickname($this->arg('nickname')); + $user = User::staticGet('nickname', $nickname); - if (!$user) { - $this->client_error(_('No such user'), 404); - return; - } - - $notice = $user->getCurrentNotice(); - - if (!$notice) { - $this->client_error(_('No current status'), 404); - } - - header('Content-Type: text/plain'); - - print $user->nickname . ': ' . $notice->content; - } + if (!$user) { + $this->client_error(_('No such user'), 404); + return; + } + + $notice = $user->getCurrentNotice(); + + if (!$notice) { + $this->client_error(_('No current status'), 404); + } + + header('Content-Type: text/plain'); + + print $user->nickname . ': ' . $notice->content; + } } diff --git a/actions/newmessage.php b/actions/newmessage.php index da48fc7e7..27fa9d518 100644 --- a/actions/newmessage.php +++ b/actions/newmessage.php @@ -19,117 +19,123 @@ if (!defined('LACONICA')) { exit(1); } -class NewmessageAction extends Action { - - function handle($args) { - parent::handle($args); - - if (!common_logged_in()) { - $this->client_error(_('Not logged in.'), 403); - } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { - $this->save_new_message(); - } else { - $this->show_form(); - } - } - - function save_new_message() { - $user = common_current_user(); - assert($user); # XXX: maybe an error instead... - - # CSRF protection - - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->show_form(_('There was a problem with your session token. Try again, please.')); - return; - } - - $content = $this->trimmed('content'); - $to = $this->trimmed('to'); - - if (!$content) { - $this->show_form(_('No content!')); - return; - } else { - $content_shortened = common_shorten_links($content); - - if (mb_strlen($content_shortened) > 140) { - common_debug("Content = '$content_shortened'", __FILE__); - common_debug("mb_strlen(\$content) = " . mb_strlen($content_shortened), __FILE__); - $this->show_form(_('That\'s too long. Max message size is 140 chars.')); - return; - } - } - - $other = User::staticGet('id', $to); - - if (!$other) { - $this->show_form(_('No recipient specified.')); - return; - } else if (!$user->mutuallySubscribed($other)) { - $this->client_error(_('You can\'t send a message to this user.'), 404); - return; - } else if ($user->id == $other->id) { - $this->client_error(_('Don\'t send a message to yourself; just say it to yourself quietly instead.'), 403); - return; - } - - $message = Message::saveNew($user->id, $other->id, $content, 'web'); - - if (is_string($message)) { - $this->show_form($message); - return; - } - - $this->notify($user, $other, $message); - - $url = common_local_url('outbox', array('nickname' => $user->nickname)); - - common_redirect($url, 303); - } - - function show_top($params) { - - list($content, $user, $to) = $params; - - assert(!is_null($user)); - - common_message_form($content, $user, $to); - } - - function show_form($msg=NULL) { - - $content = $this->trimmed('content'); - $user = common_current_user(); - - $to = $this->trimmed('to'); - - $other = User::staticGet('id', $to); - - if (!$other) { - $this->client_error(_('No such user'), 404); - return; - } - - if (!$user->mutuallySubscribed($other)) { - $this->client_error(_('You can\'t send a message to this user.'), 404); - return; - } - - common_show_header(_('New message'), NULL, - array($content, $user, $other), - array($this, 'show_top')); - - if ($msg) { - common_element('p', array('id'=>'error'), $msg); - } - - common_show_footer(); - } - - function notify($from, $to, $message) { - mail_notify_message($message, $from, $to); - # XXX: Jabber, SMS notifications... probably queued - } +class NewmessageAction extends Action +{ + + function handle($args) + { + parent::handle($args); + + if (!common_logged_in()) { + $this->client_error(_('Not logged in.'), 403); + } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $this->save_new_message(); + } else { + $this->show_form(); + } + } + + function save_new_message() + { + $user = common_current_user(); + assert($user); # XXX: maybe an error instead... + + # CSRF protection + + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->show_form(_('There was a problem with your session token. Try again, please.')); + return; + } + + $content = $this->trimmed('content'); + $to = $this->trimmed('to'); + + if (!$content) { + $this->show_form(_('No content!')); + return; + } else { + $content_shortened = common_shorten_links($content); + + if (mb_strlen($content_shortened) > 140) { + common_debug("Content = '$content_shortened'", __FILE__); + common_debug("mb_strlen(\$content) = " . mb_strlen($content_shortened), __FILE__); + $this->show_form(_('That\'s too long. Max message size is 140 chars.')); + return; + } + } + + $other = User::staticGet('id', $to); + + if (!$other) { + $this->show_form(_('No recipient specified.')); + return; + } else if (!$user->mutuallySubscribed($other)) { + $this->client_error(_('You can\'t send a message to this user.'), 404); + return; + } else if ($user->id == $other->id) { + $this->client_error(_('Don\'t send a message to yourself; just say it to yourself quietly instead.'), 403); + return; + } + + $message = Message::saveNew($user->id, $other->id, $content, 'web'); + + if (is_string($message)) { + $this->show_form($message); + return; + } + + $this->notify($user, $other, $message); + + $url = common_local_url('outbox', array('nickname' => $user->nickname)); + + common_redirect($url, 303); + } + + function show_top($params) + { + + list($content, $user, $to) = $params; + + assert(!is_null($user)); + + common_message_form($content, $user, $to); + } + + function show_form($msg=null) + { + + $content = $this->trimmed('content'); + $user = common_current_user(); + + $to = $this->trimmed('to'); + + $other = User::staticGet('id', $to); + + if (!$other) { + $this->client_error(_('No such user'), 404); + return; + } + + if (!$user->mutuallySubscribed($other)) { + $this->client_error(_('You can\'t send a message to this user.'), 404); + return; + } + + common_show_header(_('New message'), null, + array($content, $user, $other), + array($this, 'show_top')); + + if ($msg) { + common_element('p', array('id'=>'error'), $msg); + } + + common_show_footer(); + } + + function notify($from, $to, $message) + { + mail_notify_message($message, $from, $to); + # XXX: Jabber, SMS notifications... probably queued + } } diff --git a/actions/newnotice.php b/actions/newnotice.php index 42b48923f..c412e893d 100644 --- a/actions/newnotice.php +++ b/actions/newnotice.php @@ -10,145 +10,152 @@ * * 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 + * 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/>. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ if (!defined('LACONICA')) { exit(1); } require_once INSTALLDIR . '/lib/noticelist.php'; -class NewnoticeAction extends Action { - - function handle($args) { - parent::handle($args); - - if (!common_logged_in()) { - common_user_error(_('Not logged in.')); - } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { - - # CSRF protection - token set in common_notice_form() - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->client_error(_('There was a problem with your session token. Try again, please.')); - return; - } - - $this->save_new_notice(); - } else { - $this->show_form(); - } - } - - function save_new_notice() { - - $user = common_current_user(); - assert($user); # XXX: maybe an error instead... - $content = $this->trimmed('status_textarea'); - - if (!$content) { - $this->show_form(_('No content!')); - return; - } else { - $content_shortened = common_shorten_links($content); - - if (mb_strlen($content_shortened) > 140) { - common_debug("Content = '$content_shortened'", __FILE__); - common_debug("mb_strlen(\$content) = " . mb_strlen($content_shortened), __FILE__); - $this->show_form(_('That\'s too long. Max notice size is 140 chars.')); - return; - } - } - - $inter = new CommandInterpreter(); - - $cmd = $inter->handle_command($user, $content_shortened); - - if ($cmd) { - if ($this->boolean('ajax')) { - $cmd->execute(new AjaxWebChannel()); - } else { - $cmd->execute(new WebChannel()); - } - return; - } - - $replyto = $this->trimmed('inreplyto'); - - $notice = Notice::saveNew($user->id, $content, 'web', 1, ($replyto == 'false') ? NULL : $replyto); - - if (is_string($notice)) { - $this->show_form($notice); - return; - } - - common_broadcast_notice($notice); - - if ($this->boolean('ajax')) { - common_start_html('text/xml;charset=utf-8', true); - common_element_start('head'); - common_element('title', null, _('Notice posted')); - common_element_end('head'); - common_element_start('body'); - $this->show_notice($notice); - common_element_end('body'); - common_element_end('html'); - } else { - $returnto = $this->trimmed('returnto'); - - if ($returnto) { - $url = common_local_url($returnto, - array('nickname' => $user->nickname)); - } else { - $url = common_local_url('shownotice', - array('notice' => $notice->id)); - } - common_redirect($url, 303); - } - } - - function ajax_error_msg($msg) { - common_start_html('text/xml;charset=utf-8', true); - common_element_start('head'); - common_element('title', null, _('Ajax Error')); - common_element_end('head'); - common_element_start('body'); - common_element('p', array('id' => 'error'), $msg); - common_element_end('body'); - common_element_end('html'); - } - - function show_top($content=NULL) { - common_notice_form(NULL, $content); - } - - function show_form($msg=NULL) { - if ($msg && $this->boolean('ajax')) { - $this->ajax_error_msg($msg); - return; - } - $content = $this->trimmed('status_textarea'); - if (!$content) { - $replyto = $this->trimmed('replyto'); - $profile = Profile::staticGet('nickname', $replyto); - if ($profile) { - $content = '@' . $profile->nickname . ' '; - } - } - common_show_header(_('New notice'), NULL, $content, - array($this, 'show_top')); - if ($msg) { - common_element('p', array('id' => 'error'), $msg); - } - common_show_footer(); - } - - function show_notice($notice) { +class NewnoticeAction extends Action +{ + + function handle($args) + { + parent::handle($args); + + if (!common_logged_in()) { + common_user_error(_('Not logged in.')); + } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { + + # CSRF protection - token set in common_notice_form() + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->client_error(_('There was a problem with your session token. Try again, please.')); + return; + } + + $this->save_new_notice(); + } else { + $this->show_form(); + } + } + + function save_new_notice() + { + + $user = common_current_user(); + assert($user); # XXX: maybe an error instead... + $content = $this->trimmed('status_textarea'); + + if (!$content) { + $this->show_form(_('No content!')); + return; + } else { + $content_shortened = common_shorten_links($content); + + if (mb_strlen($content_shortened) > 140) { + common_debug("Content = '$content_shortened'", __FILE__); + common_debug("mb_strlen(\$content) = " . mb_strlen($content_shortened), __FILE__); + $this->show_form(_('That\'s too long. Max notice size is 140 chars.')); + return; + } + } + + $inter = new CommandInterpreter(); + + $cmd = $inter->handle_command($user, $content_shortened); + + if ($cmd) { + if ($this->boolean('ajax')) { + $cmd->execute(new AjaxWebChannel()); + } else { + $cmd->execute(new WebChannel()); + } + return; + } + + $replyto = $this->trimmed('inreplyto'); + + $notice = Notice::saveNew($user->id, $content, 'web', 1, ($replyto == 'false') ? null : $replyto); + + if (is_string($notice)) { + $this->show_form($notice); + return; + } + + common_broadcast_notice($notice); + + if ($this->boolean('ajax')) { + common_start_html('text/xml;charset=utf-8', true); + common_element_start('head'); + common_element('title', null, _('Notice posted')); + common_element_end('head'); + common_element_start('body'); + $this->show_notice($notice); + common_element_end('body'); + common_element_end('html'); + } else { + $returnto = $this->trimmed('returnto'); + + if ($returnto) { + $url = common_local_url($returnto, + array('nickname' => $user->nickname)); + } else { + $url = common_local_url('shownotice', + array('notice' => $notice->id)); + } + common_redirect($url, 303); + } + } + + function ajax_error_msg($msg) + { + common_start_html('text/xml;charset=utf-8', true); + common_element_start('head'); + common_element('title', null, _('Ajax Error')); + common_element_end('head'); + common_element_start('body'); + common_element('p', array('id' => 'error'), $msg); + common_element_end('body'); + common_element_end('html'); + } + + function show_top($content=null) + { + common_notice_form(null, $content); + } + + function show_form($msg=null) + { + if ($msg && $this->boolean('ajax')) { + $this->ajax_error_msg($msg); + return; + } + $content = $this->trimmed('status_textarea'); + if (!$content) { + $replyto = $this->trimmed('replyto'); + $profile = Profile::staticGet('nickname', $replyto); + if ($profile) { + $content = '@' . $profile->nickname . ' '; + } + } + common_show_header(_('New notice'), null, $content, + array($this, 'show_top')); + if ($msg) { + common_element('p', array('id' => 'error'), $msg); + } + common_show_footer(); + } + + function show_notice($notice) + { $nli = new NoticeListItem($notice); $nli->show(); - } + } } diff --git a/actions/noticesearch.php b/actions/noticesearch.php index 96e4d777f..b36fc8ad2 100644 --- a/actions/noticesearch.php +++ b/actions/noticesearch.php @@ -23,142 +23,149 @@ require_once(INSTALLDIR.'/lib/searchaction.php'); # XXX common parent for people and content search? -class NoticesearchAction extends SearchAction { +class NoticesearchAction extends SearchAction +{ - function get_instructions() { - return _('Search for notices on %%site.name%% by their contents. Separate search terms by spaces; they must be 3 characters or more.'); - } + function get_instructions() + { + return _('Search for notices on %%site.name%% by their contents. Separate search terms by spaces; they must be 3 characters or more.'); + } - function get_title() { - return _('Text search'); - } + function get_title() + { + return _('Text search'); + } - function show_results($q, $page) { + function show_results($q, $page) + { - $notice = new Notice(); + $notice = new Notice(); - # lcase it for comparison - $q = strtolower($q); + # lcase it for comparison + $q = strtolower($q); $search_engine = $notice->getSearchEngine('identica_notices'); $search_engine->set_sort_mode('chron'); - # Ask for an extra to see if there's more. - $search_engine->limit((($page-1)*NOTICES_PER_PAGE), NOTICES_PER_PAGE + 1); + # Ask for an extra to see if there's more. + $search_engine->limit((($page-1)*NOTICES_PER_PAGE), NOTICES_PER_PAGE + 1); if (false === $search_engine->query($q)) { $cnt = 0; } else { - $cnt = $notice->find(); + $cnt = $notice->find(); } - if ($cnt > 0) { - $terms = preg_split('/[\s,]+/', $q); - common_element_start('ul', array('id' => 'notices')); - for ($i = 0; $i < min($cnt, NOTICES_PER_PAGE); $i++) { - if ($notice->fetch()) { - $this->show_notice($notice, $terms); - } else { - // shouldn't happen! - break; - } - } - common_element_end('ul'); - } else { - common_element('p', 'error', _('No results')); - } - - common_pagination($page > 1, $cnt > NOTICES_PER_PAGE, - $page, 'noticesearch', array('q' => $q)); - } - - function show_header($arr) { - if ($arr) { - $q = $arr[0]; - } - if ($q) { - common_element('link', array('rel' => 'alternate', - 'href' => common_local_url('noticesearchrss', - array('q' => $q)), - 'type' => 'application/rss+xml', - 'title' => _('Search Stream Feed'))); - } - } - - # XXX: refactor and combine with StreamAction::show_notice() - - function show_notice($notice, $terms) { - $profile = $notice->getProfile(); - if (!$profile) { - common_log_db_error($notice, 'SELECT', __FILE__); - $this->server_error(_('Notice without matching profile')); - return; - } - # XXX: RDFa - common_element_start('li', array('class' => 'notice_single', - 'id' => 'notice-' . $notice->id)); - $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE); - common_element_start('a', array('href' => $profile->profileurl)); - common_element('img', array('src' => ($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_STREAM_SIZE), - 'class' => 'avatar stream', - 'width' => AVATAR_STREAM_SIZE, - 'height' => AVATAR_STREAM_SIZE, - 'alt' => - ($profile->fullname) ? $profile->fullname : - $profile->nickname)); - common_element_end('a'); - common_element('a', array('href' => $profile->profileurl, - 'class' => 'nickname'), - $profile->nickname); - # FIXME: URL, image, video, audio - common_element_start('p', array('class' => 'content')); - if ($notice->rendered) { - common_raw($this->highlight($notice->rendered, $terms)); - } else { - # XXX: may be some uncooked notices in the DB, - # we cook them right now. This should probably disappear in future - # versions (>> 0.4.x) - common_raw($this->highlight(common_render_content($notice->content, $notice), $terms)); - } - common_element_end('p'); - $noticeurl = common_local_url('shownotice', array('notice' => $notice->id)); - common_element_start('p', 'time'); - common_element('a', array('class' => 'permalink', - 'href' => $noticeurl, - 'title' => common_exact_date($notice->created)), - common_date_string($notice->created)); - if ($notice->reply_to) { - $replyurl = common_local_url('shownotice', array('notice' => $notice->reply_to)); - common_text(' ('); - common_element('a', array('class' => 'inreplyto', - 'href' => $replyurl), - _('in reply to...')); - common_text(')'); - } - common_element_start('a', - array('href' => common_local_url('newnotice', - array('replyto' => $profile->nickname)), - 'onclick' => 'doreply("'.$profile->nickname.'"); return false', - 'title' => _('reply'), - 'class' => 'replybutton')); - common_hidden('posttoken', common_session_token()); - - common_raw('→'); - common_element_end('a'); - common_element_end('p'); - common_element_end('li'); - } - - function highlight($text, $terms) { - /* Highligh serach terms */ - $pattern = '/('.implode('|',array_map('htmlspecialchars', $terms)).')/i'; - $result = preg_replace($pattern, '<strong>\\1</strong>', $text); - - /* Remove highlighting from inside links, loop incase multiple highlights in links */ - $pattern = '/(href="[^"]*)<strong>('.implode('|',array_map('htmlspecialchars', $terms)).')<\/strong>([^"]*")/iU'; - do { - $result = preg_replace($pattern, '\\1\\2\\3', $result, -1, $count); - } while ($count); - return $result; - } + if ($cnt > 0) { + $terms = preg_split('/[\s,]+/', $q); + common_element_start('ul', array('id' => 'notices')); + for ($i = 0; $i < min($cnt, NOTICES_PER_PAGE); $i++) { + if ($notice->fetch()) { + $this->show_notice($notice, $terms); + } else { + // shouldn't happen! + break; + } + } + common_element_end('ul'); + } else { + common_element('p', 'error', _('No results')); + } + + common_pagination($page > 1, $cnt > NOTICES_PER_PAGE, + $page, 'noticesearch', array('q' => $q)); + } + + function show_header($arr) + { + if ($arr) { + $q = $arr[0]; + } + if ($q) { + common_element('link', array('rel' => 'alternate', + 'href' => common_local_url('noticesearchrss', + array('q' => $q)), + 'type' => 'application/rss+xml', + 'title' => _('Search Stream Feed'))); + } + } + + # XXX: refactor and combine with StreamAction::show_notice() + + function show_notice($notice, $terms) + { + $profile = $notice->getProfile(); + if (!$profile) { + common_log_db_error($notice, 'SELECT', __FILE__); + $this->server_error(_('Notice without matching profile')); + return; + } + # XXX: RDFa + common_element_start('li', array('class' => 'notice_single', + 'id' => 'notice-' . $notice->id)); + $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE); + common_element_start('a', array('href' => $profile->profileurl)); + common_element('img', array('src' => ($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_STREAM_SIZE), + 'class' => 'avatar stream', + 'width' => AVATAR_STREAM_SIZE, + 'height' => AVATAR_STREAM_SIZE, + 'alt' => + ($profile->fullname) ? $profile->fullname : + $profile->nickname)); + common_element_end('a'); + common_element('a', array('href' => $profile->profileurl, + 'class' => 'nickname'), + $profile->nickname); + # FIXME: URL, image, video, audio + common_element_start('p', array('class' => 'content')); + if ($notice->rendered) { + common_raw($this->highlight($notice->rendered, $terms)); + } else { + # XXX: may be some uncooked notices in the DB, + # we cook them right now. This should probably disappear in future + # versions (>> 0.4.x) + common_raw($this->highlight(common_render_content($notice->content, $notice), $terms)); + } + common_element_end('p'); + $noticeurl = common_local_url('shownotice', array('notice' => $notice->id)); + common_element_start('p', 'time'); + common_element('a', array('class' => 'permalink', + 'href' => $noticeurl, + 'title' => common_exact_date($notice->created)), + common_date_string($notice->created)); + if ($notice->reply_to) { + $replyurl = common_local_url('shownotice', array('notice' => $notice->reply_to)); + common_text(' ('); + common_element('a', array('class' => 'inreplyto', + 'href' => $replyurl), + _('in reply to...')); + common_text(')'); + } + common_element_start('a', + array('href' => common_local_url('newnotice', + array('replyto' => $profile->nickname)), + 'onclick' => 'doreply("'.$profile->nickname.'"); return false', + 'title' => _('reply'), + 'class' => 'replybutton')); + common_hidden('posttoken', common_session_token()); + + common_raw('→'); + common_element_end('a'); + common_element_end('p'); + common_element_end('li'); + } + + function highlight($text, $terms) + { + /* Highligh serach terms */ + $pattern = '/('.implode('|',array_map('htmlspecialchars', $terms)).')/i'; + $result = preg_replace($pattern, '<strong>\\1</strong>', $text); + + /* Remove highlighting from inside links, loop incase multiple highlights in links */ + $pattern = '/(href="[^"]*)<strong>('.implode('|',array_map('htmlspecialchars', $terms)).')<\/strong>([^"]*")/iU'; + do { + $result = preg_replace($pattern, '\\1\\2\\3', $result, -1, $count); + } while ($count); + return $result; + } } diff --git a/actions/noticesearchrss.php b/actions/noticesearchrss.php index 0f38515a0..20fe0ff2a 100644 --- a/actions/noticesearchrss.php +++ b/actions/noticesearchrss.php @@ -23,48 +23,53 @@ require_once(INSTALLDIR.'/lib/rssaction.php'); // Formatting of RSS handled by Rss10Action -class NoticesearchrssAction extends Rss10Action { +class NoticesearchrssAction extends Rss10Action +{ - function init() { - return true; - } + function init() + { + return true; + } - function get_notices($limit=0) { + function get_notices($limit=0) + { - $q = $this->trimmed('q'); - $notices = array(); + $q = $this->trimmed('q'); + $notices = array(); - $notice = new Notice(); + $notice = new Notice(); - # lcase it for comparison - $q = strtolower($q); + # lcase it for comparison + $q = strtolower($q); $search_engine = $notice->getSearchEngine('identica_notices'); $search_engine->set_sort_mode('chron'); - if (!$limit) $limit = 20; + if (!$limit) $limit = 20; $search_engine->limit(0, $limit, true); $search_engine->query($q); - $notice->find(); + $notice->find(); - while ($notice->fetch()) { - $notices[] = clone($notice); - } + while ($notice->fetch()) { + $notices[] = clone($notice); + } - return $notices; - } + return $notices; + } - function get_channel() { - global $config; - $q = $this->trimmed('q'); - $c = array('url' => common_local_url('noticesearchrss', array('q' => $q)), - 'title' => $config['site']['name'] . sprintf(_(' Search Stream for "%s"'), $q), - 'link' => common_local_url('noticesearch', array('q' => $q)), - 'description' => sprintf(_('All updates matching search term "%s"'), $q)); - return $c; - } + function get_channel() + { + global $config; + $q = $this->trimmed('q'); + $c = array('url' => common_local_url('noticesearchrss', array('q' => $q)), + 'title' => $config['site']['name'] . sprintf(_(' Search Stream for "%s"'), $q), + 'link' => common_local_url('noticesearch', array('q' => $q)), + 'description' => sprintf(_('All updates matching search term "%s"'), $q)); + return $c; + } - function get_image() { - return NULL; - } + function get_image() + { + return null; + } } diff --git a/actions/nudge.php b/actions/nudge.php index 677f58800..a6480a582 100644 --- a/actions/nudge.php +++ b/actions/nudge.php @@ -21,64 +21,67 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/mail.php'); -class NudgeAction extends Action { +class NudgeAction extends Action +{ - function handle($args) { - parent::handle($args); + function handle($args) + { + parent::handle($args); - if (!common_logged_in()) { - $this->client_error(_('Not logged in.')); - return; - } + if (!common_logged_in()) { + $this->client_error(_('Not logged in.')); + return; + } - $user = common_current_user(); - $other = User::staticGet('nickname', $this->arg('nickname')); + $user = common_current_user(); + $other = User::staticGet('nickname', $this->arg('nickname')); - if ($_SERVER['REQUEST_METHOD'] != 'POST') { - common_redirect(common_local_url('showstream', array('nickname' => $other->nickname))); - return; - } + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + common_redirect(common_local_url('showstream', array('nickname' => $other->nickname))); + return; + } - # CSRF protection + # CSRF protection - $token = $this->trimmed('token'); - - if (!$token || $token != common_session_token()) { - $this->client_error(_('There was a problem with your session token. Try again, please.')); - return; - } + $token = $this->trimmed('token'); + + if (!$token || $token != common_session_token()) { + $this->client_error(_('There was a problem with your session token. Try again, please.')); + return; + } if (!$other->email || !$other->emailnotifynudge) { $this->client_error(_('This user doesn\'t allow nudges or hasn\'t confirmed or set his email yet.')); return; } - $this->notify($user, $other); + $this->notify($user, $other); - if ($this->boolean('ajax')) { - common_start_html('text/xml;charset=utf-8', true); - common_element_start('head'); - common_element('title', null, _('Nudge sent')); - common_element_end('head'); - common_element_start('body'); - common_nudge_response(); - common_element_end('body'); - common_element_end('html'); - } else { + if ($this->boolean('ajax')) { + common_start_html('text/xml;charset=utf-8', true); + common_element_start('head'); + common_element('title', null, _('Nudge sent')); + common_element_end('head'); + common_element_start('body'); + common_nudge_response(); + common_element_end('body'); + common_element_end('html'); + } else { // display a confirmation to the user - common_redirect(common_local_url('showstream', - array('nickname' => $other->nickname))); - } - } + common_redirect(common_local_url('showstream', + array('nickname' => $other->nickname))); + } + } - function notify($user, $other) { - if ($other->id != $user->id) { - if ($other->email && $other->emailnotifynudge) { - mail_notify_nudge($user, $other); - } - # XXX: notify by IM - # XXX: notify by SMS - } - } + function notify($user, $other) + { + if ($other->id != $user->id) { + if ($other->email && $other->emailnotifynudge) { + mail_notify_nudge($user, $other); + } + # XXX: notify by IM + # XXX: notify by SMS + } + } } diff --git a/actions/openidlogin.php b/actions/openidlogin.php index 1b289dbea..09679e372 100644 --- a/actions/openidlogin.php +++ b/actions/openidlogin.php @@ -21,72 +21,77 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/openid.php'); -class OpenidloginAction extends Action { +class OpenidloginAction extends Action +{ - function handle($args) { - parent::handle($args); - if (common_logged_in()) { - common_user_error(_('Already logged in.')); - } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { - $openid_url = $this->trimmed('openid_url'); + function handle($args) + { + parent::handle($args); + if (common_logged_in()) { + common_user_error(_('Already logged in.')); + } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $openid_url = $this->trimmed('openid_url'); - # CSRF protection - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->show_form(_('There was a problem with your session token. Try again, please.'), $openid_url); - return; - } + # CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->show_form(_('There was a problem with your session token. Try again, please.'), $openid_url); + return; + } - $rememberme = $this->boolean('rememberme'); - - common_ensure_session(); - - $_SESSION['openid_rememberme'] = $rememberme; - - $result = oid_authenticate($openid_url, - 'finishopenidlogin'); - - if (is_string($result)) { # error message - unset($_SESSION['openid_rememberme']); - $this->show_form($result, $openid_url); - } - } else { - $openid_url = oid_get_last(); - $this->show_form(NULL, $openid_url); - } - } + $rememberme = $this->boolean('rememberme'); + + common_ensure_session(); + + $_SESSION['openid_rememberme'] = $rememberme; + + $result = oid_authenticate($openid_url, + 'finishopenidlogin'); + + if (is_string($result)) { # error message + unset($_SESSION['openid_rememberme']); + $this->show_form($result, $openid_url); + } + } else { + $openid_url = oid_get_last(); + $this->show_form(null, $openid_url); + } + } - function get_instructions() { - return _('Login with an [OpenID](%%doc.openid%%) account.'); - } + function get_instructions() + { + return _('Login with an [OpenID](%%doc.openid%%) account.'); + } - function show_top($error=NULL) { - if ($error) { - common_element('div', array('class' => 'error'), $error); - } else { - $instr = $this->get_instructions(); - $output = common_markup_to_html($instr); - common_element_start('div', 'instructions'); - common_raw($output); - common_element_end('div'); - } - } + function show_top($error=null) + { + if ($error) { + common_element('div', array('class' => 'error'), $error); + } else { + $instr = $this->get_instructions(); + $output = common_markup_to_html($instr); + common_element_start('div', 'instructions'); + common_raw($output); + common_element_end('div'); + } + } - function show_form($error=NULL, $openid_url) { - common_show_header(_('OpenID Login'), NULL, $error, array($this, 'show_top')); - $formaction = common_local_url('openidlogin'); - common_element_start('form', array('method' => 'post', - 'id' => 'openidlogin', - 'action' => $formaction)); - common_hidden('token', common_session_token()); - common_input('openid_url', _('OpenID URL'), - $openid_url, - _('Your OpenID URL')); - common_checkbox('rememberme', _('Remember me'), false, - _('Automatically login in the future; ' . - 'not for shared computers!')); - common_submit('submit', _('Login')); - common_element_end('form'); - common_show_footer(); - } + function show_form($error=null, $openid_url) + { + common_show_header(_('OpenID Login'), null, $error, array($this, 'show_top')); + $formaction = common_local_url('openidlogin'); + common_element_start('form', array('method' => 'post', + 'id' => 'openidlogin', + 'action' => $formaction)); + common_hidden('token', common_session_token()); + common_input('openid_url', _('OpenID URL'), + $openid_url, + _('Your OpenID URL')); + common_checkbox('rememberme', _('Remember me'), false, + _('Automatically login in the future; ' . + 'not for shared computers!')); + common_submit('submit', _('Login')); + common_element_end('form'); + common_show_footer(); + } } diff --git a/actions/openidsettings.php b/actions/openidsettings.php index f539d111f..039236048 100644 --- a/actions/openidsettings.php +++ b/actions/openidsettings.php @@ -22,135 +22,140 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/settingsaction.php'); require_once(INSTALLDIR.'/lib/openid.php'); -class OpenidsettingsAction extends SettingsAction { - - function get_instructions() { - return _('[OpenID](%%doc.openid%%) lets you log into many sites ' . - ' with the same user account. '. - ' Manage your associated OpenIDs from here.'); - } - - function show_form($msg=NULL, $success=false) { - - $user = common_current_user(); - - $this->form_header(_('OpenID settings'), $msg, $success); - - common_element_start('form', array('method' => 'post', - 'id' => 'openidadd', - 'action' => - common_local_url('openidsettings'))); - common_hidden('token', common_session_token()); - common_element('h2', NULL, _('Add OpenID')); - common_element('p', NULL, - _('If you want to add an OpenID to your account, ' . - 'enter it in the box below and click "Add".')); - common_element_start('p'); - common_element('label', array('for' => 'openid_url'), - _('OpenID URL')); - common_element('input', array('name' => 'openid_url', - 'type' => 'text', - 'id' => 'openid_url')); - common_element('input', array('type' => 'submit', - 'id' => 'add', - 'name' => 'add', - 'class' => 'submit', - 'value' => _('Add'))); - common_element_end('p'); - common_element_end('form'); - - $oid = new User_openid(); - $oid->user_id = $user->id; - - $cnt = $oid->find(); - - if ($cnt > 0) { - - common_element('h2', NULL, _('Remove OpenID')); - - if ($cnt == 1 && !$user->password) { - - common_element('p', NULL, - _('Removing your only OpenID would make it impossible to log in! ' . - 'If you need to remove it, add another OpenID first.')); - - if ($oid->fetch()) { - common_element_start('p'); - common_element('a', array('href' => $oid->canonical), - $oid->display); - common_element_end('p'); - } - - } else { - - common_element('p', NULL, - _('You can remove an OpenID from your account '. - 'by clicking the button marked "Remove".')); - $idx = 0; - - while ($oid->fetch()) { - common_element_start('form', array('method' => 'POST', - 'id' => 'openiddelete' . $idx, - 'action' => - common_local_url('openidsettings'))); - common_element_start('p'); - common_hidden('token', common_session_token()); - common_element('a', array('href' => $oid->canonical), - $oid->display); - common_element('input', array('type' => 'hidden', - 'id' => 'openid_url'.$idx, - 'name' => 'openid_url', - 'value' => $oid->canonical)); - common_element('input', array('type' => 'submit', - 'id' => 'remove'.$idx, - 'name' => 'remove', - 'class' => 'submit', - 'value' => _('Remove'))); - common_element_end('p'); - common_element_end('form'); - $idx++; - } - } - } - - common_show_footer(); - } - - function handle_post() { - # CSRF protection - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->show_form(_('There was a problem with your session token. Try again, please.')); - return; - } - - if ($this->arg('add')) { - $result = oid_authenticate($this->trimmed('openid_url'), 'finishaddopenid'); - if (is_string($result)) { # error message - $this->show_form($result); - } - } else if ($this->arg('remove')) { - $this->remove_openid(); - } else { - $this->show_form(_('Something weird happened.')); - } - } - - function remove_openid() { - - $openid_url = $this->trimmed('openid_url'); - $oid = User_openid::staticGet('canonical', $openid_url); - if (!$oid) { - $this->show_form(_('No such OpenID.')); - return; - } - $cur = common_current_user(); - if (!$cur || $oid->user_id != $cur->id) { - $this->show_form(_('That OpenID does not belong to you.')); - return; - } - $oid->delete(); - $this->show_form(_('OpenID removed.'), true); - return; - } +class OpenidsettingsAction extends SettingsAction +{ + + function get_instructions() + { + return _('[OpenID](%%doc.openid%%) lets you log into many sites ' . + ' with the same user account. '. + ' Manage your associated OpenIDs from here.'); + } + + function show_form($msg=null, $success=false) + { + + $user = common_current_user(); + + $this->form_header(_('OpenID settings'), $msg, $success); + + common_element_start('form', array('method' => 'post', + 'id' => 'openidadd', + 'action' => + common_local_url('openidsettings'))); + common_hidden('token', common_session_token()); + common_element('h2', null, _('Add OpenID')); + common_element('p', null, + _('If you want to add an OpenID to your account, ' . + 'enter it in the box below and click "Add".')); + common_element_start('p'); + common_element('label', array('for' => 'openid_url'), + _('OpenID URL')); + common_element('input', array('name' => 'openid_url', + 'type' => 'text', + 'id' => 'openid_url')); + common_element('input', array('type' => 'submit', + 'id' => 'add', + 'name' => 'add', + 'class' => 'submit', + 'value' => _('Add'))); + common_element_end('p'); + common_element_end('form'); + + $oid = new User_openid(); + $oid->user_id = $user->id; + + $cnt = $oid->find(); + + if ($cnt > 0) { + + common_element('h2', null, _('Remove OpenID')); + + if ($cnt == 1 && !$user->password) { + + common_element('p', null, + _('Removing your only OpenID would make it impossible to log in! ' . + 'If you need to remove it, add another OpenID first.')); + + if ($oid->fetch()) { + common_element_start('p'); + common_element('a', array('href' => $oid->canonical), + $oid->display); + common_element_end('p'); + } + + } else { + + common_element('p', null, + _('You can remove an OpenID from your account '. + 'by clicking the button marked "Remove".')); + $idx = 0; + + while ($oid->fetch()) { + common_element_start('form', array('method' => 'POST', + 'id' => 'openiddelete' . $idx, + 'action' => + common_local_url('openidsettings'))); + common_element_start('p'); + common_hidden('token', common_session_token()); + common_element('a', array('href' => $oid->canonical), + $oid->display); + common_element('input', array('type' => 'hidden', + 'id' => 'openid_url'.$idx, + 'name' => 'openid_url', + 'value' => $oid->canonical)); + common_element('input', array('type' => 'submit', + 'id' => 'remove'.$idx, + 'name' => 'remove', + 'class' => 'submit', + 'value' => _('Remove'))); + common_element_end('p'); + common_element_end('form'); + $idx++; + } + } + } + + common_show_footer(); + } + + function handle_post() + { + # CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->show_form(_('There was a problem with your session token. Try again, please.')); + return; + } + + if ($this->arg('add')) { + $result = oid_authenticate($this->trimmed('openid_url'), 'finishaddopenid'); + if (is_string($result)) { # error message + $this->show_form($result); + } + } else if ($this->arg('remove')) { + $this->remove_openid(); + } else { + $this->show_form(_('Something weird happened.')); + } + } + + function remove_openid() + { + + $openid_url = $this->trimmed('openid_url'); + $oid = User_openid::staticGet('canonical', $openid_url); + if (!$oid) { + $this->show_form(_('No such OpenID.')); + return; + } + $cur = common_current_user(); + if (!$cur || $oid->user_id != $cur->id) { + $this->show_form(_('That OpenID does not belong to you.')); + return; + } + $oid->delete(); + $this->show_form(_('OpenID removed.'), true); + return; + } } diff --git a/actions/opensearch.php b/actions/opensearch.php index 0f366be4c..96691fa6f 100644 --- a/actions/opensearch.php +++ b/actions/opensearch.php @@ -19,41 +19,43 @@ if (!defined('LACONICA')) { exit(1); } -class OpensearchAction extends Action { - - function handle($args) { - - parent::handle($args); - - $type = $this->trimmed('type'); - - $short_name = ''; - if ($type == 'people') { - $type = 'peoplesearch'; - $short_name = _('People Search'); - } else { - $short_name = _('Notice Search'); - $type = 'noticesearch'; - } - - header('Content-Type: text/html'); - - common_start_xml(); - common_element_start('OpenSearchDescription', array('xmlns' => 'http://a9.com/-/spec/opensearch/1.1/')); - - $short_name = common_config('site', 'name').' '.$short_name; - common_element('ShortName', NULL, $short_name); - common_element('Contact', NULL, common_config('site', 'email')); - common_element('Url', array('type' => 'text/html', 'method' => 'get', - 'template' => str_replace('---', '{searchTerms}', common_local_url($type, array('q' => '---'))))); - common_element('Image', array('height' => 16, 'width' => 16, 'type' => 'image/vnd.microsoft.icon'), common_path('favicon.ico')); - common_element('Image', array('height' => 50, 'width' => 50, 'type' => 'image/png'), theme_path('logo.png')); - common_element('AdultContent', NULL, 'false'); - common_element('Language', NULL, common_language()); - common_element('OutputEncoding', NULL, 'UTF-8'); - common_element('InputEncoding', NULL, 'UTF-8'); - - common_element_end('OpenSearchDescription'); - common_end_xml(); - } +class OpensearchAction extends Action +{ + + function handle($args) + { + + parent::handle($args); + + $type = $this->trimmed('type'); + + $short_name = ''; + if ($type == 'people') { + $type = 'peoplesearch'; + $short_name = _('People Search'); + } else { + $short_name = _('Notice Search'); + $type = 'noticesearch'; + } + + header('Content-Type: text/html'); + + common_start_xml(); + common_element_start('OpenSearchDescription', array('xmlns' => 'http://a9.com/-/spec/opensearch/1.1/')); + + $short_name = common_config('site', 'name').' '.$short_name; + common_element('ShortName', null, $short_name); + common_element('Contact', null, common_config('site', 'email')); + common_element('Url', array('type' => 'text/html', 'method' => 'get', + 'template' => str_replace('---', '{searchTerms}', common_local_url($type, array('q' => '---'))))); + common_element('Image', array('height' => 16, 'width' => 16, 'type' => 'image/vnd.microsoft.icon'), common_path('favicon.ico')); + common_element('Image', array('height' => 50, 'width' => 50, 'type' => 'image/png'), theme_path('logo.png')); + common_element('AdultContent', null, 'false'); + common_element('Language', null, common_language()); + common_element('OutputEncoding', null, 'UTF-8'); + common_element('InputEncoding', null, 'UTF-8'); + + common_element_end('OpenSearchDescription'); + common_end_xml(); + } } diff --git a/actions/othersettings.php b/actions/othersettings.php index eccf90e91..c2f08934c 100644 --- a/actions/othersettings.php +++ b/actions/othersettings.php @@ -21,161 +21,168 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/settingsaction.php'); -class OthersettingsAction extends SettingsAction { +class OthersettingsAction extends SettingsAction +{ - function get_instructions() { - return _('Manage various other options.'); - } + function get_instructions() + { + return _('Manage various other options.'); + } - function show_form($msg=NULL, $success=false) { - $user = common_current_user(); + function show_form($msg=null, $success=false) + { + $user = common_current_user(); - $this->form_header(_('Other Settings'), $msg, $success); + $this->form_header(_('Other Settings'), $msg, $success); - common_element('h2', NULL, _('URL Auto-shortening')); - common_element_start('form', array('method' => 'post', - 'id' => 'othersettings', - 'action' => - common_local_url('othersettings'))); - common_hidden('token', common_session_token()); + common_element('h2', null, _('URL Auto-shortening')); + common_element_start('form', array('method' => 'post', + 'id' => 'othersettings', + 'action' => + common_local_url('othersettings'))); + common_hidden('token', common_session_token()); - $services = array( - '' => 'None', + $services = array( + '' => 'None', 'ur1.ca' => 'ur1.ca (free service)', '2tu.us' => '2tu.us (free service)', 'ptiturl.com' => 'ptiturl.com', 'bit.ly' => 'bit.ly', - 'tinyurl.com' => 'tinyurl.com', - 'is.gd' => 'is.gd', - 'snipr.com' => 'snipr.com', - 'metamark.net' => 'metamark.net' - ); + 'tinyurl.com' => 'tinyurl.com', + 'is.gd' => 'is.gd', + 'snipr.com' => 'snipr.com', + 'metamark.net' => 'metamark.net' + ); - common_dropdown('urlshorteningservice', _('Service'), $services, _('Automatic shortening service to use.'), FALSE, $user->urlshorteningservice); + common_dropdown('urlshorteningservice', _('Service'), $services, _('Automatic shortening service to use.'), false, $user->urlshorteningservice); - common_submit('save', _('Save')); + common_submit('save', _('Save')); - common_element_end('form'); + common_element_end('form'); -// common_element('h2', NULL, _('Delete my account')); -// $this->show_delete_form(); +// common_element('h2', null, _('Delete my account')); +// $this->show_delete_form(); - common_show_footer(); - } + common_show_footer(); + } - function show_feeds_list($feeds) { - common_element_start('div', array('class' => 'feedsdel')); - common_element('p', null, 'Feeds:'); - common_element_start('ul', array('class' => 'xoxo')); + function show_feeds_list($feeds) + { + common_element_start('div', array('class' => 'feedsdel')); + common_element('p', null, 'Feeds:'); + common_element_start('ul', array('class' => 'xoxo')); - foreach ($feeds as $key => $value) { - $this->common_feed_item($feeds[$key]); - } - common_element_end('ul'); - common_element_end('div'); - } + foreach ($feeds as $key => $value) { + $this->common_feed_item($feeds[$key]); + } + common_element_end('ul'); + common_element_end('div'); + } //TODO move to common.php (and retrace its origin) - function common_feed_item($feed) { + function common_feed_item($feed) + { $user = common_current_user(); - $nickname = $user->nickname; - - switch($feed['item']) { - case 'notices': default: - $feed_classname = $feed['type']; - $feed_mimetype = "application/".$feed['type']."+xml"; - $feed_title = "$nickname's ".$feed['version']." notice feed"; - $feed['textContent'] = "RSS"; - break; - - case 'foaf': - $feed_classname = "foaf"; - $feed_mimetype = "application/".$feed['type']."+xml"; - $feed_title = "$nickname's FOAF file"; - $feed['textContent'] = "FOAF"; - break; - } - common_element_start('li'); - common_element('a', array('href' => $feed['href'], - 'class' => $feed_classname, - 'type' => $feed_mimetype, - 'title' => $feed_title), - $feed['textContent']); - common_element_end('li'); - } - -// function show_delete_form() { -// $user = common_current_user(); + $nickname = $user->nickname; + + switch($feed['item']) { + case 'notices': default: + $feed_classname = $feed['type']; + $feed_mimetype = "application/".$feed['type']."+xml"; + $feed_title = "$nickname's ".$feed['version']." notice feed"; + $feed['textContent'] = "RSS"; + break; + + case 'foaf': + $feed_classname = "foaf"; + $feed_mimetype = "application/".$feed['type']."+xml"; + $feed_title = "$nickname's FOAF file"; + $feed['textContent'] = "FOAF"; + break; + } + common_element_start('li'); + common_element('a', array('href' => $feed['href'], + 'class' => $feed_classname, + 'type' => $feed_mimetype, + 'title' => $feed_title), + $feed['textContent']); + common_element_end('li'); + } + +// function show_delete_form() { +// $user = common_current_user(); // $notices = DB_DataObject::factory('notice'); // $notices->profile_id = $user->id; // $notice_count = (int) $notices->count(); // -// common_element_start('form', array('method' => 'POST', -// 'id' => 'delete', -// 'action' => -// common_local_url('deleteprofile'))); +// common_element_start('form', array('method' => 'POST', +// 'id' => 'delete', +// 'action' => +// common_local_url('deleteprofile'))); // -// common_hidden('token', common_session_token()); +// common_hidden('token', common_session_token()); // common_element('p', null, "You can copy your notices and contacts by saving the two links below before deleting your account. Be careful, this operation cannot be undone."); // -// $this->show_feeds_list(array(0=>array('href'=>common_local_url('userrss', array('limit' => $notice_count, 'nickname' => $user->nickname)), -// 'type' => 'rss', -// 'version' => 'RSS 1.0', -// 'item' => 'notices'), -// 1=>array('href'=>common_local_url('foaf',array('nickname' => $user->nickname)), -// 'type' => 'rdf', -// 'version' => 'FOAF', -// 'item' => 'foaf'))); +// $this->show_feeds_list(array(0=>array('href'=>common_local_url('userrss', array('limit' => $notice_count, 'nickname' => $user->nickname)), +// 'type' => 'rss', +// 'version' => 'RSS 1.0', +// 'item' => 'notices'), +// 1=>array('href'=>common_local_url('foaf',array('nickname' => $user->nickname)), +// 'type' => 'rdf', +// 'version' => 'FOAF', +// 'item' => 'foaf'))); // -// common_submit('deleteaccount', _('Delete my account')); -// common_element_end('form'); -// } +// common_submit('deleteaccount', _('Delete my account')); +// common_element_end('form'); +// } - function handle_post() { + function handle_post() + { - # CSRF protection - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->show_form(_('There was a problem with your session token. Try again, please.')); - return; - } + # CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->show_form(_('There was a problem with your session token. Try again, please.')); + return; + } - if ($this->arg('save')) { - $this->save_preferences(); - }else { - $this->show_form(_('Unexpected form submission.')); - } - } + if ($this->arg('save')) { + $this->save_preferences(); + }else { + $this->show_form(_('Unexpected form submission.')); + } + } - function save_preferences() { + function save_preferences() + { - $urlshorteningservice = $this->trimmed('urlshorteningservice'); + $urlshorteningservice = $this->trimmed('urlshorteningservice'); - if (!is_null($urlshorteningservice) && strlen($urlshorteningservice) > 50) { - $this->show_form(_('URL shortening service is too long (max 50 chars).')); - return; - } + if (!is_null($urlshorteningservice) && strlen($urlshorteningservice) > 50) { + $this->show_form(_('URL shortening service is too long (max 50 chars).')); + return; + } - $user = common_current_user(); + $user = common_current_user(); - assert(!is_null($user)); # should already be checked + assert(!is_null($user)); # should already be checked - $user->query('BEGIN'); + $user->query('BEGIN'); - $original = clone($user); + $original = clone($user); - $user->urlshorteningservice = $urlshorteningservice; + $user->urlshorteningservice = $urlshorteningservice; - $result = $user->update($original); + $result = $user->update($original); - if ($result === FALSE) { - common_log_db_error($user, 'UPDATE', __FILE__); - common_server_error(_('Couldn\'t update user.')); - return; - } + if ($result === false) { + common_log_db_error($user, 'UPDATE', __FILE__); + common_server_error(_('Couldn\'t update user.')); + return; + } - $user->query('COMMIT'); + $user->query('COMMIT'); - $this->show_form(_('Preferences saved.'), true); - } + $this->show_form(_('Preferences saved.'), true); + } } diff --git a/actions/outbox.php b/actions/outbox.php index c48d9c206..9fb6dbf9f 100644 --- a/actions/outbox.php +++ b/actions/outbox.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 + * action handler for message inbox + * + * 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,42 +18,108 @@ * * 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 Message + * @package Laconica + * @author Evan Prodromou <evan@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); +} + +require_once INSTALLDIR.'/lib/mailbox.php'; + +/** + * action handler for message outbox + * + * @category Message + * @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/ + * @see MailboxAction */ -if (!defined('LACONICA')) { exit(1); } - -require_once(INSTALLDIR.'/lib/mailbox.php'); - -class OutboxAction extends MailboxAction { - - function get_title($user, $page) { - if ($page > 1) { - $title = sprintf(_("Outbox for %s - page %d"), $user->nickname, $page); - } else { - $title = sprintf(_("Outbox for %s"), $user->nickname); - } - return $title; - } - - function get_messages($user, $page) { - $message = new Message(); - $message->from_profile = $user->id; - $message->orderBy('created DESC, id DESC'); - $message->limit((($page-1)*MESSAGES_PER_PAGE), MESSAGES_PER_PAGE + 1); - - if ($message->find()) { - return $message; - } else { - return NULL; - } - } - - function get_message_profile($message) { - return $message->getTo(); - } - - function get_instructions() { - return _('This is your outbox, which lists private messages you have sent.'); - } - +class OutboxAction extends MailboxAction +{ + /** + * returns the title of the page + * + * @param User $user current user + * @param int $page current page + * + * @return string localised title of the page + * + * @see MailboxAction::getTitle() + */ + + function getTitle($user, $page) + { + if ($page > 1) { + $title = sprintf(_("Outbox for %s - page %d"), $user->nickname, $page); + } else { + $title = sprintf(_("Outbox for %s"), $user->nickname); + } + return $title; + } + + /** + * retrieve the messages for this user and this page + * + * Does a query for the right messages + * + * @param User $user The current user + * @param int $page The page the user is on + * + * @return Message data object with stream for messages + * + * @see MailboxAction::getMessages() + */ + + function getMessages($user, $page) + { + $message = new Message(); + + $message->from_profile = $user->id; + $message->orderBy('created DESC, id DESC'); + $message->limit((($page-1)*MESSAGES_PER_PAGE), MESSAGES_PER_PAGE + 1); + + if ($message->find()) { + return $message; + } else { + return null; + } + } + + /** + * returns the profile we want to show with the message + * + * For outboxes, we show the recipient. + * + * @param Message $message The message to get the profile for + * + * @return Profile The profile of the message recipient + * + * @see MailboxAction::getMessageProfile() + */ + + function getMessageProfile($message) + { + return $message->getTo(); + } + + /** + * instructions for using this page + * + * @return string localised instructions for using the page + */ + + function getInstructions() + { + return _('This is your outbox, which lists private messages you have sent.'); + } } diff --git a/actions/peoplesearch.php b/actions/peoplesearch.php index 2e54233ec..0d0fae4e5 100644 --- a/actions/peoplesearch.php +++ b/actions/peoplesearch.php @@ -22,63 +22,70 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/searchaction.php'); require_once(INSTALLDIR.'/lib/profilelist.php'); -class PeoplesearchAction extends SearchAction { +class PeoplesearchAction extends SearchAction +{ - function get_instructions() { - return _('Search for people on %%site.name%% by their name, location, or interests. ' . - 'Separate the terms by spaces; they must be 3 characters or more.'); - } + function get_instructions() + { + return _('Search for people on %%site.name%% by their name, location, or interests. ' . + 'Separate the terms by spaces; they must be 3 characters or more.'); + } - function get_title() { - return _('People search'); - } + function get_title() + { + return _('People search'); + } - function show_results($q, $page) { + function show_results($q, $page) + { - $profile = new Profile(); + $profile = new Profile(); - # lcase it for comparison - $q = strtolower($q); + # lcase it for comparison + $q = strtolower($q); $search_engine = $profile->getSearchEngine('identica_people'); $search_engine->set_sort_mode('chron'); - # Ask for an extra to see if there's more. + # Ask for an extra to see if there's more. $search_engine->limit((($page-1)*PROFILES_PER_PAGE), PROFILES_PER_PAGE + 1); if (false === $search_engine->query($q)) { $cnt = 0; } else { - $cnt = $profile->find(); + $cnt = $profile->find(); + } + if ($cnt > 0) { + $terms = preg_split('/[\s,]+/', $q); + $results = new PeopleSearchResults($profile, $terms); + $results->show_list(); + } else { + common_element('p', 'error', _('No results')); } - if ($cnt > 0) { - $terms = preg_split('/[\s,]+/', $q); - $results = new PeopleSearchResults($profile, $terms); - $results->show_list(); - } else { - common_element('p', 'error', _('No results')); - } - $profile->free(); - - common_pagination($page > 1, $cnt > PROFILES_PER_PAGE, - $page, 'peoplesearch', array('q' => $q)); - } + $profile->free(); + + common_pagination($page > 1, $cnt > PROFILES_PER_PAGE, + $page, 'peoplesearch', array('q' => $q)); + } } -class PeopleSearchResults extends ProfileList { +class PeopleSearchResults extends ProfileList +{ - var $terms = NULL; - var $pattern = NULL; - - function __construct($profile, $terms) { - parent::__construct($profile); - $this->terms = array_map('preg_quote', - array_map('htmlspecialchars', $terms)); - $this->pattern = '/('.implode('|',$terms).')/i'; - } - - function highlight($text) { - return preg_replace($this->pattern, '<strong>\\1</strong>', htmlspecialchars($text)); - } + var $terms = null; + var $pattern = null; + + function __construct($profile, $terms) + { + parent::__construct($profile); + $this->terms = array_map('preg_quote', + array_map('htmlspecialchars', $terms)); + $this->pattern = '/('.implode('|',$terms).')/i'; + } + + function highlight($text) + { + return preg_replace($this->pattern, '<strong>\\1</strong>', htmlspecialchars($text)); + } } diff --git a/actions/peopletag.php b/actions/peopletag.php index c508e0594..13a0b7a41 100644 --- a/actions/peopletag.php +++ b/actions/peopletag.php @@ -21,83 +21,89 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/profilelist.php'); -class PeopletagAction extends Action { - - function handle($args) { +class PeopletagAction extends Action +{ + + function handle($args) + { - parent::handle($args); + parent::handle($args); - $tag = $this->trimmed('tag'); - - if (!common_valid_profile_tag($tag)) { - $this->client_error(sprintf(_('Not a valid people tag: %s'), $tag)); - return; - } + $tag = $this->trimmed('tag'); + + if (!common_valid_profile_tag($tag)) { + $this->client_error(sprintf(_('Not a valid people tag: %s'), $tag)); + return; + } - $page = $this->trimmed('page'); - - if (!$page) { - $page = 1; - } - - # Looks like we're good; show the header + $page = $this->trimmed('page'); + + if (!$page) { + $page = 1; + } + + # Looks like we're good; show the header - common_show_header(sprintf(_('Users self-tagged with %s - page %d'), $tag, $page), - NULL, $tag, array($this, 'show_top')); + common_show_header(sprintf(_('Users self-tagged with %s - page %d'), $tag, $page), + null, $tag, array($this, 'show_top')); - $this->show_people($tag, $page); + $this->show_people($tag, $page); - common_show_footer(); - } + common_show_footer(); + } - function show_people($tag, $page) { - - $profile = new Profile(); + function show_people($tag, $page) + { + + $profile = new Profile(); - $offset = ($page-1)*PROFILES_PER_PAGE; - $limit = PROFILES_PER_PAGE + 1; - - if (common_config('db','type') == 'pgsql') { - $lim = ' LIMIT ' . $limit . ' OFFSET ' . $offset; - } else { - $lim = ' LIMIT ' . $offset . ', ' . $limit; - } + $offset = ($page-1)*PROFILES_PER_PAGE; + $limit = PROFILES_PER_PAGE + 1; + + if (common_config('db','type') == 'pgsql') { + $lim = ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $lim = ' LIMIT ' . $offset . ', ' . $limit; + } - # XXX: memcached this - - $profile->query(sprintf('SELECT profile.* ' . - 'FROM profile JOIN profile_tag ' . - 'ON profile.id = profile_tag.tagger ' . - 'WHERE profile_tag.tagger = profile_tag.tagged ' . - 'AND tag = "%s" ' . - 'ORDER BY profile_tag.modified DESC ' . - $lim, $tag)); + # XXX: memcached this + + $profile->query(sprintf('SELECT profile.* ' . + 'FROM profile JOIN profile_tag ' . + 'ON profile.id = profile_tag.tagger ' . + 'WHERE profile_tag.tagger = profile_tag.tagged ' . + 'AND tag = "%s" ' . + 'ORDER BY profile_tag.modified DESC ' . + $lim, $tag)); - $pl = new ProfileList($profile); - $cnt = $pl->show_list(); - - common_pagination($page > 1, - $cnt > PROFILES_PER_PAGE, - $page, - $this->trimmed('action'), - array('tag' => $tag)); - } - - function show_top($tag) { - $instr = sprintf(_('These are users who have tagged themselves "%s" ' . - 'to show a common interest, characteristic, hobby or job.'), $tag); - common_element_start('div', 'instructions'); - common_element_start('p'); - common_text($instr); - common_element_end('p'); - common_element_end('div'); - } + $pl = new ProfileList($profile); + $cnt = $pl->show_list(); + + common_pagination($page > 1, + $cnt > PROFILES_PER_PAGE, + $page, + $this->trimmed('action'), + array('tag' => $tag)); + } + + function show_top($tag) + { + $instr = sprintf(_('These are users who have tagged themselves "%s" ' . + 'to show a common interest, characteristic, hobby or job.'), $tag); + common_element_start('div', 'instructions'); + common_element_start('p'); + common_text($instr); + common_element_end('p'); + common_element_end('div'); + } - function get_title() { - return NULL; - } + function get_title() + { + return null; + } - function show_header($arr) { - return; - } + function show_header($arr) + { + return; + } } diff --git a/actions/postnotice.php b/actions/postnotice.php index 243081f12..dec62a678 100644 --- a/actions/postnotice.php +++ b/actions/postnotice.php @@ -21,68 +21,71 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/omb.php'); -class PostnoticeAction extends Action { - function handle($args) { - parent::handle($args); - try { - common_remove_magic_from_request(); - $req = OAuthRequest::from_request(); - # Note: server-to-server function! - $server = omb_oauth_server(); - list($consumer, $token) = $server->verify_request($req); - if ($this->save_notice($req, $consumer, $token)) { - print "omb_version=".OMB_VERSION_01; - } - } catch (OAuthException $e) { - common_server_error($e->getMessage()); - return; - } - } +class PostnoticeAction extends Action +{ + function handle($args) + { + parent::handle($args); + try { + common_remove_magic_from_request(); + $req = OAuthRequest::from_request(); + # Note: server-to-server function! + $server = omb_oauth_server(); + list($consumer, $token) = $server->verify_request($req); + if ($this->save_notice($req, $consumer, $token)) { + print "omb_version=".OMB_VERSION_01; + } + } catch (OAuthException $e) { + common_server_error($e->getMessage()); + return; + } + } - function save_notice(&$req, &$consumer, &$token) { - $version = $req->get_parameter('omb_version'); - if ($version != OMB_VERSION_01) { - common_user_error(_('Unsupported OMB version'), 400); - return false; - } - # First, check to see - $listenee = $req->get_parameter('omb_listenee'); - $remote_profile = Remote_profile::staticGet('uri', $listenee); - if (!$remote_profile) { - common_user_error(_('Profile unknown'), 403); - return false; - } - $sub = Subscription::staticGet('token', $token->key); - if (!$sub) { - common_user_error(_('No such subscription'), 403); - return false; - } - $content = $req->get_parameter('omb_notice_content'); + function save_notice(&$req, &$consumer, &$token) + { + $version = $req->get_parameter('omb_version'); + if ($version != OMB_VERSION_01) { + common_user_error(_('Unsupported OMB version'), 400); + return false; + } + # First, check to see + $listenee = $req->get_parameter('omb_listenee'); + $remote_profile = Remote_profile::staticGet('uri', $listenee); + if (!$remote_profile) { + common_user_error(_('Profile unknown'), 403); + return false; + } + $sub = Subscription::staticGet('token', $token->key); + if (!$sub) { + common_user_error(_('No such subscription'), 403); + return false; + } + $content = $req->get_parameter('omb_notice_content'); $content_shortened = common_shorten_links($content); if (mb_strlen($content_shortened) > 140) { common_user_error(_('Invalid notice content'), 400); return false; } - $notice_uri = $req->get_parameter('omb_notice'); - if (!Validate::uri($notice_uri) && - !common_valid_tag($notice_uri)) { - common_user_error(_('Invalid notice uri'), 400); - return false; - } - $notice_url = $req->get_parameter('omb_notice_url'); - if ($notice_url && !common_valid_http_url($notice_url)) { - common_user_error(_('Invalid notice url'), 400); - return false; - } - $notice = Notice::staticGet('uri', $notice_uri); - if (!$notice) { - $notice = Notice::saveNew($remote_profile->id, $content, 'omb', false, 0, $notice_uri); - if (is_string($notice)) { - common_server_serror($notice, 500); - return false; - } - common_broadcast_notice($notice, true); - } - return true; - } + $notice_uri = $req->get_parameter('omb_notice'); + if (!Validate::uri($notice_uri) && + !common_valid_tag($notice_uri)) { + common_user_error(_('Invalid notice uri'), 400); + return false; + } + $notice_url = $req->get_parameter('omb_notice_url'); + if ($notice_url && !common_valid_http_url($notice_url)) { + common_user_error(_('Invalid notice url'), 400); + return false; + } + $notice = Notice::staticGet('uri', $notice_uri); + if (!$notice) { + $notice = Notice::saveNew($remote_profile->id, $content, 'omb', false, 0, $notice_uri); + if (is_string($notice)) { + common_server_serror($notice, 500); + return false; + } + common_broadcast_notice($notice, true); + } + return true; + } } diff --git a/actions/profilesettings.php b/actions/profilesettings.php index ed2623c9b..d861919b9 100644 --- a/actions/profilesettings.php +++ b/actions/profilesettings.php @@ -21,419 +21,464 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/settingsaction.php'); -class ProfilesettingsAction extends SettingsAction { - - function get_instructions() { - return _('You can update your personal profile info here '. - 'so people know more about you.'); - } - - function show_form($msg=NULL, $success=false) { - $this->form_header(_('Profile settings'), $msg, $success); - $this->show_settings_form(); - common_element('h2', NULL, _('Avatar')); - $this->show_avatar_form(); - common_element('h2', NULL, _('Change password')); - $this->show_password_form(); -// common_element('h2', NULL, _('Delete my account')); -// $this->show_delete_form(); - common_show_footer(); - } - - function handle_post() { - - # CSRF protection - - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->show_form(_('There was a problem with your session token. Try again, please.')); - return; - } - - if ($this->arg('save')) { - $this->save_profile(); - } else if ($this->arg('upload')) { - $this->upload_avatar(); - } else if ($this->arg('changepass')) { - $this->change_password(); - } - - } - - function show_settings_form() { - - $user = common_current_user(); - $profile = $user->getProfile(); - - common_element_start('form', array('method' => 'POST', - 'id' => 'profilesettings', - 'action' => - common_local_url('profilesettings'))); - common_hidden('token', common_session_token()); - - # too much common patterns here... abstractable? - - common_input('nickname', _('Nickname'), - ($this->arg('nickname')) ? $this->arg('nickname') : $profile->nickname, - _('1-64 lowercase letters or numbers, no punctuation or spaces')); - common_input('fullname', _('Full name'), - ($this->arg('fullname')) ? $this->arg('fullname') : $profile->fullname); - common_input('homepage', _('Homepage'), - ($this->arg('homepage')) ? $this->arg('homepage') : $profile->homepage, - _('URL of your homepage, blog, or profile on another site')); - common_textarea('bio', _('Bio'), - ($this->arg('bio')) ? $this->arg('bio') : $profile->bio, - _('Describe yourself and your interests in 140 chars')); - common_input('location', _('Location'), - ($this->arg('location')) ? $this->arg('location') : $profile->location, - _('Where you are, like "City, State (or Region), Country"')); - common_input('tags', _('Tags'), - ($this->arg('tags')) ? $this->arg('tags') : implode(' ', $user->getSelfTags()), - _('Tags for yourself (letters, numbers, -, ., and _), comma- or space- separated')); - - $language = common_language(); - common_dropdown('language', _('Language'), get_nice_language_list(), _('Preferred language'), TRUE, $language); - $timezone = common_timezone(); - $timezones = array(); - foreach(DateTimeZone::listIdentifiers() as $k => $v) { - $timezones[$v] = $v; - } - common_dropdown('timezone', _('Timezone'), $timezones, _('What timezone are you normally in?'), TRUE, $timezone); - - common_checkbox('autosubscribe', _('Automatically subscribe to whoever subscribes to me (best for non-humans)'), - ($this->arg('autosubscribe')) ? $this->boolean('autosubscribe') : $user->autosubscribe); - - common_submit('save', _('Save')); - - common_element_end('form'); - - - } - - function show_avatar_form() { - - $user = common_current_user(); - $profile = $user->getProfile(); - - if (!$profile) { - common_log_db_error($user, 'SELECT', __FILE__); - $this->server_error(_('User without matching profile')); - return; - } - - $original = $profile->getOriginalAvatar(); - - - common_element_start('form', array('enctype' => 'multipart/form-data', - 'method' => 'POST', - 'id' => 'avatar', - 'action' => - common_local_url('profilesettings'))); - common_hidden('token', common_session_token()); - - if ($original) { - common_element('img', array('src' => $original->url, - 'class' => 'avatar original', - 'width' => $original->width, - 'height' => $original->height, - 'alt' => $user->nickname)); - } - - $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); - - if ($avatar) { - common_element('img', array('src' => $avatar->url, - 'class' => 'avatar profile', - 'width' => AVATAR_PROFILE_SIZE, - 'height' => AVATAR_PROFILE_SIZE, - 'alt' => $user->nickname)); - } - - - common_element('input', array('name' => 'MAX_FILE_SIZE', - 'type' => 'hidden', - 'id' => 'MAX_FILE_SIZE', - 'value' => MAX_AVATAR_SIZE)); - - common_element_start('p'); - - - common_element('input', array('name' => 'avatarfile', - 'type' => 'file', - 'id' => 'avatarfile')); - common_element_end('p'); - - common_submit('upload', _('Upload')); - common_element_end('form'); - - } - - function show_password_form() { - - $user = common_current_user(); - common_element_start('form', array('method' => 'POST', - 'id' => 'password', - 'action' => - common_local_url('profilesettings'))); - - common_hidden('token', common_session_token()); - - # Users who logged in with OpenID won't have a pwd - if ($user->password) { - common_password('oldpassword', _('Old password')); - } - common_password('newpassword', _('New password'), - _('6 or more characters')); - common_password('confirm', _('Confirm'), - _('same as password above')); - common_submit('changepass', _('Change')); - common_element_end('form'); - } - - function save_profile() { - $nickname = $this->trimmed('nickname'); - $fullname = $this->trimmed('fullname'); - $homepage = $this->trimmed('homepage'); - $bio = $this->trimmed('bio'); - $location = $this->trimmed('location'); - $autosubscribe = $this->boolean('autosubscribe'); - $language = $this->trimmed('language'); - $timezone = $this->trimmed('timezone'); - $tagstring = $this->trimmed('tags'); - - # Some validation - - if (!Validate::string($nickname, array('min_length' => 1, - 'max_length' => 64, - 'format' => VALIDATE_NUM . VALIDATE_ALPHA_LOWER))) { - $this->show_form(_('Nickname must have only lowercase letters and numbers and no spaces.')); - return; - } else if (!User::allowed_nickname($nickname)) { - $this->show_form(_('Not a valid nickname.')); - return; - } else if (!is_null($homepage) && (strlen($homepage) > 0) && - !Validate::uri($homepage, array('allowed_schemes' => array('http', 'https')))) { - $this->show_form(_('Homepage is not a valid URL.')); - return; - } else if (!is_null($fullname) && strlen($fullname) > 255) { - $this->show_form(_('Full name is too long (max 255 chars).')); - return; - } else if (!is_null($bio) && strlen($bio) > 140) { - $this->show_form(_('Bio is too long (max 140 chars).')); - return; - } else if (!is_null($location) && strlen($location) > 255) { - $this->show_form(_('Location is too long (max 255 chars).')); - return; - } else if (is_null($timezone) || !in_array($timezone, DateTimeZone::listIdentifiers())) { - $this->show_form(_('Timezone not selected.')); - return; - } else if ($this->nickname_exists($nickname)) { - $this->show_form(_('Nickname already in use. Try another one.')); - return; +class ProfilesettingsAction extends SettingsAction +{ + + function get_instructions() + { + return _('You can update your personal profile info here '. + 'so people know more about you.'); + } + + function show_form($msg=null, $success=false) + { + $this->form_header(_('Profile settings'), $msg, $success); + $this->show_settings_form(); + common_element('h2', null, _('Avatar')); + $this->show_avatar_form(); + common_element('h2', null, _('Change password')); + $this->show_password_form(); +// common_element('h2', null, _('Delete my account')); +// $this->show_delete_form(); + common_show_footer(); + } + + function handle_post() + { + + # CSRF protection + + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->show_form(_('There was a problem with your session token. Try again, please.')); + return; + } + + if ($this->arg('save')) { + $this->save_profile(); + } else if ($this->arg('upload')) { + $this->upload_avatar(); + } else if ($this->arg('crop')) { + $this->crop_avatar(); + } else if ($this->arg('changepass')) { + $this->change_password(); + } else { + $this->show_form(_('Unexpected form submission.')); + } + + } + + function show_settings_form() + { + + $user = common_current_user(); + $profile = $user->getProfile(); + + common_element_start('form', array('method' => 'POST', + 'id' => 'profilesettings', + 'action' => common_local_url('profilesettings'))); + common_hidden('token', common_session_token()); + + # too much common patterns here... abstractable? + + common_input('nickname', _('Nickname'), + ($this->arg('nickname')) ? $this->arg('nickname') : $profile->nickname, + _('1-64 lowercase letters or numbers, no punctuation or spaces')); + common_input('fullname', _('Full name'), + ($this->arg('fullname')) ? $this->arg('fullname') : $profile->fullname); + common_input('homepage', _('Homepage'), + ($this->arg('homepage')) ? $this->arg('homepage') : $profile->homepage, + _('URL of your homepage, blog, or profile on another site')); + common_textarea('bio', _('Bio'), + ($this->arg('bio')) ? $this->arg('bio') : $profile->bio, + _('Describe yourself and your interests in 140 chars')); + common_input('location', _('Location'), + ($this->arg('location')) ? $this->arg('location') : $profile->location, + _('Where you are, like "City, State (or Region), Country"')); + common_input('tags', _('Tags'), + ($this->arg('tags')) ? $this->arg('tags') : implode(' ', $user->getSelfTags()), + _('Tags for yourself (letters, numbers, -, ., and _), comma- or space- separated')); + + $language = common_language(); + common_dropdown('language', _('Language'), get_nice_language_list(), _('Preferred language'), true, $language); + $timezone = common_timezone(); + $timezones = array(); + foreach(DateTimeZone::listIdentifiers() as $k => $v) { + $timezones[$v] = $v; + } + common_dropdown('timezone', _('Timezone'), $timezones, _('What timezone are you normally in?'), true, $timezone); + + common_checkbox('autosubscribe', _('Automatically subscribe to whoever subscribes to me (best for non-humans)'), + ($this->arg('autosubscribe')) ? $this->boolean('autosubscribe') : $user->autosubscribe); + + common_submit('save', _('Save')); + + common_element_end('form'); + + } + + function show_avatar_form() + { + + $user = common_current_user(); + $profile = $user->getProfile(); + + if (!$profile) { + common_log_db_error($user, 'SELECT', __FILE__); + $this->server_error(_('User without matching profile')); + return; + } + + $original = $profile->getOriginalAvatar(); + + + common_element_start('form', array('enctype' => 'multipart/form-data', + 'method' => 'POST', + 'id' => 'avatar', + 'action' => + common_local_url('profilesettings'))); + common_hidden('token', common_session_token()); + + if ($original) { + common_element_start('div', array('id'=>'avatar_original', 'class'=>'avatar_view')); + common_element('h3', null, _("Original:")); + common_element_start('div', array('id'=>'avatar_original_view')); + common_element('img', array('src' => $original->url, + 'class' => 'avatar original', + 'width' => $original->width, + 'height' => $original->height, + 'alt' => $user->nickname)); + common_element_end('div'); + common_element_end('div'); + } + + $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); + + if ($avatar) { + common_element_start('div', array('id'=>'avatar_preview', 'class'=>'avatar_view')); + common_element('h3', null, _("Preview:")); + common_element_start('div', array('id'=>'avatar_preview_view')); + common_element('img', array('src' => $original->url,//$avatar->url, + 'class' => 'avatar profile', + 'width' => AVATAR_PROFILE_SIZE, + 'height' => AVATAR_PROFILE_SIZE, + 'alt' => $user->nickname)); + common_element_end('div'); + common_element_end('div'); + + foreach(array('avatar_crop_x', 'avatar_crop_y', 'avatar_crop_w', 'avatar_crop_h') as $crop_info) { + common_element('input', array('name' => $crop_info, + 'type' => 'hidden', + 'id' => $crop_info)); + } + common_submit('crop', _('Crop')); + } + + common_element('input', array('name' => 'MAX_FILE_SIZE', + 'type' => 'hidden', + 'id' => 'MAX_FILE_SIZE', + 'value' => MAX_AVATAR_SIZE)); + + common_element_start('p'); + + common_element('input', array('name' => 'avatarfile', + 'type' => 'file', + 'id' => 'avatarfile')); + common_element_end('p'); + + common_submit('upload', _('Upload')); + common_element_end('form'); + + } + + function show_password_form() + { + + $user = common_current_user(); + common_element_start('form', array('method' => 'POST', + 'id' => 'password', + 'action' => + common_local_url('profilesettings'))); + + common_hidden('token', common_session_token()); + + # Users who logged in with OpenID won't have a pwd + if ($user->password) { + common_password('oldpassword', _('Old password')); + } + common_password('newpassword', _('New password'), + _('6 or more characters')); + common_password('confirm', _('Confirm'), + _('same as password above')); + common_submit('changepass', _('Change')); + common_element_end('form'); + } + + function save_profile() + { + $nickname = $this->trimmed('nickname'); + $fullname = $this->trimmed('fullname'); + $homepage = $this->trimmed('homepage'); + $bio = $this->trimmed('bio'); + $location = $this->trimmed('location'); + $autosubscribe = $this->boolean('autosubscribe'); + $language = $this->trimmed('language'); + $timezone = $this->trimmed('timezone'); + $tagstring = $this->trimmed('tags'); + + # Some validation + + if (!Validate::string($nickname, array('min_length' => 1, + 'max_length' => 64, + 'format' => VALIDATE_NUM . VALIDATE_ALPHA_LOWER))) { + $this->show_form(_('Nickname must have only lowercase letters and numbers and no spaces.')); + return; + } else if (!User::allowed_nickname($nickname)) { + $this->show_form(_('Not a valid nickname.')); + return; + } else if (!is_null($homepage) && (strlen($homepage) > 0) && + !Validate::uri($homepage, array('allowed_schemes' => array('http', 'https')))) { + $this->show_form(_('Homepage is not a valid URL.')); + return; + } else if (!is_null($fullname) && strlen($fullname) > 255) { + $this->show_form(_('Full name is too long (max 255 chars).')); + return; + } else if (!is_null($bio) && strlen($bio) > 140) { + $this->show_form(_('Bio is too long (max 140 chars).')); + return; + } else if (!is_null($location) && strlen($location) > 255) { + $this->show_form(_('Location is too long (max 255 chars).')); + return; + } else if (is_null($timezone) || !in_array($timezone, DateTimeZone::listIdentifiers())) { + $this->show_form(_('Timezone not selected.')); + return; + } else if ($this->nickname_exists($nickname)) { + $this->show_form(_('Nickname already in use. Try another one.')); + return; } else if (!is_null($language) && strlen($language) > 50) { - $this->show_form(_('Language is too long (max 50 chars).')); - return; - } - - if ($tagstring) { - $tags = array_map('common_canonical_tag', preg_split('/[\s,]+/', $tagstring)); - } else { - $tags = array(); - } - - foreach ($tags as $tag) { - if (!common_valid_profile_tag($tag)) { - $this->show_form(sprintf(_('Invalid tag: "%s"'), $tag)); - return; - } - } - - $user = common_current_user(); - - $user->query('BEGIN'); - - if ($user->nickname != $nickname || - $user->language != $language || - $user->timezone != $timezone) { - - common_debug('Updating user nickname from ' . $user->nickname . ' to ' . $nickname, - __FILE__); - common_debug('Updating user language from ' . $user->language . ' to ' . $language, - __FILE__); - common_debug('Updating user timezone from ' . $user->timezone . ' to ' . $timezone, - __FILE__); - - $original = clone($user); - - $user->nickname = $nickname; - $user->language = $language; - $user->timezone = $timezone; - - $result = $user->updateKeys($original); - - if ($result === FALSE) { - common_log_db_error($user, 'UPDATE', __FILE__); - common_server_error(_('Couldn\'t update user.')); - return; - } else { - # Re-initialize language environment if it changed - common_init_language(); - } - } - - # XXX: XOR - - if ($user->autosubscribe ^ $autosubscribe) { - - $original = clone($user); - - $user->autosubscribe = $autosubscribe; - - $result = $user->update($original); - - if ($result === FALSE) { - common_log_db_error($user, 'UPDATE', __FILE__); - common_server_error(_('Couldn\'t update user for autosubscribe.')); - return; - } - } - - $profile = $user->getProfile(); - - $orig_profile = clone($profile); - - $profile->nickname = $user->nickname; - $profile->fullname = $fullname; - $profile->homepage = $homepage; - $profile->bio = $bio; - $profile->location = $location; - $profile->profileurl = common_profile_url($nickname); - - common_debug('Old profile: ' . common_log_objstring($orig_profile), __FILE__); - common_debug('New profile: ' . common_log_objstring($profile), __FILE__); - - $result = $profile->update($orig_profile); - - if (!$result) { - common_log_db_error($profile, 'UPDATE', __FILE__); - common_server_error(_('Couldn\'t save profile.')); - return; - } - - # Set the user tags - - $result = $user->setSelfTags($tags); - - if (!$result) { - common_server_error(_('Couldn\'t save tags.')); - return; - } - - $user->query('COMMIT'); - - common_broadcast_profile($profile); - - $this->show_form(_('Settings saved.'), TRUE); - } - - - function upload_avatar() { - switch ($_FILES['avatarfile']['error']) { - case UPLOAD_ERR_OK: # success, jump out - break; - case UPLOAD_ERR_INI_SIZE: - case UPLOAD_ERR_FORM_SIZE: - $this->show_form(_('That file is too big.')); - return; - case UPLOAD_ERR_PARTIAL: - @unlink($_FILES['avatarfile']['tmp_name']); - $this->show_form(_('Partial upload.')); - return; - default: - $this->show_form(_('System error uploading file.')); - return; - } - - $info = @getimagesize($_FILES['avatarfile']['tmp_name']); - - if (!$info) { - @unlink($_FILES['avatarfile']['tmp_name']); - $this->show_form(_('Not an image or corrupt file.')); - return; - } - - switch ($info[2]) { - case IMAGETYPE_GIF: - case IMAGETYPE_JPEG: - case IMAGETYPE_PNG: - break; - default: - $this->show_form(_('Unsupported image file format.')); - return; - } - - $user = common_current_user(); - $profile = $user->getProfile(); - - if ($profile->setOriginal($_FILES['avatarfile']['tmp_name'])) { - $this->show_form(_('Avatar updated.'), true); - } else { - $this->show_form(_('Failed updating avatar.')); - } - - @unlink($_FILES['avatarfile']['tmp_name']); - } - - function nickname_exists($nickname) { - $user = common_current_user(); - $other = User::staticGet('nickname', $nickname); - if (!$other) { - return false; - } else { - return $other->id != $user->id; - } - } - - function change_password() { - - $user = common_current_user(); - assert(!is_null($user)); # should already be checked - - # FIXME: scrub input - - $newpassword = $this->arg('newpassword'); - $confirm = $this->arg('confirm'); - $token = $this->arg('token'); - - if (0 != strcmp($newpassword, $confirm)) { - $this->show_form(_('Passwords don\'t match.')); - return; - } - - if ($user->password) { - $oldpassword = $this->arg('oldpassword'); - - if (!common_check_user($user->nickname, $oldpassword)) { - $this->show_form(_('Incorrect old password')); - return; - } - } - - $original = clone($user); - - $user->password = common_munge_password($newpassword, $user->id); - - $val = $user->validate(); - if ($val !== TRUE) { - $this->show_form(_('Error saving user; invalid.')); - return; - } - - if (!$user->update($original)) { - common_server_error(_('Can\'t save new password.')); - return; - } - - $this->show_form(_('Password saved.'), true); - } + $this->show_form(_('Language is too long (max 50 chars).')); + return; + } + + if ($tagstring) { + $tags = array_map('common_canonical_tag', preg_split('/[\s,]+/', $tagstring)); + } else { + $tags = array(); + } + + foreach ($tags as $tag) { + if (!common_valid_profile_tag($tag)) { + $this->show_form(sprintf(_('Invalid tag: "%s"'), $tag)); + return; + } + } + + $user = common_current_user(); + + $user->query('BEGIN'); + + if ($user->nickname != $nickname || + $user->language != $language || + $user->timezone != $timezone) { + + common_debug('Updating user nickname from ' . $user->nickname . ' to ' . $nickname, + __FILE__); + common_debug('Updating user language from ' . $user->language . ' to ' . $language, + __FILE__); + common_debug('Updating user timezone from ' . $user->timezone . ' to ' . $timezone, + __FILE__); + + $original = clone($user); + + $user->nickname = $nickname; + $user->language = $language; + $user->timezone = $timezone; + + $result = $user->updateKeys($original); + + if ($result === false) { + common_log_db_error($user, 'UPDATE', __FILE__); + common_server_error(_('Couldn\'t update user.')); + return; + } else { + # Re-initialize language environment if it changed + common_init_language(); + } + } + + # XXX: XOR + + if ($user->autosubscribe ^ $autosubscribe) { + + $original = clone($user); + + $user->autosubscribe = $autosubscribe; + + $result = $user->update($original); + + if ($result === false) { + common_log_db_error($user, 'UPDATE', __FILE__); + common_server_error(_('Couldn\'t update user for autosubscribe.')); + return; + } + } + + $profile = $user->getProfile(); + + $orig_profile = clone($profile); + + $profile->nickname = $user->nickname; + $profile->fullname = $fullname; + $profile->homepage = $homepage; + $profile->bio = $bio; + $profile->location = $location; + $profile->profileurl = common_profile_url($nickname); + + common_debug('Old profile: ' . common_log_objstring($orig_profile), __FILE__); + common_debug('New profile: ' . common_log_objstring($profile), __FILE__); + + $result = $profile->update($orig_profile); + + if (!$result) { + common_log_db_error($profile, 'UPDATE', __FILE__); + common_server_error(_('Couldn\'t save profile.')); + return; + } + + # Set the user tags + + $result = $user->setSelfTags($tags); + + if (!$result) { + common_server_error(_('Couldn\'t save tags.')); + return; + } + + $user->query('COMMIT'); + + common_broadcast_profile($profile); + + $this->show_form(_('Settings saved.'), true); + } + + + function upload_avatar() + { + switch ($_FILES['avatarfile']['error']) { + case UPLOAD_ERR_OK: # success, jump out + break; + case UPLOAD_ERR_INI_SIZE: + case UPLOAD_ERR_FORM_SIZE: + $this->show_form(_('That file is too big.')); + return; + case UPLOAD_ERR_PARTIAL: + @unlink($_FILES['avatarfile']['tmp_name']); + $this->show_form(_('Partial upload.')); + return; + default: + $this->show_form(_('System error uploading file.')); + return; + } + + $info = @getimagesize($_FILES['avatarfile']['tmp_name']); + + if (!$info) { + @unlink($_FILES['avatarfile']['tmp_name']); + $this->show_form(_('Not an image or corrupt file.')); + return; + } + + switch ($info[2]) { + case IMAGETYPE_GIF: + case IMAGETYPE_JPEG: + case IMAGETYPE_PNG: + break; + default: + $this->show_form(_('Unsupported image file format.')); + return; + } + + $user = common_current_user(); + $profile = $user->getProfile(); + + if ($profile->setOriginal($_FILES['avatarfile']['tmp_name'])) { + $this->show_form(_('Avatar updated.'), true); + } else { + $this->show_form(_('Failed updating avatar.')); + } + + @unlink($_FILES['avatarfile']['tmp_name']); + } + + function crop_avatar() { + + $user = common_current_user(); + $profile = $user->getProfile(); + + $x = $this->arg('avatar_crop_x'); + $y = $this->arg('avatar_crop_y'); + $w = $this->arg('avatar_crop_w'); + $h = $this->arg('avatar_crop_h'); + + if ($profile->crop_avatars($x, $y, $w, $h)) { + $this->show_form(_('Avatar updated.'), true); + } else { + $this->show_form(_('Failed updating avatar.')); + } + } + + function nickname_exists($nickname) + { + $user = common_current_user(); + $other = User::staticGet('nickname', $nickname); + if (!$other) { + return false; + } else { + return $other->id != $user->id; + } + } + + function change_password() + { + + $user = common_current_user(); + assert(!is_null($user)); # should already be checked + + # FIXME: scrub input + + $newpassword = $this->arg('newpassword'); + $confirm = $this->arg('confirm'); + $token = $this->arg('token'); + + if (0 != strcmp($newpassword, $confirm)) { + $this->show_form(_('Passwords don\'t match.')); + return; + } + + if ($user->password) { + $oldpassword = $this->arg('oldpassword'); + + if (!common_check_user($user->nickname, $oldpassword)) { + $this->show_form(_('Incorrect old password')); + return; + } + } + + $original = clone($user); + + $user->password = common_munge_password($newpassword, $user->id); + + $val = $user->validate(); + if ($val !== true) { + $this->show_form(_('Error saving user; invalid.')); + return; + } + + if (!$user->update($original)) { + common_server_error(_('Can\'t save new password.')); + return; + } + + $this->show_form(_('Password saved.'), true); + } } diff --git a/actions/public.php b/actions/public.php index 218f80194..039e885e6 100644 --- a/actions/public.php +++ b/actions/public.php @@ -21,79 +21,85 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/stream.php'); -class PublicAction extends StreamAction { - - function handle($args) { - parent::handle($args); - - $page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; - - header('X-XRDS-Location: '. common_local_url('publicxrds')); - - common_show_header(_('Public timeline'), - array($this, 'show_header'), NULL, - array($this, 'show_top')); - - # XXX: Public sidebar here? - - $this->show_notices($page); - - common_show_footer(); - } - - function show_top() { - if (common_logged_in()) { - common_notice_form('public'); - } else { - $instr = $this->get_instructions(); - $output = common_markup_to_html($instr); - common_element_start('div', 'instructions'); - common_raw($output); - common_element_end('div'); - } - - $this->public_views_menu(); - - $this->show_feeds_list(array(0=>array('href'=>common_local_url('publicrss'), - 'type' => 'rss', - 'version' => 'RSS 1.0', - 'item' => 'publicrss'), - 1=>array('href'=>common_local_url('publicatom'), - 'type' => 'atom', - 'version' => 'Atom 1.0', - 'item' => 'publicatom'))); - } - - function get_instructions() { - return _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' . - 'based on the Free Software [Laconica](http://laconi.ca/) tool. ' . - '[Join now](%%action.register%%) to share notices about yourself with friends, family, and colleagues! ([Read more](%%doc.help%%))'); - } - - function show_header() { - common_element('link', array('rel' => 'alternate', - 'href' => common_local_url('publicrss'), - 'type' => 'application/rss+xml', - 'title' => _('Public Stream Feed'))); - # for client side of OpenID authentication - common_element('meta', array('http-equiv' => 'X-XRDS-Location', - 'content' => common_local_url('publicxrds'))); - } - - function show_notices($page) { - - $cnt = 0; - $notice = Notice::publicStream(($page-1)*NOTICES_PER_PAGE, - NOTICES_PER_PAGE + 1); - - if (!$notice) { +class PublicAction extends StreamAction +{ + + function handle($args) + { + parent::handle($args); + + $page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; + + header('X-XRDS-Location: '. common_local_url('publicxrds')); + + common_show_header(_('Public timeline'), + array($this, 'show_header'), null, + array($this, 'show_top')); + + # XXX: Public sidebar here? + + $this->show_notices($page); + + common_show_footer(); + } + + function show_top() + { + if (common_logged_in()) { + common_notice_form('public'); + } else { + $instr = $this->get_instructions(); + $output = common_markup_to_html($instr); + common_element_start('div', 'instructions'); + common_raw($output); + common_element_end('div'); + } + + $this->public_views_menu(); + + $this->show_feeds_list(array(0=>array('href'=>common_local_url('publicrss'), + 'type' => 'rss', + 'version' => 'RSS 1.0', + 'item' => 'publicrss'), + 1=>array('href'=>common_local_url('publicatom'), + 'type' => 'atom', + 'version' => 'Atom 1.0', + 'item' => 'publicatom'))); + } + + function get_instructions() + { + return _('This is %%site.name%%, a [micro-blogging](http://en.wikipedia.org/wiki/Micro-blogging) service ' . + 'based on the Free Software [Laconica](http://laconi.ca/) tool. ' . + '[Join now](%%action.register%%) to share notices about yourself with friends, family, and colleagues! ([Read more](%%doc.help%%))'); + } + + function show_header() + { + common_element('link', array('rel' => 'alternate', + 'href' => common_local_url('publicrss'), + 'type' => 'application/rss+xml', + 'title' => _('Public Stream Feed'))); + # for client side of OpenID authentication + common_element('meta', array('http-equiv' => 'X-XRDS-Location', + 'content' => common_local_url('publicxrds'))); + } + + function show_notices($page) + { + + $cnt = 0; + $notice = Notice::publicStream(($page-1)*NOTICES_PER_PAGE, + NOTICES_PER_PAGE + 1); + + if (!$notice) { $this->server_error(_('Could not retrieve public stream.')); return; - } + } $cnt = $this->show_notice_list($notice); - common_pagination($page > 1, $cnt > NOTICES_PER_PAGE, - $page, 'public'); - } + common_pagination($page > 1, $cnt > NOTICES_PER_PAGE, + $page, 'public'); + } } diff --git a/actions/publicrss.php b/actions/publicrss.php index 1ab6a8be0..822bc2db7 100644 --- a/actions/publicrss.php +++ b/actions/publicrss.php @@ -23,35 +23,40 @@ require_once(INSTALLDIR.'/lib/rssaction.php'); // Formatting of RSS handled by Rss10Action -class PublicrssAction extends Rss10Action { - - function init() { - return true; - } - - function get_notices($limit=0) { - - $notices = array(); - - $notice = Notice::publicStream(0, ($limit == 0) ? 48 : $limit); - - while ($notice->fetch()) { - $notices[] = clone($notice); - } - - return $notices; - } - - function get_channel() { - global $config; - $c = array('url' => common_local_url('publicrss'), - 'title' => sprintf(_('%s Public Stream'), $config['site']['name']), - 'link' => common_local_url('public'), - 'description' => sprintf(_('All updates for %s'), $config['site']['name'])); - return $c; - } - - function get_image() { - return NULL; - } +class PublicrssAction extends Rss10Action +{ + + function init() + { + return true; + } + + function get_notices($limit=0) + { + + $notices = array(); + + $notice = Notice::publicStream(0, ($limit == 0) ? 48 : $limit); + + while ($notice->fetch()) { + $notices[] = clone($notice); + } + + return $notices; + } + + function get_channel() + { + global $config; + $c = array('url' => common_local_url('publicrss'), + 'title' => sprintf(_('%s Public Stream'), $config['site']['name']), + 'link' => common_local_url('public'), + 'description' => sprintf(_('All updates for %s'), $config['site']['name'])); + return $c; + } + + function get_image() + { + return null; + } }
\ No newline at end of file diff --git a/actions/publicxrds.php b/actions/publicxrds.php index 951434c87..3d731d79f 100644 --- a/actions/publicxrds.php +++ b/actions/publicxrds.php @@ -23,57 +23,61 @@ require_once(INSTALLDIR.'/lib/openid.php'); # XXX: factor out similarities with XrdsAction -class PublicxrdsAction extends Action { +class PublicxrdsAction extends Action +{ - function is_readonly() { - return true; - } + function is_readonly() + { + return true; + } - function handle($args) { + function handle($args) + { - parent::handle($args); + parent::handle($args); - header('Content-Type: application/xrds+xml'); + header('Content-Type: application/xrds+xml'); - common_start_xml(); - common_element_start('XRDS', array('xmlns' => 'xri://$xrds')); + common_start_xml(); + common_element_start('XRDS', array('xmlns' => 'xri://$xrds')); - common_element_start('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)', - 'xmlns:simple' => 'http://xrds-simple.net/core/1.0', - 'version' => '2.0')); + common_element_start('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)', + 'xmlns:simple' => 'http://xrds-simple.net/core/1.0', + 'version' => '2.0')); - common_element('Type', NULL, 'xri://$xrds*simple'); + common_element('Type', null, 'xri://$xrds*simple'); - foreach (array('finishopenidlogin', 'finishaddopenid', 'finishimmediate') as $finish) { - $this->show_service(Auth_OpenID_RP_RETURN_TO_URL_TYPE, - common_local_url($finish)); - } + foreach (array('finishopenidlogin', 'finishaddopenid', 'finishimmediate') as $finish) { + $this->show_service(Auth_OpenID_RP_RETURN_TO_URL_TYPE, + common_local_url($finish)); + } - common_element_end('XRD'); + common_element_end('XRD'); - common_element_end('XRDS'); - common_end_xml(); - } + common_element_end('XRDS'); + common_end_xml(); + } - function show_service($type, $uri, $params=NULL, $sigs=NULL, $localId=NULL) { - common_element_start('Service'); - if ($uri) { - common_element('URI', NULL, $uri); - } - common_element('Type', NULL, $type); - if ($params) { - foreach ($params as $param) { - common_element('Type', NULL, $param); - } - } - if ($sigs) { - foreach ($sigs as $sig) { - common_element('Type', NULL, $sig); - } - } - if ($localId) { - common_element('LocalID', NULL, $localId); - } - common_element_end('Service'); - } + function show_service($type, $uri, $params=null, $sigs=null, $localId=null) + { + common_element_start('Service'); + if ($uri) { + common_element('URI', null, $uri); + } + common_element('Type', null, $type); + if ($params) { + foreach ($params as $param) { + common_element('Type', null, $param); + } + } + if ($sigs) { + foreach ($sigs as $sig) { + common_element('Type', null, $sig); + } + } + if ($localId) { + common_element('LocalID', null, $localId); + } + common_element_end('Service'); + } }
\ No newline at end of file diff --git a/actions/recoverpassword.php b/actions/recoverpassword.php index 38c42f41d..bb6ef81d6 100644 --- a/actions/recoverpassword.php +++ b/actions/recoverpassword.php @@ -23,309 +23,321 @@ if (!defined('LACONICA')) { exit(1); } define(MAX_RECOVERY_TIME, 24 * 60 * 60); -class RecoverpasswordAction extends Action { +class RecoverpasswordAction extends Action +{ - function handle($args) { + function handle($args) + { parent::handle($args); if (common_logged_in()) { - $this->client_error(_('You are already logged in!')); + $this->client_error(_('You are already logged in!')); return; } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { - if ($this->arg('recover')) { - $this->recover_password(); + if ($this->arg('recover')) { + $this->recover_password(); } else if ($this->arg('reset')) { - $this->reset_password(); - } else { - $this->client_error(_('Unexpected form submission.')); - } - } else { - if ($this->trimmed('code')) { - $this->check_code(); - } else { - $this->show_form(); - } - } - } - - function check_code() { - - $code = $this->trimmed('code'); - $confirm = Confirm_address::staticGet('code', $code); - - if (!$confirm) { - $this->client_error(_('No such recovery code.')); - return; - } - if ($confirm->address_type != 'recover') { - $this->client_error(_('Not a recovery code.')); - return; - } - - $user = User::staticGet($confirm->user_id); - - if (!$user) { - $this->server_error(_('Recovery code for unknown user.')); - return; - } - - $touched = strtotime($confirm->modified); - $email = $confirm->address; - - # Burn this code - - $result = $confirm->delete(); - - if (!$result) { - common_log_db_error($confirm, 'DELETE', __FILE__); - common_server_error(_('Error with confirmation code.')); - return; - } - - # These should be reaped, but for now we just check mod time - # Note: it's still deleted; let's avoid a second attempt! - - if ((time() - $touched) > MAX_RECOVERY_TIME) { - common_log(LOG_WARNING, - 'Attempted redemption on recovery code ' . - 'that is ' . $touched . ' seconds old. '); - $this->client_error(_('This confirmation code is too old. ' . - 'Please start again.')); - return; - } - - # If we used an outstanding confirmation to send the email, - # it's been confirmed at this point. - - if (!$user->email) { - $orig = clone($user); - $user->email = $email; - $result = $user->updateKeys($orig); - if (!$result) { - common_log_db_error($user, 'UPDATE', __FILE__); - $this->server_error(_('Could not update user with confirmed email address.')); - return; - } - } - - # Success! - - $this->set_temp_user($user); - $this->show_password_form(); - } - - function set_temp_user(&$user) { - common_ensure_session(); - $_SESSION['tempuser'] = $user->id; - } - - function get_temp_user() { - common_ensure_session(); - $user_id = $_SESSION['tempuser']; - if ($user_id) { - $user = User::staticGet($user_id); - } - return $user; - } - - function clear_temp_user() { - common_ensure_session(); - unset($_SESSION['tempuser']); - } - - function show_top($msg=NULL) { - if ($msg) { + $this->reset_password(); + } else { + $this->client_error(_('Unexpected form submission.')); + } + } else { + if ($this->trimmed('code')) { + $this->check_code(); + } else { + $this->show_form(); + } + } + } + + function check_code() + { + + $code = $this->trimmed('code'); + $confirm = Confirm_address::staticGet('code', $code); + + if (!$confirm) { + $this->client_error(_('No such recovery code.')); + return; + } + if ($confirm->address_type != 'recover') { + $this->client_error(_('Not a recovery code.')); + return; + } + + $user = User::staticGet($confirm->user_id); + + if (!$user) { + $this->server_error(_('Recovery code for unknown user.')); + return; + } + + $touched = strtotime($confirm->modified); + $email = $confirm->address; + + # Burn this code + + $result = $confirm->delete(); + + if (!$result) { + common_log_db_error($confirm, 'DELETE', __FILE__); + common_server_error(_('Error with confirmation code.')); + return; + } + + # These should be reaped, but for now we just check mod time + # Note: it's still deleted; let's avoid a second attempt! + + if ((time() - $touched) > MAX_RECOVERY_TIME) { + common_log(LOG_WARNING, + 'Attempted redemption on recovery code ' . + 'that is ' . $touched . ' seconds old. '); + $this->client_error(_('This confirmation code is too old. ' . + 'Please start again.')); + return; + } + + # If we used an outstanding confirmation to send the email, + # it's been confirmed at this point. + + if (!$user->email) { + $orig = clone($user); + $user->email = $email; + $result = $user->updateKeys($orig); + if (!$result) { + common_log_db_error($user, 'UPDATE', __FILE__); + $this->server_error(_('Could not update user with confirmed email address.')); + return; + } + } + + # Success! + + $this->set_temp_user($user); + $this->show_password_form(); + } + + function set_temp_user(&$user) + { + common_ensure_session(); + $_SESSION['tempuser'] = $user->id; + } + + function get_temp_user() + { + common_ensure_session(); + $user_id = $_SESSION['tempuser']; + if ($user_id) { + $user = User::staticGet($user_id); + } + return $user; + } + + function clear_temp_user() + { + common_ensure_session(); + unset($_SESSION['tempuser']); + } + + function show_top($msg=null) + { + if ($msg) { common_element('div', 'error', $msg); - } else { - common_element_start('div', 'instructions'); - common_element('p', NULL, - _('If you\'ve forgotten or lost your' . - ' password, you can get a new one sent to' . - ' the email address you have stored ' . - ' in your account.')); - common_element_end('div'); - } - } - - function show_password_top($msg=NULL) { - if ($msg) { + } else { + common_element_start('div', 'instructions'); + common_element('p', null, + _('If you\'ve forgotten or lost your' . + ' password, you can get a new one sent to' . + ' the email address you have stored ' . + ' in your account.')); + common_element_end('div'); + } + } + + function show_password_top($msg=null) + { + if ($msg) { common_element('div', 'error', $msg); - } else { - common_element('div', 'instructions', - _('You\'ve been identified. Enter a ' . - ' new password below. ')); - } - } - - function show_form($msg=NULL) { - - common_show_header(_('Recover password'), NULL, - $msg, array($this, 'show_top')); - - common_element_start('form', array('method' => 'post', - 'id' => 'recoverpassword', - 'action' => common_local_url('recoverpassword'))); - common_input('nicknameoremail', _('Nickname or email'), - $this->trimmed('nicknameoremail'), - _('Your nickname on this server, ' . - 'or your registered email address.')); - common_submit('recover', _('Recover')); - common_element_end('form'); - common_show_footer(); - } - - function show_password_form($msg=NULL) { - - common_show_header(_('Reset password'), NULL, - $msg, array($this, 'show_password_top')); - - common_element_start('form', array('method' => 'post', - 'id' => 'recoverpassword', - 'action' => common_local_url('recoverpassword'))); - common_hidden('token', common_session_token()); - common_password('newpassword', _('New password'), - _('6 or more characters, and don\'t forget it!')); - common_password('confirm', _('Confirm'), - _('Same as password above')); - common_submit('reset', _('Reset')); - common_element_end('form'); - common_show_footer(); - } - - function recover_password() { - $nore = $this->trimmed('nicknameoremail'); - if (!$nore) { - $this->show_form(_('Enter a nickname or email address.')); - return; - } - - $user = User::staticGet('email', common_canonical_email($nore)); - - if (!$user) { - $user = User::staticGet('nickname', common_canonical_nickname($nore)); - } - - # See if it's an unconfirmed email address - - if (!$user) { - $confirm_email = Confirm_address::staticGet('address', common_canonical_email($nore)); - if ($confirm_email && $confirm_email->address_type == 'email') { - $user = User::staticGet($confirm_email->user_id); - } - } - - if (!$user) { - $this->show_form(_('No user with that email address or username.')); - return; - } - - # Try to get an unconfirmed email address if they used a user name - - if (!$user->email && !$confirm_email) { - $confirm_email = Confirm_address::staticGet('user_id', $user->id); - if ($confirm_email && $confirm_email->address_type != 'email') { - # Skip non-email confirmations - $confirm_email = NULL; - } - } - - if (!$user->email && !$confirm_email) { - $this->client_error(_('No registered email address for that user.')); - return; - } - - # Success! We have a valid user and a confirmed or unconfirmed email address - - $confirm = new Confirm_address(); - $confirm->code = common_confirmation_code(128); - $confirm->address_type = 'recover'; - $confirm->user_id = $user->id; - $confirm->address = (isset($user->email)) ? $user->email : $confirm_email->address; - - if (!$confirm->insert()) { - common_log_db_error($confirm, 'INSERT', __FILE__); - $this->server_error(_('Error saving address confirmation.')); - return; - } - - $body = "Hey, $user->nickname."; - $body .= "\n\n"; - $body .= 'Someone just asked for a new password ' . - 'for this account on ' . common_config('site', 'name') . '.'; - $body .= "\n\n"; - $body .= 'If it was you, and you want to confirm, use the URL below:'; - $body .= "\n\n"; - $body .= "\t".common_local_url('recoverpassword', - array('code' => $confirm->code)); - $body .= "\n\n"; - $body .= 'If not, just ignore this message.'; - $body .= "\n\n"; - $body .= 'Thanks for your time, '; - $body .= "\n"; - $body .= common_config('site', 'name'); - $body .= "\n"; - - mail_to_user($user, _('Password recovery requested'), $body, $confirm->address); - - common_show_header(_('Password recovery requested')); - common_element('p', NULL, - _('Instructions for recovering your password ' . - 'have been sent to the email address registered to your ' . - 'account.')); - common_show_footer(); - } - - function reset_password() { - - # CSRF protection - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->show_form(_('There was a problem with your session token. Try again, please.')); - return; - } - - $user = $this->get_temp_user(); - - if (!$user) { - $this->client_error(_('Unexpected password reset.')); - return; - } - - $newpassword = $this->trimmed('newpassword'); - $confirm = $this->trimmed('confirm'); - - if (!$newpassword || strlen($newpassword) < 6) { - $this->show_password_form(_('Password must be 6 chars or more.')); - return; - } - if ($newpassword != $confirm) { - $this->show_password_form(_('Password and confirmation do not match.')); - return; - } - - # OK, we're ready to go - - $original = clone($user); - - $user->password = common_munge_password($newpassword, $user->id); - - if (!$user->update($original)) { - common_log_db_error($user, 'UPDATE', __FILE__); - common_server_error(_('Can\'t save new password.')); - return; - } - - $this->clear_temp_user(); - - if (!common_set_user($user->nickname)) { - common_server_error(_('Error setting user.')); - return; - } - - common_real_login(true); - - common_show_header(_('Password saved.')); - common_element('p', NULL, _('New password successfully saved. ' . - 'You are now logged in.')); - common_show_footer(); - } + } else { + common_element('div', 'instructions', + _('You\'ve been identified. Enter a ' . + ' new password below. ')); + } + } + + function show_form($msg=null) + { + + common_show_header(_('Recover password'), null, + $msg, array($this, 'show_top')); + + common_element_start('form', array('method' => 'post', + 'id' => 'recoverpassword', + 'action' => common_local_url('recoverpassword'))); + common_input('nicknameoremail', _('Nickname or email'), + $this->trimmed('nicknameoremail'), + _('Your nickname on this server, ' . + 'or your registered email address.')); + common_submit('recover', _('Recover')); + common_element_end('form'); + common_show_footer(); + } + + function show_password_form($msg=null) + { + + common_show_header(_('Reset password'), null, + $msg, array($this, 'show_password_top')); + + common_element_start('form', array('method' => 'post', + 'id' => 'recoverpassword', + 'action' => common_local_url('recoverpassword'))); + common_hidden('token', common_session_token()); + common_password('newpassword', _('New password'), + _('6 or more characters, and don\'t forget it!')); + common_password('confirm', _('Confirm'), + _('Same as password above')); + common_submit('reset', _('Reset')); + common_element_end('form'); + common_show_footer(); + } + + function recover_password() + { + $nore = $this->trimmed('nicknameoremail'); + if (!$nore) { + $this->show_form(_('Enter a nickname or email address.')); + return; + } + + $user = User::staticGet('email', common_canonical_email($nore)); + + if (!$user) { + $user = User::staticGet('nickname', common_canonical_nickname($nore)); + } + + # See if it's an unconfirmed email address + + if (!$user) { + $confirm_email = Confirm_address::staticGet('address', common_canonical_email($nore)); + if ($confirm_email && $confirm_email->address_type == 'email') { + $user = User::staticGet($confirm_email->user_id); + } + } + + if (!$user) { + $this->show_form(_('No user with that email address or username.')); + return; + } + + # Try to get an unconfirmed email address if they used a user name + + if (!$user->email && !$confirm_email) { + $confirm_email = Confirm_address::staticGet('user_id', $user->id); + if ($confirm_email && $confirm_email->address_type != 'email') { + # Skip non-email confirmations + $confirm_email = null; + } + } + + if (!$user->email && !$confirm_email) { + $this->client_error(_('No registered email address for that user.')); + return; + } + + # Success! We have a valid user and a confirmed or unconfirmed email address + + $confirm = new Confirm_address(); + $confirm->code = common_confirmation_code(128); + $confirm->address_type = 'recover'; + $confirm->user_id = $user->id; + $confirm->address = (isset($user->email)) ? $user->email : $confirm_email->address; + + if (!$confirm->insert()) { + common_log_db_error($confirm, 'INSERT', __FILE__); + $this->server_error(_('Error saving address confirmation.')); + return; + } + + $body = "Hey, $user->nickname."; + $body .= "\n\n"; + $body .= 'Someone just asked for a new password ' . + 'for this account on ' . common_config('site', 'name') . '.'; + $body .= "\n\n"; + $body .= 'If it was you, and you want to confirm, use the URL below:'; + $body .= "\n\n"; + $body .= "\t".common_local_url('recoverpassword', + array('code' => $confirm->code)); + $body .= "\n\n"; + $body .= 'If not, just ignore this message.'; + $body .= "\n\n"; + $body .= 'Thanks for your time, '; + $body .= "\n"; + $body .= common_config('site', 'name'); + $body .= "\n"; + + mail_to_user($user, _('Password recovery requested'), $body, $confirm->address); + + common_show_header(_('Password recovery requested')); + common_element('p', null, + _('Instructions for recovering your password ' . + 'have been sent to the email address registered to your ' . + 'account.')); + common_show_footer(); + } + + function reset_password() + { + + # CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->show_form(_('There was a problem with your session token. Try again, please.')); + return; + } + + $user = $this->get_temp_user(); + + if (!$user) { + $this->client_error(_('Unexpected password reset.')); + return; + } + + $newpassword = $this->trimmed('newpassword'); + $confirm = $this->trimmed('confirm'); + + if (!$newpassword || strlen($newpassword) < 6) { + $this->show_password_form(_('Password must be 6 chars or more.')); + return; + } + if ($newpassword != $confirm) { + $this->show_password_form(_('Password and confirmation do not match.')); + return; + } + + # OK, we're ready to go + + $original = clone($user); + + $user->password = common_munge_password($newpassword, $user->id); + + if (!$user->update($original)) { + common_log_db_error($user, 'UPDATE', __FILE__); + common_server_error(_('Can\'t save new password.')); + return; + } + + $this->clear_temp_user(); + + if (!common_set_user($user->nickname)) { + common_server_error(_('Error setting user.')); + return; + } + + common_real_login(true); + + common_show_header(_('Password saved.')); + common_element('p', null, _('New password successfully saved. ' . + 'You are now logged in.')); + common_show_footer(); + } } diff --git a/actions/register.php b/actions/register.php index a22ffca28..c479816ef 100644 --- a/actions/register.php +++ b/actions/register.php @@ -19,244 +19,250 @@ if (!defined('LACONICA')) { exit(1); } -class RegisterAction extends Action { - - function handle($args) { - parent::handle($args); - - if (common_config('site', 'closed')) { - common_user_error(_('Registration not allowed.')); - } else if (common_logged_in()) { - common_user_error(_('Already logged in.')); - } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { - $this->try_register(); - } else { - $this->show_form(); - } - } - - function try_register() { - - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->show_form(_('There was a problem with your session token. Try again, please.')); - return; - } - - $nickname = $this->trimmed('nickname'); - $email = $this->trimmed('email'); - $fullname = $this->trimmed('fullname'); - $homepage = $this->trimmed('homepage'); - $bio = $this->trimmed('bio'); - $location = $this->trimmed('location'); - - # We don't trim these... whitespace is OK in a password! - - $password = $this->arg('password'); - $confirm = $this->arg('confirm'); - - # invitation code, if any - - $code = $this->trimmed('code'); - - if ($code) { - $invite = Invitation::staticGet($code); - } - - if (common_config('site', 'inviteonly') && !($code && $invite)) { - $this->client_error(_('Sorry, only invited people can register.')); - return; - } - - # Input scrubbing - - $nickname = common_canonical_nickname($nickname); - $email = common_canonical_email($email); - - if (!$this->boolean('license')) { - $this->show_form(_('You can\'t register if you don\'t agree to the license.')); - } else if ($email && !Validate::email($email, true)) { - $this->show_form(_('Not a valid email address.')); - } else if (!Validate::string($nickname, array('min_length' => 1, - 'max_length' => 64, - 'format' => VALIDATE_NUM . VALIDATE_ALPHA_LOWER))) { - $this->show_form(_('Nickname must have only lowercase letters and numbers and no spaces.')); - } else if ($this->nickname_exists($nickname)) { - $this->show_form(_('Nickname already in use. Try another one.')); - } else if (!User::allowed_nickname($nickname)) { - $this->show_form(_('Not a valid nickname.')); - } else if ($this->email_exists($email)) { - $this->show_form(_('Email address already exists.')); - } else if (!is_null($homepage) && (strlen($homepage) > 0) && - !Validate::uri($homepage, array('allowed_schemes' => array('http', 'https')))) { - $this->show_form(_('Homepage is not a valid URL.')); - return; - } else if (!is_null($fullname) && strlen($fullname) > 255) { - $this->show_form(_('Full name is too long (max 255 chars).')); - return; - } else if (!is_null($bio) && strlen($bio) > 140) { - $this->show_form(_('Bio is too long (max 140 chars).')); - return; - } else if (!is_null($location) && strlen($location) > 255) { - $this->show_form(_('Location is too long (max 255 chars).')); - return; - } else if (strlen($password) < 6) { - $this->show_form(_('Password must be 6 or more characters.')); - return; - } else if ($password != $confirm) { - $this->show_form(_('Passwords don\'t match.')); - } else if ($user = User::register(array('nickname' => $nickname, 'password' => $password, 'email' => $email, - 'fullname' => $fullname, 'homepage' => $homepage, 'bio' => $bio, - 'location' => $location, 'code' => $code))) { - if (!$user) { - $this->show_form(_('Invalid username or password.')); - return; - } - # success! - if (!common_set_user($user)) { - common_server_error(_('Error setting user.')); - return; - } - # this is a real login - common_real_login(true); - if ($this->boolean('rememberme')) { - common_debug('Adding rememberme cookie for ' . $nickname); - common_rememberme($user); - } - # Re-init language env in case it changed (not yet, but soon) - common_init_language(); - $this->show_success(); - } else { - $this->show_form(_('Invalid username or password.')); - } - } - - # checks if *CANONICAL* nickname exists - - function nickname_exists($nickname) { - $user = User::staticGet('nickname', $nickname); - return ($user !== false); - } - - # checks if *CANONICAL* email exists - - function email_exists($email) { - $email = common_canonical_email($email); - if (!$email || strlen($email) == 0) { - return false; - } - $user = User::staticGet('email', $email); - return ($user !== false); - } - - function show_top($error=NULL) { - if ($error) { - common_element('p', 'error', $error); - } else { - $instr = common_markup_to_html(_('With this form you can create a new account. ' . - 'You can then post notices and link up to friends and colleagues. '. - '(Have an [OpenID](http://openid.net/)? ' . - 'Try our [OpenID registration](%%action.openidlogin%%)!)')); - - common_element_start('div', 'instructions'); - common_raw($instr); - common_element_end('div'); - } - } - - function show_form($error=NULL) { - global $config; - - $code = $this->trimmed('code'); - - if ($code) { - $invite = Invitation::staticGet($code); - } - - if (common_config('site', 'inviteonly') && !($code && $invite)) { - $this->client_error(_('Sorry, only invited people can register.')); - return; - } - - common_show_header(_('Register'), NULL, $error, array($this, 'show_top')); - common_element_start('form', array('method' => 'post', - 'id' => 'login', - 'action' => common_local_url('register'))); - - common_hidden('token', common_session_token()); - - if ($code) { - common_hidden('code', $code); - } - - common_input('nickname', _('Nickname'), $this->trimmed('nickname'), - _('1-64 lowercase letters or numbers, no punctuation or spaces. Required.')); - common_password('password', _('Password'), - _('6 or more characters. Required.')); - common_password('confirm', _('Confirm'), - _('Same as password above. Required.')); - if ($invite && $invite->address_type == 'email') { - common_input('email', _('Email'), $invite->address, - _('Used only for updates, announcements, and password recovery')); - } else { - common_input('email', _('Email'), $this->trimmed('email'), - _('Used only for updates, announcements, and password recovery')); - } - common_input('fullname', _('Full name'), - $this->trimmed('fullname'), - _('Longer name, preferably your "real" name')); - common_input('homepage', _('Homepage'), - $this->trimmed('homepage'), - _('URL of your homepage, blog, or profile on another site')); - common_textarea('bio', _('Bio'), - $this->trimmed('bio'), - _('Describe yourself and your interests in 140 chars')); - common_input('location', _('Location'), - $this->trimmed('location'), - _('Where you are, like "City, State (or Region), Country"')); - common_checkbox('rememberme', _('Remember me'), - $this->boolean('rememberme'), - _('Automatically login in the future; not for shared computers!')); - common_element_start('p'); - $attrs = array('type' => 'checkbox', - 'id' => 'license', - 'name' => 'license', - 'value' => 'true'); - if ($this->boolean('license')) { - $attrs['checked'] = 'checked'; - } - common_element('input', $attrs); - common_text(_('My text and files are available under ')); - common_element('a', array('href' => $config['license']['url']), - $config['license']['title']); - common_text(_(' except this private data: password, email address, IM address, phone number.')); - common_element_end('p'); - common_submit('submit', _('Register')); - common_element_end('form'); - common_show_footer(); - } - - function show_success() { - $nickname = $this->arg('nickname'); - common_show_header(_('Registration successful')); - common_element_start('div', 'success'); - $instr = sprintf(_('Congratulations, %s! And welcome to %%%%site.name%%%%. From here, you may want to...'. "\n\n" . - '* Go to [your profile](%s) and post your first message.' . "\n" . - '* Add a [Jabber/GTalk address](%%%%action.imsettings%%%%) so you can send notices through instant messages.' . "\n" . - '* [Search for people](%%%%action.peoplesearch%%%%) that you may know or that share your interests. ' . "\n" . - '* Update your [profile settings](%%%%action.profilesettings%%%%) to tell others more about you. ' . "\n" . - '* Read over the [online docs](%%%%doc.help%%%%) for features you may have missed. ' . "\n\n" . - 'Thanks for signing up and we hope you enjoy using this service.'), - $nickname, common_local_url('showstream', array('nickname' => $nickname))); - common_raw(common_markup_to_html($instr)); - $have_email = $this->trimmed('email'); - if ($have_email) { - $emailinstr = _('(You should receive a message by email momentarily, with ' . - 'instructions on how to confirm your email address.)'); - common_raw(common_markup_to_html($emailinstr)); - } - common_element_end('div'); - common_show_footer(); - } +class RegisterAction extends Action +{ + function handle($args) + { + parent::handle($args); + + if (common_config('site', 'closed')) { + common_user_error(_('Registration not allowed.')); + } else if (common_logged_in()) { + common_user_error(_('Already logged in.')); + } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $this->try_register(); + } else { + $this->show_form(); + } + } + + function try_register() + { + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->show_form(_('There was a problem with your session token. Try again, please.')); + return; + } + + $nickname = $this->trimmed('nickname'); + $email = $this->trimmed('email'); + $fullname = $this->trimmed('fullname'); + $homepage = $this->trimmed('homepage'); + $bio = $this->trimmed('bio'); + $location = $this->trimmed('location'); + + # We don't trim these... whitespace is OK in a password! + + $password = $this->arg('password'); + $confirm = $this->arg('confirm'); + + # invitation code, if any + + $code = $this->trimmed('code'); + + if ($code) { + $invite = Invitation::staticGet($code); + } + + if (common_config('site', 'inviteonly') && !($code && $invite)) { + $this->client_error(_('Sorry, only invited people can register.')); + return; + } + + # Input scrubbing + + $nickname = common_canonical_nickname($nickname); + $email = common_canonical_email($email); + + if (!$this->boolean('license')) { + $this->show_form(_('You can\'t register if you don\'t agree to the license.')); + } else if ($email && !Validate::email($email, true)) { + $this->show_form(_('Not a valid email address.')); + } else if (!Validate::string($nickname, array('min_length' => 1, + 'max_length' => 64, + 'format' => VALIDATE_NUM . VALIDATE_ALPHA_LOWER))) { + $this->show_form(_('Nickname must have only lowercase letters and numbers and no spaces.')); + } else if ($this->nickname_exists($nickname)) { + $this->show_form(_('Nickname already in use. Try another one.')); + } else if (!User::allowed_nickname($nickname)) { + $this->show_form(_('Not a valid nickname.')); + } else if ($this->email_exists($email)) { + $this->show_form(_('Email address already exists.')); + } else if (!is_null($homepage) && (strlen($homepage) > 0) && + !Validate::uri($homepage, array('allowed_schemes' => array('http', 'https')))) { + $this->show_form(_('Homepage is not a valid URL.')); + return; + } else if (!is_null($fullname) && strlen($fullname) > 255) { + $this->show_form(_('Full name is too long (max 255 chars).')); + return; + } else if (!is_null($bio) && strlen($bio) > 140) { + $this->show_form(_('Bio is too long (max 140 chars).')); + return; + } else if (!is_null($location) && strlen($location) > 255) { + $this->show_form(_('Location is too long (max 255 chars).')); + return; + } else if (strlen($password) < 6) { + $this->show_form(_('Password must be 6 or more characters.')); + return; + } else if ($password != $confirm) { + $this->show_form(_('Passwords don\'t match.')); + } else if ($user = User::register(array('nickname' => $nickname, 'password' => $password, 'email' => $email, + 'fullname' => $fullname, 'homepage' => $homepage, 'bio' => $bio, + 'location' => $location, 'code' => $code))) { + if (!$user) { + $this->show_form(_('Invalid username or password.')); + return; + } + # success! + if (!common_set_user($user)) { + common_server_error(_('Error setting user.')); + return; + } + # this is a real login + common_real_login(true); + if ($this->boolean('rememberme')) { + common_debug('Adding rememberme cookie for ' . $nickname); + common_rememberme($user); + } + # Re-init language env in case it changed (not yet, but soon) + common_init_language(); + $this->show_success(); + } else { + $this->show_form(_('Invalid username or password.')); + } + } + + # checks if *CANONICAL* nickname exists + + function nickname_exists($nickname) + { + $user = User::staticGet('nickname', $nickname); + return ($user !== false); + } + + # checks if *CANONICAL* email exists + + function email_exists($email) + { + $email = common_canonical_email($email); + if (!$email || strlen($email) == 0) { + return false; + } + $user = User::staticGet('email', $email); + return ($user !== false); + } + + function show_top($error=null) + { + if ($error) { + common_element('p', 'error', $error); + } else { + $instr = common_markup_to_html(_('With this form you can create a new account. ' . + 'You can then post notices and link up to friends and colleagues. '. + '(Have an [OpenID](http://openid.net/)? ' . + 'Try our [OpenID registration](%%action.openidlogin%%)!)')); + + common_element_start('div', 'instructions'); + common_raw($instr); + common_element_end('div'); + } + } + + function show_form($error=null) + { + global $config; + + $code = $this->trimmed('code'); + + if ($code) { + $invite = Invitation::staticGet($code); + } + + if (common_config('site', 'inviteonly') && !($code && $invite)) { + $this->client_error(_('Sorry, only invited people can register.')); + return; + } + + common_show_header(_('Register'), null, $error, array($this, 'show_top')); + common_element_start('form', array('method' => 'post', + 'id' => 'login', + 'action' => common_local_url('register'))); + + common_hidden('token', common_session_token()); + + if ($code) { + common_hidden('code', $code); + } + + common_input('nickname', _('Nickname'), $this->trimmed('nickname'), + _('1-64 lowercase letters or numbers, no punctuation or spaces. Required.')); + common_password('password', _('Password'), + _('6 or more characters. Required.')); + common_password('confirm', _('Confirm'), + _('Same as password above. Required.')); + if ($invite && $invite->address_type == 'email') { + common_input('email', _('Email'), $invite->address, + _('Used only for updates, announcements, and password recovery')); + } else { + common_input('email', _('Email'), $this->trimmed('email'), + _('Used only for updates, announcements, and password recovery')); + } + common_input('fullname', _('Full name'), + $this->trimmed('fullname'), + _('Longer name, preferably your "real" name')); + common_input('homepage', _('Homepage'), + $this->trimmed('homepage'), + _('URL of your homepage, blog, or profile on another site')); + common_textarea('bio', _('Bio'), + $this->trimmed('bio'), + _('Describe yourself and your interests in 140 chars')); + common_input('location', _('Location'), + $this->trimmed('location'), + _('Where you are, like "City, State (or Region), Country"')); + common_checkbox('rememberme', _('Remember me'), + $this->boolean('rememberme'), + _('Automatically login in the future; not for shared computers!')); + common_element_start('p'); + $attrs = array('type' => 'checkbox', + 'id' => 'license', + 'name' => 'license', + 'value' => 'true'); + if ($this->boolean('license')) { + $attrs['checked'] = 'checked'; + } + common_element('input', $attrs); + common_text(_('My text and files are available under ')); + common_element('a', array('href' => $config['license']['url']), + $config['license']['title']); + common_text(_(' except this private data: password, email address, IM address, phone number.')); + common_element_end('p'); + common_submit('submit', _('Register')); + common_element_end('form'); + common_show_footer(); + } + + function show_success() + { + $nickname = $this->arg('nickname'); + common_show_header(_('Registration successful')); + common_element_start('div', 'success'); + $instr = sprintf(_('Congratulations, %s! And welcome to %%%%site.name%%%%. From here, you may want to...'. "\n\n" . + '* Go to [your profile](%s) and post your first message.' . "\n" . + '* Add a [Jabber/GTalk address](%%%%action.imsettings%%%%) so you can send notices through instant messages.' . "\n" . + '* [Search for people](%%%%action.peoplesearch%%%%) that you may know or that share your interests. ' . "\n" . + '* Update your [profile settings](%%%%action.profilesettings%%%%) to tell others more about you. ' . "\n" . + '* Read over the [online docs](%%%%doc.help%%%%) for features you may have missed. ' . "\n\n" . + 'Thanks for signing up and we hope you enjoy using this service.'), + $nickname, common_local_url('showstream', array('nickname' => $nickname))); + common_raw(common_markup_to_html($instr)); + $have_email = $this->trimmed('email'); + if ($have_email) { + $emailinstr = _('(You should receive a message by email momentarily, with ' . + 'instructions on how to confirm your email address.)'); + common_raw(common_markup_to_html($emailinstr)); + } + common_element_end('div'); + common_show_footer(); + } } diff --git a/actions/remotesubscribe.php b/actions/remotesubscribe.php index c3a09bcfc..a9494772e 100644 --- a/actions/remotesubscribe.php +++ b/actions/remotesubscribe.php @@ -21,366 +21,379 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/omb.php'); -class RemotesubscribeAction extends Action { - - function handle($args) { - - parent::handle($args); - - if (common_logged_in()) { - common_user_error(_('You can use the local subscription!')); - return; - } - - if ($_SERVER['REQUEST_METHOD'] == 'POST') { - - # CSRF protection - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->show_form(_('There was a problem with your session token. Try again, please.')); - return; - } - - $this->remote_subscription(); - } else { - $this->show_form(); - } - } - - function get_instructions() { - return _('To subscribe, you can [login](%%action.login%%),' . - ' or [register](%%action.register%%) a new ' . - ' account. If you already have an account ' . - ' on a [compatible microblogging site](%%doc.openmublog%%), ' . - ' enter your profile URL below.'); - } - - function show_top($err=NULL) { - if ($err) { - common_element('div', 'error', $err); - } else { - $instructions = $this->get_instructions(); - $output = common_markup_to_html($instructions); - common_element_start('div', 'instructions'); - common_raw($output); - common_element_end('p'); - } - } - - function show_form($err=NULL) { - $nickname = $this->trimmed('nickname'); - $profile = $this->trimmed('profile_url'); - common_show_header(_('Remote subscribe'), NULL, $err, - array($this, 'show_top')); - # id = remotesubscribe conflicts with the - # button on profile page - common_element_start('form', array('id' => 'remsub', 'method' => 'post', - 'action' => common_local_url('remotesubscribe'))); - common_hidden('token', common_session_token()); - common_input('nickname', _('User nickname'), $nickname, - _('Nickname of the user you want to follow')); - common_input('profile_url', _('Profile URL'), $profile, - _('URL of your profile on another compatible microblogging service')); - common_submit('submit', _('Subscribe')); - common_element_end('form'); - common_show_footer(); - } - - function remote_subscription() { - $user = $this->get_user(); - - if (!$user) { - $this->show_form(_('No such user.')); - return; - } - - $profile = $this->trimmed('profile_url'); - - if (!$profile) { - $this->show_form(_('No such user.')); - return; - } - - if (!Validate::uri($profile, array('allowed_schemes' => array('http', 'https')))) { - $this->show_form(_('Invalid profile URL (bad format)')); - return; - } - - $fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); - $yadis = Auth_Yadis_Yadis::discover($profile, $fetcher); - - if (!$yadis || $yadis->failed) { - $this->show_form(_('Not a valid profile URL (no YADIS document).')); - return; - } - - # XXX: a little liberal for sites that accidentally put whitespace before the xml declaration +class RemotesubscribeAction extends Action +{ + + function handle($args) + { + + parent::handle($args); + + if (common_logged_in()) { + common_user_error(_('You can use the local subscription!')); + return; + } + + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + + # CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->show_form(_('There was a problem with your session token. Try again, please.')); + return; + } + + $this->remote_subscription(); + } else { + $this->show_form(); + } + } + + function get_instructions() + { + return _('To subscribe, you can [login](%%action.login%%),' . + ' or [register](%%action.register%%) a new ' . + ' account. If you already have an account ' . + ' on a [compatible microblogging site](%%doc.openmublog%%), ' . + ' enter your profile URL below.'); + } + + function show_top($err=null) + { + if ($err) { + common_element('div', 'error', $err); + } else { + $instructions = $this->get_instructions(); + $output = common_markup_to_html($instructions); + common_element_start('div', 'instructions'); + common_raw($output); + common_element_end('p'); + } + } + + function show_form($err=null) + { + $nickname = $this->trimmed('nickname'); + $profile = $this->trimmed('profile_url'); + common_show_header(_('Remote subscribe'), null, $err, + array($this, 'show_top')); + # id = remotesubscribe conflicts with the + # button on profile page + common_element_start('form', array('id' => 'remsub', 'method' => 'post', + 'action' => common_local_url('remotesubscribe'))); + common_hidden('token', common_session_token()); + common_input('nickname', _('User nickname'), $nickname, + _('Nickname of the user you want to follow')); + common_input('profile_url', _('Profile URL'), $profile, + _('URL of your profile on another compatible microblogging service')); + common_submit('submit', _('Subscribe')); + common_element_end('form'); + common_show_footer(); + } + + function remote_subscription() + { + $user = $this->get_user(); + + if (!$user) { + $this->show_form(_('No such user.')); + return; + } + + $profile = $this->trimmed('profile_url'); + + if (!$profile) { + $this->show_form(_('No such user.')); + return; + } + + if (!Validate::uri($profile, array('allowed_schemes' => array('http', 'https')))) { + $this->show_form(_('Invalid profile URL (bad format)')); + return; + } + + $fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); + $yadis = Auth_Yadis_Yadis::discover($profile, $fetcher); + + if (!$yadis || $yadis->failed) { + $this->show_form(_('Not a valid profile URL (no YADIS document).')); + return; + } + + # XXX: a little liberal for sites that accidentally put whitespace before the xml declaration $xrds =& Auth_Yadis_XRDS::parseXRDS(trim($yadis->response_text)); - if (!$xrds) { - $this->show_form(_('Not a valid profile URL (no XRDS defined).')); - return; - } + if (!$xrds) { + $this->show_form(_('Not a valid profile URL (no XRDS defined).')); + return; + } - $omb = $this->getOmb($xrds); + $omb = $this->getOmb($xrds); - if (!$omb) { - $this->show_form(_('Not a valid profile URL (incorrect services).')); - return; - } + if (!$omb) { + $this->show_form(_('Not a valid profile URL (incorrect services).')); + return; + } - if (omb_service_uri($omb[OAUTH_ENDPOINT_REQUEST]) == - common_local_url('requesttoken')) - { - $this->show_form(_('That\'s a local profile! Login to subscribe.')); - return; - } - - if (User::staticGet('uri', omb_local_id($omb[OAUTH_ENDPOINT_REQUEST]))) { - $this->show_form(_('That\'s a local profile! Login to subscribe.')); - return; - } - - list($token, $secret) = $this->request_token($omb); - - if (!$token || !$secret) { - $this->show_form(_('Couldn\'t get a request token.')); - return; - } - - $this->request_authorization($user, $omb, $token, $secret); - } - - function get_user() { - $user = NULL; - $nickname = $this->trimmed('nickname'); - if ($nickname) { - $user = User::staticGet('nickname', $nickname); - } - return $user; - } - - function getOmb($xrds) { - - static $omb_endpoints = array(OMB_ENDPOINT_UPDATEPROFILE, OMB_ENDPOINT_POSTNOTICE); - static $oauth_endpoints = array(OAUTH_ENDPOINT_REQUEST, OAUTH_ENDPOINT_AUTHORIZE, - OAUTH_ENDPOINT_ACCESS); - $omb = array(); - - # XXX: the following code could probably be refactored to eliminate dupes - - $oauth_services = omb_get_services($xrds, OAUTH_DISCOVERY); - - if (!$oauth_services) { - return NULL; - } - - $oauth_service = $oauth_services[0]; - - $oauth_xrd = $this->getXRD($oauth_service, $xrds); + if (omb_service_uri($omb[OAUTH_ENDPOINT_REQUEST]) == + common_local_url('requesttoken')) + { + $this->show_form(_('That\'s a local profile! Login to subscribe.')); + return; + } + + if (User::staticGet('uri', omb_local_id($omb[OAUTH_ENDPOINT_REQUEST]))) { + $this->show_form(_('That\'s a local profile! Login to subscribe.')); + return; + } + + list($token, $secret) = $this->request_token($omb); + + if (!$token || !$secret) { + $this->show_form(_('Couldn\'t get a request token.')); + return; + } + + $this->request_authorization($user, $omb, $token, $secret); + } + + function get_user() + { + $user = null; + $nickname = $this->trimmed('nickname'); + if ($nickname) { + $user = User::staticGet('nickname', $nickname); + } + return $user; + } + + function getOmb($xrds) + { + + static $omb_endpoints = array(OMB_ENDPOINT_UPDATEPROFILE, OMB_ENDPOINT_POSTNOTICE); + static $oauth_endpoints = array(OAUTH_ENDPOINT_REQUEST, OAUTH_ENDPOINT_AUTHORIZE, + OAUTH_ENDPOINT_ACCESS); + $omb = array(); + + # XXX: the following code could probably be refactored to eliminate dupes + + $oauth_services = omb_get_services($xrds, OAUTH_DISCOVERY); + + if (!$oauth_services) { + return null; + } + + $oauth_service = $oauth_services[0]; + + $oauth_xrd = $this->getXRD($oauth_service, $xrds); + + if (!$oauth_xrd) { + return null; + } + + if (!$this->addServices($oauth_xrd, $oauth_endpoints, $omb)) { + return null; + } + + $omb_services = omb_get_services($xrds, OMB_NAMESPACE); - if (!$oauth_xrd) { - return NULL; - } + if (!$omb_services) { + return null; + } + + $omb_service = $omb_services[0]; + + $omb_xrd = $this->getXRD($omb_service, $xrds); + + if (!$omb_xrd) { + return null; + } + + if (!$this->addServices($omb_xrd, $omb_endpoints, $omb)) { + return null; + } + + # XXX: check that we got all the services we needed + + foreach (array_merge($omb_endpoints, $oauth_endpoints) as $type) { + if (!array_key_exists($type, $omb) || !$omb[$type]) { + return null; + } + } + + if (!omb_local_id($omb[OAUTH_ENDPOINT_REQUEST])) { + return null; + } + + return $omb; + } - if (!$this->addServices($oauth_xrd, $oauth_endpoints, $omb)) { - return NULL; - } + function getXRD($main_service, $main_xrds) + { + $uri = omb_service_uri($main_service); + if (strpos($uri, "#") !== 0) { + # FIXME: more rigorous handling of external service definitions + return null; + } + $id = substr($uri, 1); + $nodes = $main_xrds->allXrdNodes; + $parser = $main_xrds->parser; + foreach ($nodes as $node) { + $attrs = $parser->attributes($node); + if (array_key_exists('xml:id', $attrs) && + $attrs['xml:id'] == $id) { + # XXX: trick the constructor into thinking this is the only node + $bogus_nodes = array($node); + return new Auth_Yadis_XRDS($parser, $bogus_nodes); + } + } + return null; + } + + function addServices($xrd, $types, &$omb) + { + foreach ($types as $type) { + $matches = omb_get_services($xrd, $type); + if ($matches) { + $omb[$type] = $matches[0]; + } else { + # no match for type + return false; + } + } + return true; + } - $omb_services = omb_get_services($xrds, OMB_NAMESPACE); + function request_token($omb) + { + $con = omb_oauth_consumer(); - if (!$omb_services) { - return NULL; - } + $url = omb_service_uri($omb[OAUTH_ENDPOINT_REQUEST]); - $omb_service = $omb_services[0]; + # XXX: Is this the right thing to do? Strip off GET params and make them + # POST params? Seems wrong to me. - $omb_xrd = $this->getXRD($omb_service, $xrds); + $parsed = parse_url($url); + $params = array(); + parse_str($parsed['query'], $params); - if (!$omb_xrd) { - return NULL; - } + $req = OAuthRequest::from_consumer_and_token($con, null, "POST", $url, $params); - if (!$this->addServices($omb_xrd, $omb_endpoints, $omb)) { - return NULL; - } + $listener = omb_local_id($omb[OAUTH_ENDPOINT_REQUEST]); - # XXX: check that we got all the services we needed + if (!$listener) { + return null; + } - foreach (array_merge($omb_endpoints, $oauth_endpoints) as $type) { - if (!array_key_exists($type, $omb) || !$omb[$type]) { - return NULL; - } - } - - if (!omb_local_id($omb[OAUTH_ENDPOINT_REQUEST])) { - return NULL; - } - - return $omb; - } - - function getXRD($main_service, $main_xrds) { - $uri = omb_service_uri($main_service); - if (strpos($uri, "#") !== 0) { - # FIXME: more rigorous handling of external service definitions - return NULL; - } - $id = substr($uri, 1); - $nodes = $main_xrds->allXrdNodes; - $parser = $main_xrds->parser; - foreach ($nodes as $node) { - $attrs = $parser->attributes($node); - if (array_key_exists('xml:id', $attrs) && - $attrs['xml:id'] == $id) { - # XXX: trick the constructor into thinking this is the only node - $bogus_nodes = array($node); - return new Auth_Yadis_XRDS($parser, $bogus_nodes); - } - } - return NULL; - } - - function addServices($xrd, $types, &$omb) { - foreach ($types as $type) { - $matches = omb_get_services($xrd, $type); - if ($matches) { - $omb[$type] = $matches[0]; - } else { - # no match for type - return false; - } - } - return true; - } - - function request_token($omb) { - $con = omb_oauth_consumer(); - - $url = omb_service_uri($omb[OAUTH_ENDPOINT_REQUEST]); - - # XXX: Is this the right thing to do? Strip off GET params and make them - # POST params? Seems wrong to me. - - $parsed = parse_url($url); - $params = array(); - parse_str($parsed['query'], $params); - - $req = OAuthRequest::from_consumer_and_token($con, NULL, "POST", $url, $params); - - $listener = omb_local_id($omb[OAUTH_ENDPOINT_REQUEST]); - - if (!$listener) { - return NULL; - } - - $req->set_parameter('omb_listener', $listener); - $req->set_parameter('omb_version', OMB_VERSION_01); - - # XXX: test to see if endpoint accepts this signature method - - $req->sign_request(omb_hmac_sha1(), $con, NULL); - - # We re-use this tool's fetcher, since it's pretty good - - $fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); - - $result = $fetcher->post($req->get_normalized_http_url(), - $req->to_postdata(), + $req->set_parameter('omb_listener', $listener); + $req->set_parameter('omb_version', OMB_VERSION_01); + + # XXX: test to see if endpoint accepts this signature method + + $req->sign_request(omb_hmac_sha1(), $con, null); + + # We re-use this tool's fetcher, since it's pretty good + + $fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); + + $result = $fetcher->post($req->get_normalized_http_url(), + $req->to_postdata(), array('User-Agent' => 'Laconica/' . LACONICA_VERSION)); - if ($result->status != 200) { - return NULL; - } + if ($result->status != 200) { + return null; + } - parse_str($result->body, $return); + parse_str($result->body, $return); - return array($return['oauth_token'], $return['oauth_token_secret']); - } + return array($return['oauth_token'], $return['oauth_token_secret']); + } - function request_authorization($user, $omb, $token, $secret) { - global $config; # for license URL + function request_authorization($user, $omb, $token, $secret) + { + global $config; # for license URL - $con = omb_oauth_consumer(); - $tok = new OAuthToken($token, $secret); + $con = omb_oauth_consumer(); + $tok = new OAuthToken($token, $secret); - $url = omb_service_uri($omb[OAUTH_ENDPOINT_AUTHORIZE]); + $url = omb_service_uri($omb[OAUTH_ENDPOINT_AUTHORIZE]); - # XXX: Is this the right thing to do? Strip off GET params and make them - # POST params? Seems wrong to me. + # XXX: Is this the right thing to do? Strip off GET params and make them + # POST params? Seems wrong to me. - $parsed = parse_url($url); - $params = array(); - parse_str($parsed['query'], $params); + $parsed = parse_url($url); + $params = array(); + parse_str($parsed['query'], $params); - $req = OAuthRequest::from_consumer_and_token($con, $tok, 'GET', $url, $params); + $req = OAuthRequest::from_consumer_and_token($con, $tok, 'GET', $url, $params); - # We send over a ton of information. This lets the other - # server store info about our user, and it lets the current - # user decide if they really want to authorize the subscription. + # We send over a ton of information. This lets the other + # server store info about our user, and it lets the current + # user decide if they really want to authorize the subscription. - $req->set_parameter('omb_version', OMB_VERSION_01); - $req->set_parameter('omb_listener', omb_local_id($omb[OAUTH_ENDPOINT_REQUEST])); - $req->set_parameter('omb_listenee', $user->uri); - $req->set_parameter('omb_listenee_profile', common_profile_url($user->nickname)); - $req->set_parameter('omb_listenee_nickname', $user->nickname); - $req->set_parameter('omb_listenee_license', $config['license']['url']); + $req->set_parameter('omb_version', OMB_VERSION_01); + $req->set_parameter('omb_listener', omb_local_id($omb[OAUTH_ENDPOINT_REQUEST])); + $req->set_parameter('omb_listenee', $user->uri); + $req->set_parameter('omb_listenee_profile', common_profile_url($user->nickname)); + $req->set_parameter('omb_listenee_nickname', $user->nickname); + $req->set_parameter('omb_listenee_license', $config['license']['url']); - $profile = $user->getProfile(); - if (!$profile) { - common_log_db_error($user, 'SELECT', __FILE__); - $this->server_error(_('User without matching profile')); - return; - } + $profile = $user->getProfile(); + if (!$profile) { + common_log_db_error($user, 'SELECT', __FILE__); + $this->server_error(_('User without matching profile')); + return; + } - if ($profile->fullname) { - $req->set_parameter('omb_listenee_fullname', $profile->fullname); - } - if ($profile->homepage) { - $req->set_parameter('omb_listenee_homepage', $profile->homepage); - } - if ($profile->bio) { - $req->set_parameter('omb_listenee_bio', $profile->bio); - } - if ($profile->location) { - $req->set_parameter('omb_listenee_location', $profile->location); - } - $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); - if ($avatar) { - $req->set_parameter('omb_listenee_avatar', $avatar->url); - } + if ($profile->fullname) { + $req->set_parameter('omb_listenee_fullname', $profile->fullname); + } + if ($profile->homepage) { + $req->set_parameter('omb_listenee_homepage', $profile->homepage); + } + if ($profile->bio) { + $req->set_parameter('omb_listenee_bio', $profile->bio); + } + if ($profile->location) { + $req->set_parameter('omb_listenee_location', $profile->location); + } + $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); + if ($avatar) { + $req->set_parameter('omb_listenee_avatar', $avatar->url); + } - # XXX: add a nonce to prevent replay attacks + # XXX: add a nonce to prevent replay attacks - $req->set_parameter('oauth_callback', common_local_url('finishremotesubscribe')); + $req->set_parameter('oauth_callback', common_local_url('finishremotesubscribe')); - # XXX: test to see if endpoint accepts this signature method + # XXX: test to see if endpoint accepts this signature method - $req->sign_request(omb_hmac_sha1(), $con, $tok); + $req->sign_request(omb_hmac_sha1(), $con, $tok); - # store all our info here + # store all our info here - $omb['listenee'] = $user->nickname; - $omb['listener'] = omb_local_id($omb[OAUTH_ENDPOINT_REQUEST]); - $omb['token'] = $token; - $omb['secret'] = $secret; - # call doesn't work after bounce back so we cache; maybe serialization issue...? - $omb['access_token_url'] = omb_service_uri($omb[OAUTH_ENDPOINT_ACCESS]); - $omb['post_notice_url'] = omb_service_uri($omb[OMB_ENDPOINT_POSTNOTICE]); - $omb['update_profile_url'] = omb_service_uri($omb[OMB_ENDPOINT_UPDATEPROFILE]); + $omb['listenee'] = $user->nickname; + $omb['listener'] = omb_local_id($omb[OAUTH_ENDPOINT_REQUEST]); + $omb['token'] = $token; + $omb['secret'] = $secret; + # call doesn't work after bounce back so we cache; maybe serialization issue...? + $omb['access_token_url'] = omb_service_uri($omb[OAUTH_ENDPOINT_ACCESS]); + $omb['post_notice_url'] = omb_service_uri($omb[OMB_ENDPOINT_POSTNOTICE]); + $omb['update_profile_url'] = omb_service_uri($omb[OMB_ENDPOINT_UPDATEPROFILE]); - common_ensure_session(); + common_ensure_session(); - $_SESSION['oauth_authorization_request'] = $omb; + $_SESSION['oauth_authorization_request'] = $omb; - # Redirect to authorization service + # Redirect to authorization service - common_redirect($req->to_url()); - return; - } + common_redirect($req->to_url()); + return; + } - function make_nonce() { - return common_good_rand(16); - } + function make_nonce() + { + return common_good_rand(16); + } } diff --git a/actions/replies.php b/actions/replies.php index 835871ffc..eceeb4d65 100644 --- a/actions/replies.php +++ b/actions/replies.php @@ -21,74 +21,80 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/actions/showstream.php'); -class RepliesAction extends StreamAction { +class RepliesAction extends StreamAction +{ - function handle($args) { + function handle($args) + { - parent::handle($args); + parent::handle($args); - $nickname = common_canonical_nickname($this->arg('nickname')); - $user = User::staticGet('nickname', $nickname); + $nickname = common_canonical_nickname($this->arg('nickname')); + $user = User::staticGet('nickname', $nickname); - if (!$user) { - $this->no_such_user(); - return; - } + if (!$user) { + $this->no_such_user(); + return; + } - $profile = $user->getProfile(); + $profile = $user->getProfile(); - if (!$profile) { - common_server_error(_('User has no profile.')); - return; - } + if (!$profile) { + common_server_error(_('User has no profile.')); + return; + } - # Looks like we're good; show the header + # Looks like we're good; show the header - common_show_header(sprintf(_("Replies to %s"), $profile->nickname), - array($this, 'show_header'), $user, - array($this, 'show_top')); + common_show_header(sprintf(_("Replies to %s"), $profile->nickname), + array($this, 'show_header'), $user, + array($this, 'show_top')); - $this->show_replies($user); + $this->show_replies($user); - common_show_footer(); - } + common_show_footer(); + } - function no_such_user() { - common_user_error(_('No such user.')); - } + function no_such_user() + { + common_user_error(_('No such user.')); + } - function show_header($user) { - common_element('link', array('rel' => 'alternate', - 'href' => common_local_url('repliesrss', array('nickname' => - $user->nickname)), - 'type' => 'application/rss+xml', - 'title' => sprintf(_('Feed for replies to %s'), $user->nickname))); - } + function show_header($user) + { + common_element('link', array('rel' => 'alternate', + 'href' => common_local_url('repliesrss', array('nickname' => + $user->nickname)), + 'type' => 'application/rss+xml', + 'title' => sprintf(_('Feed for replies to %s'), $user->nickname))); + } - function show_top($user) { - $cur = common_current_user(); + function show_top($user) + { + $cur = common_current_user(); - if ($cur && $cur->id == $user->id) { - common_notice_form('replies'); - } + if ($cur && $cur->id == $user->id) { + common_notice_form('replies'); + } - $this->views_menu(); + $this->views_menu(); - $this->show_feeds_list(array(0=>array('href'=>common_local_url('repliesrss', array('nickname' => $user->nickname)), - 'type' => 'rss', - 'version' => 'RSS 1.0', - 'item' => 'repliesrss'))); - } + $this->show_feeds_list(array(0=>array('href'=>common_local_url('repliesrss', array('nickname' => $user->nickname)), + 'type' => 'rss', + 'version' => 'RSS 1.0', + 'item' => 'repliesrss'))); + } - function show_replies($user) { + function show_replies($user) + { - $page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; + $page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; - $notice = $user->getReplies(($page-1) * NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1); + $notice = $user->getReplies(($page-1) * NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1); - $cnt = $this->show_notice_list($notice); + $cnt = $this->show_notice_list($notice); - common_pagination($page > 1, $cnt > NOTICES_PER_PAGE, - $page, 'replies', array('nickname' => $user->nickname)); - } + common_pagination($page > 1, $cnt > NOTICES_PER_PAGE, + $page, 'replies', array('nickname' => $user->nickname)); + } } diff --git a/actions/repliesrss.php b/actions/repliesrss.php index 7369db5e0..5f85f8d2e 100644 --- a/actions/repliesrss.php +++ b/actions/repliesrss.php @@ -23,57 +23,62 @@ require_once(INSTALLDIR.'/lib/rssaction.php'); // Formatting of RSS handled by Rss10Action -class RepliesrssAction extends Rss10Action { +class RepliesrssAction extends Rss10Action +{ - var $user = NULL; + var $user = null; - function init() { - $nickname = $this->trimmed('nickname'); - $this->user = User::staticGet('nickname', $nickname); + function init() + { + $nickname = $this->trimmed('nickname'); + $this->user = User::staticGet('nickname', $nickname); - if (!$this->user) { - common_user_error(_('No such user.')); - return false; - } else { - return true; - } - } + if (!$this->user) { + common_user_error(_('No such user.')); + return false; + } else { + return true; + } + } - function get_notices($limit=0) { + function get_notices($limit=0) + { - $user = $this->user; + $user = $this->user; - $notice = $user->getReplies(0, ($limit == 0) ? 48 : $limit); + $notice = $user->getReplies(0, ($limit == 0) ? 48 : $limit); - $notices = array(); - - while ($notice->fetch()) { - $notices[] = clone($notice); - } + $notices = array(); + + while ($notice->fetch()) { + $notices[] = clone($notice); + } - return $notices; - } + return $notices; + } - function get_channel() { - $user = $this->user; - $c = array('url' => common_local_url('repliesrss', - array('nickname' => - $user->nickname)), - 'title' => sprintf(_("Replies to %s"), $user->nickname), - 'link' => common_local_url('replies', - array('nickname' => - $user->nickname)), - 'description' => sprintf(_('Feed for replies to %s'), $user->nickname)); - return $c; - } + function get_channel() + { + $user = $this->user; + $c = array('url' => common_local_url('repliesrss', + array('nickname' => + $user->nickname)), + 'title' => sprintf(_("Replies to %s"), $user->nickname), + 'link' => common_local_url('replies', + array('nickname' => + $user->nickname)), + 'description' => sprintf(_('Feed for replies to %s'), $user->nickname)); + return $c; + } - function get_image() { - $user = $this->user; - $profile = $user->getProfile(); - if (!$profile) { - return NULL; - } - $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); - return ($avatar) ? $avatar->url : NULL; - } + function get_image() + { + $user = $this->user; + $profile = $user->getProfile(); + if (!$profile) { + return null; + } + $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); + return ($avatar) ? $avatar->url : null; + } }
\ No newline at end of file diff --git a/actions/requesttoken.php b/actions/requesttoken.php index 76019a929..a74548739 100644 --- a/actions/requesttoken.php +++ b/actions/requesttoken.php @@ -21,22 +21,25 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/omb.php'); -class RequesttokenAction extends Action { - - function is_readonly() { - return false; - } - - function handle($args) { - parent::handle($args); - try { - common_remove_magic_from_request(); - $req = OAuthRequest::from_request(); - $server = omb_oauth_server(); - $token = $server->fetch_request_token($req); - print $token; - } catch (OAuthException $e) { - common_server_error($e->getMessage()); - } - } +class RequesttokenAction extends Action +{ + + function is_readonly() + { + return false; + } + + function handle($args) + { + parent::handle($args); + try { + common_remove_magic_from_request(); + $req = OAuthRequest::from_request(); + $server = omb_oauth_server(); + $token = $server->fetch_request_token($req); + print $token; + } catch (OAuthException $e) { + common_server_error($e->getMessage()); + } + } } diff --git a/actions/showfavorites.php b/actions/showfavorites.php index 4de4b1271..f4344833d 100644 --- a/actions/showfavorites.php +++ b/actions/showfavorites.php @@ -21,77 +21,82 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/actions/showstream.php'); -class ShowfavoritesAction extends StreamAction { +class ShowfavoritesAction extends StreamAction +{ - function handle($args) { + function handle($args) + { - parent::handle($args); + parent::handle($args); - $nickname = common_canonical_nickname($this->arg('nickname')); - $user = User::staticGet('nickname', $nickname); + $nickname = common_canonical_nickname($this->arg('nickname')); + $user = User::staticGet('nickname', $nickname); - if (!$user) { - $this->client_error(_('No such user.')); - return; - } + if (!$user) { + $this->client_error(_('No such user.')); + return; + } - $profile = $user->getProfile(); + $profile = $user->getProfile(); - if (!$profile) { - common_server_error(_('User has no profile.')); - return; - } + if (!$profile) { + common_server_error(_('User has no profile.')); + return; + } - # Looks like we're good; show the header + # Looks like we're good; show the header - common_show_header(sprintf(_("%s favorite notices"), $profile->nickname), - array($this, 'show_header'), $user, - array($this, 'show_top')); + common_show_header(sprintf(_("%s favorite notices"), $profile->nickname), + array($this, 'show_header'), $user, + array($this, 'show_top')); - $this->show_notices($user); + $this->show_notices($user); - common_show_footer(); - } + common_show_footer(); + } - function show_header($user) { - common_element('link', array('rel' => 'alternate', - 'href' => common_local_url('favoritesrss', array('nickname' => - $user->nickname)), - 'type' => 'application/rss+xml', - 'title' => sprintf(_('Feed for favorites of %s'), $user->nickname))); - } + function show_header($user) + { + common_element('link', array('rel' => 'alternate', + 'href' => common_local_url('favoritesrss', array('nickname' => + $user->nickname)), + 'type' => 'application/rss+xml', + 'title' => sprintf(_('Feed for favorites of %s'), $user->nickname))); + } - function show_top($user) { - $cur = common_current_user(); + function show_top($user) + { + $cur = common_current_user(); - if ($cur && $cur->id == $user->id) { - common_notice_form('all'); - } + if ($cur && $cur->id == $user->id) { + common_notice_form('all'); + } - $this->show_feeds_list(array(0=>array('href'=>common_local_url('favoritesrss', array('nickname' => $user->nickname)), - 'type' => 'rss', - 'version' => 'RSS 1.0', - 'item' => 'Favorites'))); - $this->views_menu(); - } + $this->show_feeds_list(array(0=>array('href'=>common_local_url('favoritesrss', array('nickname' => $user->nickname)), + 'type' => 'rss', + 'version' => 'RSS 1.0', + 'item' => 'Favorites'))); + $this->views_menu(); + } - function show_notices($user) { + function show_notices($user) + { - $page = $this->trimmed('page'); - if (!$page) { - $page = 1; - } + $page = $this->trimmed('page'); + if (!$page) { + $page = 1; + } - $notice = $user->favoriteNotices(($page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1); + $notice = $user->favoriteNotices(($page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1); - if (!$notice) { - $this->server_error(_('Could not retrieve favorite notices.')); - return; - } + if (!$notice) { + $this->server_error(_('Could not retrieve favorite notices.')); + return; + } $cnt = $this->show_notice_list($notice); - common_pagination($page > 1, $cnt > NOTICES_PER_PAGE, - $page, 'showfavorites', array('nickname' => $user->nickname)); - } + common_pagination($page > 1, $cnt > NOTICES_PER_PAGE, + $page, 'showfavorites', array('nickname' => $user->nickname)); + } } diff --git a/actions/showmessage.php b/actions/showmessage.php index c171ffe0b..25330a568 100644 --- a/actions/showmessage.php +++ b/actions/showmessage.php @@ -21,80 +21,88 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/mailbox.php'); -class ShowmessageAction extends MailboxAction { +class ShowmessageAction extends MailboxAction +{ - function handle($args) { + function handle($args) + { - Action::handle($args); + Action::handle($args); - $message = $this->get_message(); + $message = $this->get_message(); - if (!$message) { - $this->client_error(_('No such message.'), 404); - return; - } - - $cur = common_current_user(); - - if ($cur && ($cur->id == $message->from_profile || $cur->id == $message->to_profile)) { - $this->show_page($cur, 1); - } else { - $this->client_error(_('Only the sender and recipient may read this message.'), 403); - return; - } - } - - function get_message() { - $id = $this->trimmed('message'); - $message = Message::staticGet('id', $id); - return $message; - } - - function get_title($user, $page) { - $message = $this->get_message(); - if (!$message) { - return NULL; - } - - if ($user->id == $message->from_profile) { - $to = $message->getTo(); - $title = sprintf(_("Message to %1\$s on %2\$s"), - $to->nickname, - common_exact_date($message->created)); - } else if ($user->id == $message->to_profile) { - $from = $message->getFrom(); - $title = sprintf(_("Message from %1\$s on %2\$s"), - $from->nickname, - common_exact_date($message->created)); - } - return $title; - } + if (!$message) { + $this->client_error(_('No such message.'), 404); + return; + } + + $cur = common_current_user(); + + if ($cur && ($cur->id == $message->from_profile || $cur->id == $message->to_profile)) { + $this->show_page($cur, 1); + } else { + $this->client_error(_('Only the sender and recipient may read this message.'), 403); + return; + } + } + + function get_message() + { + $id = $this->trimmed('message'); + $message = Message::staticGet('id', $id); + return $message; + } + + function get_title($user, $page) + { + $message = $this->get_message(); + if (!$message) { + return null; + } + + if ($user->id == $message->from_profile) { + $to = $message->getTo(); + $title = sprintf(_("Message to %1\$s on %2\$s"), + $to->nickname, + common_exact_date($message->created)); + } else if ($user->id == $message->to_profile) { + $from = $message->getFrom(); + $title = sprintf(_("Message from %1\$s on %2\$s"), + $from->nickname, + common_exact_date($message->created)); + } + return $title; + } - function get_messages($user, $page) { - $message = new Message(); - $message->id = $this->trimmed('message'); - $message->find(); - return $message; - } - - function get_message_profile($message) { - $user = common_current_user(); - if ($user->id == $message->from_profile) { - return $message->getTo(); - } else if ($user->id == $message->to_profile) { - return $message->getFrom(); - } else { - # This shouldn't happen - return NULL; - } - } - - function get_instructions() { - return ''; - } - - function views_menu() { - return; - } + function get_messages($user, $page) + { + $message = new Message(); + $message->id = $this->trimmed('message'); + $message->find(); + return $message; + } + + function get_message_profile($message) + { + $user = common_current_user(); + if ($user->id == $message->from_profile) { + return $message->getTo(); + } else if ($user->id == $message->to_profile) { + return $message->getFrom(); + } else { + # This shouldn't happen + return null; + } + } + + function get_instructions() + { + return ''; + } + + function views_menu() + { + return; + } } -
\ No newline at end of file +
\ No newline at end of file diff --git a/actions/shownotice.php b/actions/shownotice.php index 6dea6d7bb..2df09cb3f 100644 --- a/actions/shownotice.php +++ b/actions/shownotice.php @@ -21,96 +21,104 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/stream.php'); -class ShownoticeAction extends StreamAction { +class ShownoticeAction extends StreamAction +{ - var $notice = NULL; - var $profile = NULL; - var $avatar = NULL; + var $notice = null; + var $profile = null; + var $avatar = null; - function prepare($args) { + function prepare($args) + { - parent::prepare($args); + parent::prepare($args); - $id = $this->arg('notice'); - $this->notice = Notice::staticGet($id); + $id = $this->arg('notice'); + $this->notice = Notice::staticGet($id); - if (!$this->notice) { - $this->client_error(_('No such notice.'), 404); - return false; - } + if (!$this->notice) { + $this->client_error(_('No such notice.'), 404); + return false; + } - $this->profile = $this->notice->getProfile(); + $this->profile = $this->notice->getProfile(); - if (!$this->profile) { - $this->server_error(_('Notice has no profile'), 500); - return false; - } + if (!$this->profile) { + $this->server_error(_('Notice has no profile'), 500); + return false; + } - $this->avatar = $this->profile->getAvatar(AVATAR_STREAM_SIZE); + $this->avatar = $this->profile->getAvatar(AVATAR_STREAM_SIZE); - return true; - } + return true; + } - function last_modified() { - return max(strtotime($this->notice->created), - strtotime($this->profile->modified), - ($this->avatar) ? strtotime($this->avatar->modified) : 0); - } + function last_modified() + { + return max(strtotime($this->notice->created), + strtotime($this->profile->modified), + ($this->avatar) ? strtotime($this->avatar->modified) : 0); + } - function etag() { - return 'W/"' . implode(':', array($this->arg('action'), - common_language(), - $this->notice->id, - strtotime($this->notice->created), - strtotime($this->profile->modified), - ($this->avatar) ? strtotime($this->avatar->modified) : 0)) . '"'; - } + function etag() + { + return 'W/"' . implode(':', array($this->arg('action'), + common_language(), + $this->notice->id, + strtotime($this->notice->created), + strtotime($this->profile->modified), + ($this->avatar) ? strtotime($this->avatar->modified) : 0)) . '"'; + } - function handle($args) { + function handle($args) + { - parent::handle($args); + parent::handle($args); - common_show_header(sprintf(_('%1$s\'s status on %2$s'), - $this->profile->nickname, - common_exact_date($this->notice->created)), - array($this, 'show_header'), NULL, - array($this, 'show_top')); + common_show_header(sprintf(_('%1$s\'s status on %2$s'), + $this->profile->nickname, + common_exact_date($this->notice->created)), + array($this, 'show_header'), null, + array($this, 'show_top')); - common_element_start('ul', array('id' => 'notices')); + common_element_start('ul', array('id' => 'notices')); $nli = new NoticeListItem($this->notice); $nli->show(); - common_element_end('ul'); - - common_show_footer(); - } - - function show_header() { - - $user = User::staticGet($this->profile->id); - - if (!$user) { - return; - } - - if ($user->emailmicroid && $user->email && $this->notice->uri) { - common_element('meta', array('name' => 'microid', - 'content' => "mailto+http:sha1:" . sha1(sha1('mailto:' . $user->email) . sha1($this->notice->uri)))); - } - - if ($user->jabbermicroid && $user->jabber && $this->notice->uri) { - common_element('meta', array('name' => 'microid', - 'content' => "xmpp+http:sha1:" . sha1(sha1('xmpp:' . $user->jabber) . sha1($this->notice->uri)))); - } - } - - function show_top() { - $cur = common_current_user(); - if ($cur && $cur->id == $this->profile->id) { - common_notice_form(); - } - } - - function no_such_notice() { - common_user_error(_('No such notice.')); - } + common_element_end('ul'); + + common_show_footer(); + } + + function show_header() + { + + $user = User::staticGet($this->profile->id); + + if (!$user) { + return; + } + + if ($user->emailmicroid && $user->email && $this->notice->uri) { + common_element('meta', array('name' => 'microid', + 'content' => "mailto+http:sha1:" . sha1(sha1('mailto:' . $user->email) . sha1($this->notice->uri)))); + } + + if ($user->jabbermicroid && $user->jabber && $this->notice->uri) { + common_element('meta', array('name' => 'microid', + 'content' => "xmpp+http:sha1:" . sha1(sha1('xmpp:' . $user->jabber) . sha1($this->notice->uri)))); + } + } + + function show_top() + { + $cur = common_current_user(); + if ($cur && $cur->id == $this->profile->id) { + common_notice_form(); + } + } + + function no_such_notice() + { + common_user_error(_('No such notice.')); + } } diff --git a/actions/showstream.php b/actions/showstream.php index 6d6225661..e4e5d96d1 100644 --- a/actions/showstream.php +++ b/actions/showstream.php @@ -24,14 +24,16 @@ require_once(INSTALLDIR.'/lib/stream.php'); define('SUBSCRIPTIONS_PER_ROW', 4); define('SUBSCRIPTIONS', 80); -class ShowstreamAction extends StreamAction { +class ShowstreamAction extends StreamAction +{ - function handle($args) { + function handle($args) + { - parent::handle($args); + parent::handle($args); $nickname_arg = $this->arg('nickname'); - $nickname = common_canonical_nickname($nickname_arg); + $nickname = common_canonical_nickname($nickname_arg); # Permanent redirect on non-canonical nickname @@ -44,163 +46,168 @@ class ShowstreamAction extends StreamAction { return; } - $user = User::staticGet('nickname', $nickname); - - if (!$user) { - $this->no_such_user(); - return; - } - - $profile = $user->getProfile(); - - if (!$profile) { - common_server_error(_('User has no profile.')); - return; - } - - # Looks like we're good; start output - - # For YADIS discovery, we also have a <meta> tag - - header('X-XRDS-Location: '. common_local_url('xrds', array('nickname' => - $user->nickname))); - - common_show_header($profile->nickname, - array($this, 'show_header'), $user, - array($this, 'show_top')); - - $this->show_profile($profile); - - $this->show_notices($user); - - common_show_footer(); - } - - function show_top($user) { - $cur = common_current_user(); - - if ($cur && $cur->id == $user->id) { - common_notice_form('showstream'); - } - - $this->views_menu(); - - $this->show_feeds_list(array(0=>array('href'=>common_local_url('userrss', array('nickname' => $user->nickname)), - 'type' => 'rss', - 'version' => 'RSS 1.0', - 'item' => 'notices'), - 1=>array('href'=>common_local_url('usertimeline', array('nickname' => $user->nickname)), - 'type' => 'atom', - 'version' => 'Atom 1.0', - 'item' => 'usertimeline'), - - 2=>array('href'=>common_local_url('foaf',array('nickname' => $user->nickname)), - 'type' => 'rdf', - 'version' => 'FOAF', - 'item' => 'foaf'))); - } - - function show_header($user) { - # Feeds - common_element('link', array('rel' => 'alternate', - 'href' => common_local_url('api', - array('apiaction' => 'statuses', - 'method' => 'user_timeline.rss', - 'argument' => $user->nickname)), - 'type' => 'application/rss+xml', - 'title' => sprintf(_('Notice feed for %s'), $user->nickname))); - common_element('link', array('rel' => 'alternate feed', - 'href' => common_local_url('api', - array('apiaction' => 'statuses', - 'method' => 'user_timeline.atom', - 'argument' => $user->nickname)), - 'type' => 'application/atom+xml', - 'title' => sprintf(_('Notice feed for %s'), $user->nickname))); - common_element('link', array('rel' => 'alternate', - 'href' => common_local_url('userrss', array('nickname' => - $user->nickname)), - 'type' => 'application/rdf+xml', - 'title' => sprintf(_('Notice feed for %s'), $user->nickname))); - # FOAF - common_element('link', array('rel' => 'meta', - 'href' => common_local_url('foaf', array('nickname' => - $user->nickname)), - 'type' => 'application/rdf+xml', - 'title' => 'FOAF')); - # for remote subscriptions etc. - common_element('meta', array('http-equiv' => 'X-XRDS-Location', - 'content' => common_local_url('xrds', array('nickname' => - $user->nickname)))); - $profile = $user->getProfile(); - if ($profile->bio) { - common_element('meta', array('name' => 'description', - 'content' => $profile->bio)); - } - - if ($user->emailmicroid && $user->email && $profile->profileurl) { - common_element('meta', array('name' => 'microid', - 'content' => "mailto+http:sha1:" . sha1(sha1('mailto:' . $user->email) . sha1($profile->profileurl)))); - } - if ($user->jabbermicroid && $user->jabber && $profile->profileurl) { - common_element('meta', array('name' => 'microid', - 'content' => "xmpp+http:sha1:" . sha1(sha1('xmpp:' . $user->jabber) . sha1($profile->profileurl)))); - } - - # See https://wiki.mozilla.org/Microsummaries - - common_element('link', array('rel' => 'microsummary', - 'href' => common_local_url('microsummary', - array('nickname' => $profile->nickname)))); - } - - function no_such_user() { - $this->client_error(_('No such user.'), 404); - } - - function show_profile($profile) { - - common_element_start('div', array('id' => 'profile', 'class' => 'vcard')); - - $this->show_personal($profile); - - $this->show_last_notice($profile); - - $cur = common_current_user(); - - $this->show_subscriptions($profile); - - common_element_end('div'); - } - - function show_personal($profile) { - - $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); - common_element_start('div', array('id' => 'profile_avatar')); - common_element('img', array('src' => ($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_PROFILE_SIZE), - 'class' => 'avatar profile photo', - 'width' => AVATAR_PROFILE_SIZE, - 'height' => AVATAR_PROFILE_SIZE, - 'alt' => $profile->nickname)); + $user = User::staticGet('nickname', $nickname); + + if (!$user) { + $this->no_such_user(); + return; + } + + $profile = $user->getProfile(); + + if (!$profile) { + common_server_error(_('User has no profile.')); + return; + } + + # Looks like we're good; start output + + # For YADIS discovery, we also have a <meta> tag + + header('X-XRDS-Location: '. common_local_url('xrds', array('nickname' => + $user->nickname))); + + common_show_header($profile->nickname, + array($this, 'show_header'), $user, + array($this, 'show_top')); + + $this->show_profile($profile); + + $this->show_notices($user); + + common_show_footer(); + } + + function show_top($user) + { + $cur = common_current_user(); + + if ($cur && $cur->id == $user->id) { + common_notice_form('showstream'); + } + + $this->views_menu(); + + $this->show_feeds_list(array(0=>array('href'=>common_local_url('userrss', array('nickname' => $user->nickname)), + 'type' => 'rss', + 'version' => 'RSS 1.0', + 'item' => 'notices'), + 1=>array('href'=>common_local_url('usertimeline', array('nickname' => $user->nickname)), + 'type' => 'atom', + 'version' => 'Atom 1.0', + 'item' => 'usertimeline'), + + 2=>array('href'=>common_local_url('foaf',array('nickname' => $user->nickname)), + 'type' => 'rdf', + 'version' => 'FOAF', + 'item' => 'foaf'))); + } + + function show_header($user) + { + # Feeds + common_element('link', array('rel' => 'alternate', + 'href' => common_local_url('api', + array('apiaction' => 'statuses', + 'method' => 'user_timeline.rss', + 'argument' => $user->nickname)), + 'type' => 'application/rss+xml', + 'title' => sprintf(_('Notice feed for %s'), $user->nickname))); + common_element('link', array('rel' => 'alternate feed', + 'href' => common_local_url('api', + array('apiaction' => 'statuses', + 'method' => 'user_timeline.atom', + 'argument' => $user->nickname)), + 'type' => 'application/atom+xml', + 'title' => sprintf(_('Notice feed for %s'), $user->nickname))); + common_element('link', array('rel' => 'alternate', + 'href' => common_local_url('userrss', array('nickname' => + $user->nickname)), + 'type' => 'application/rdf+xml', + 'title' => sprintf(_('Notice feed for %s'), $user->nickname))); + # FOAF + common_element('link', array('rel' => 'meta', + 'href' => common_local_url('foaf', array('nickname' => + $user->nickname)), + 'type' => 'application/rdf+xml', + 'title' => 'FOAF')); + # for remote subscriptions etc. + common_element('meta', array('http-equiv' => 'X-XRDS-Location', + 'content' => common_local_url('xrds', array('nickname' => + $user->nickname)))); + $profile = $user->getProfile(); + if ($profile->bio) { + common_element('meta', array('name' => 'description', + 'content' => $profile->bio)); + } + + if ($user->emailmicroid && $user->email && $profile->profileurl) { + common_element('meta', array('name' => 'microid', + 'content' => "mailto+http:sha1:" . sha1(sha1('mailto:' . $user->email) . sha1($profile->profileurl)))); + } + if ($user->jabbermicroid && $user->jabber && $profile->profileurl) { + common_element('meta', array('name' => 'microid', + 'content' => "xmpp+http:sha1:" . sha1(sha1('xmpp:' . $user->jabber) . sha1($profile->profileurl)))); + } + + # See https://wiki.mozilla.org/Microsummaries + + common_element('link', array('rel' => 'microsummary', + 'href' => common_local_url('microsummary', + array('nickname' => $profile->nickname)))); + } + + function no_such_user() + { + $this->client_error(_('No such user.'), 404); + } + + function show_profile($profile) + { + + common_element_start('div', array('id' => 'profile', 'class' => 'vcard')); + + $this->show_personal($profile); + + $this->show_last_notice($profile); + + $cur = common_current_user(); + + $this->show_subscriptions($profile); + + common_element_end('div'); + } + + function show_personal($profile) + { + + $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); + common_element_start('div', array('id' => 'profile_avatar')); + common_element('img', array('src' => ($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_PROFILE_SIZE), + 'class' => 'avatar profile photo', + 'width' => AVATAR_PROFILE_SIZE, + 'height' => AVATAR_PROFILE_SIZE, + 'alt' => $profile->nickname)); common_element_start('ul', array('id' => 'profile_actions')); common_element_start('li', array('id' => 'profile_subscribe')); - $cur = common_current_user(); - if ($cur) { - if ($cur->id != $profile->id) { - if ($cur->isSubscribed($profile)) { - common_unsubscribe_form($profile); - } else { - common_subscribe_form($profile); - } - } - } else { - $this->show_remote_subscribe_link($profile); - } + $cur = common_current_user(); + if ($cur) { + if ($cur->id != $profile->id) { + if ($cur->isSubscribed($profile)) { + common_unsubscribe_form($profile); + } else { + common_subscribe_form($profile); + } + } + } else { + $this->show_remote_subscribe_link($profile); + } common_element_end('li'); - $user = User::staticGet('id', $profile->id); - common_profile_new_message_nudge($cur, $user, $profile); + $user = User::staticGet('id', $profile->id); + common_profile_new_message_nudge($cur, $user, $profile); if ($cur && $cur->id != $profile->id) { $blocked = $cur->hasBlocked($profile); @@ -215,236 +222,246 @@ class ShowstreamAction extends StreamAction { common_element_end('li'); } - common_element_end('ul'); - - common_element_end('div'); - - common_element_start('div', array('id' => 'profile_information')); - - if ($profile->fullname) { - common_element('h1', array('class' => 'fn'), $profile->fullname . ' (' . $profile->nickname . ')'); - } else { - common_element('h1', array('class' => 'fn nickname'), $profile->nickname); - } - - if ($profile->location) { - common_element('p', 'location', $profile->location); - } - if ($profile->bio) { - common_element('p', 'description note', $profile->bio); - } - if ($profile->homepage) { - common_element_start('p', 'website'); - common_element('a', array('href' => $profile->homepage, - 'rel' => 'me', 'class' => 'url'), - $profile->homepage); - common_element_end('p'); - } - - $this->show_statistics($profile); - - common_element_end('div'); - } - - function show_remote_subscribe_link($profile) { - $url = common_local_url('remotesubscribe', - array('nickname' => $profile->nickname)); - common_element('a', array('href' => $url, - 'id' => 'remotesubscribe'), - _('Subscribe')); - } - - function show_unsubscribe_form($profile) { - common_element_start('form', array('id' => 'unsubscribe', 'method' => 'post', - 'action' => common_local_url('unsubscribe'))); - common_hidden('token', common_session_token()); - common_element('input', array('id' => 'unsubscribeto', - 'name' => 'unsubscribeto', - 'type' => 'hidden', - 'value' => $profile->nickname)); - common_element('input', array('type' => 'submit', - 'class' => 'submit', - 'value' => _('Unsubscribe'))); - common_element_end('form'); - } - - function show_subscriptions($profile) { - global $config; - - $subs = DB_DataObject::factory('subscription'); - $subs->subscriber = $profile->id; - $subs->whereAdd('subscribed != ' . $profile->id); - - $subs->orderBy('created DESC'); - - # We ask for an extra one to know if we need to do another page - - $subs->limit(0, SUBSCRIPTIONS + 1); - - $subs_count = $subs->find(); - - common_element_start('div', array('id' => 'subscriptions')); - - common_element('h2', NULL, _('Subscriptions')); - - if ($subs_count > 0) { - - common_element_start('ul', array('id' => 'subscriptions_avatars')); - - for ($i = 0; $i < min($subs_count, SUBSCRIPTIONS); $i++) { - - if (!$subs->fetch()) { - common_debug('Weirdly, broke out of subscriptions loop early', __FILE__); - break; - } - - $other = Profile::staticGet($subs->subscribed); - - if (!$other) { - common_log_db_error($subs, 'SELECT', __FILE__); - continue; - } - - common_element_start('li', 'vcard'); - common_element_start('a', array('title' => ($other->fullname) ? - $other->fullname : - $other->nickname, - 'href' => $other->profileurl, - 'rel' => 'contact', - 'class' => 'subscription fn url')); - $avatar = $other->getAvatar(AVATAR_MINI_SIZE); - common_element('img', array('src' => (($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_MINI_SIZE)), - 'width' => AVATAR_MINI_SIZE, - 'height' => AVATAR_MINI_SIZE, - 'class' => 'avatar mini photo', - 'alt' => ($other->fullname) ? - $other->fullname : - $other->nickname)); - common_element_end('a'); - common_element_end('li'); - } - - common_element_end('ul'); - } - - if ($subs_count > SUBSCRIPTIONS) { - common_element_start('p', array('id' => 'subscriptions_viewall')); - - common_element('a', array('href' => common_local_url('subscriptions', - array('nickname' => $profile->nickname)), - 'class' => 'moresubscriptions'), - _('All subscriptions')); - common_element_end('p'); - } - - common_element_end('div'); - } - - function show_statistics($profile) { - - // XXX: WORM cache this - $subs = DB_DataObject::factory('subscription'); - $subs->subscriber = $profile->id; - $subs_count = (int) $subs->count() - 1; - - $subbed = DB_DataObject::factory('subscription'); - $subbed->subscribed = $profile->id; - $subbed_count = (int) $subbed->count() - 1; - - $notices = DB_DataObject::factory('notice'); - $notices->profile_id = $profile->id; - $notice_count = (int) $notices->count(); - - common_element_start('div', 'statistics'); - common_element('h2', 'statistics', _('Statistics')); - - # Other stats...? - common_element_start('dl', 'statistics'); - common_element('dt', 'membersince', _('Member since')); - common_element('dd', 'membersince', date('j M Y', - strtotime($profile->created))); - - common_element_start('dt', 'subscriptions'); - common_element('a', array('href' => common_local_url('subscriptions', - array('nickname' => $profile->nickname))), - _('Subscriptions')); - common_element_end('dt'); - common_element('dd', 'subscriptions', (is_int($subs_count)) ? $subs_count : '0'); - common_element_start('dt', 'subscribers'); - common_element('a', array('href' => common_local_url('subscribers', - array('nickname' => $profile->nickname))), - _('Subscribers')); - common_element_end('dt'); - common_element('dd', 'subscribers', (is_int($subbed_count)) ? $subbed_count : '0'); - common_element('dt', 'notices', _('Notices')); - common_element('dd', 'notices', (is_int($notice_count)) ? $notice_count : '0'); - # XXX: link these to something - common_element('dt', 'tags', _('Tags')); - common_element_start('dd', 'tags'); - $tags = Profile_tag::getTags($profile->id, $profile->id); - - common_element_start('ul', 'tags xoxo'); - foreach ($tags as $tag) { - common_element_start('li'); - common_element('a', array('rel' => 'bookmark tag', - 'href' => common_local_url('peopletag', - array('tag' => $tag))), - $tag); - common_element_end('li'); - } - common_element_end('ul'); - common_element_end('dd'); - - common_element_end('dl'); - - common_element_end('div'); - } - - function show_notices($user) { - - $page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; - - $notice = $user->getNotices(($page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1); + common_element_end('ul'); + + common_element_end('div'); + + common_element_start('div', array('id' => 'profile_information')); + + if ($profile->fullname) { + common_element('h1', array('class' => 'fn'), $profile->fullname . ' (' . $profile->nickname . ')'); + } else { + common_element('h1', array('class' => 'fn nickname'), $profile->nickname); + } + + if ($profile->location) { + common_element('p', 'location', $profile->location); + } + if ($profile->bio) { + common_element('p', 'description note', $profile->bio); + } + if ($profile->homepage) { + common_element_start('p', 'website'); + common_element('a', array('href' => $profile->homepage, + 'rel' => 'me', 'class' => 'url'), + $profile->homepage); + common_element_end('p'); + } + + $this->show_statistics($profile); + + common_element_end('div'); + } + + function show_remote_subscribe_link($profile) + { + $url = common_local_url('remotesubscribe', + array('nickname' => $profile->nickname)); + common_element('a', array('href' => $url, + 'id' => 'remotesubscribe'), + _('Subscribe')); + } + + function show_unsubscribe_form($profile) + { + common_element_start('form', array('id' => 'unsubscribe', 'method' => 'post', + 'action' => common_local_url('unsubscribe'))); + common_hidden('token', common_session_token()); + common_element('input', array('id' => 'unsubscribeto', + 'name' => 'unsubscribeto', + 'type' => 'hidden', + 'value' => $profile->nickname)); + common_element('input', array('type' => 'submit', + 'class' => 'submit', + 'value' => _('Unsubscribe'))); + common_element_end('form'); + } + + function show_subscriptions($profile) + { + global $config; + + $subs = DB_DataObject::factory('subscription'); + $subs->subscriber = $profile->id; + $subs->whereAdd('subscribed != ' . $profile->id); + + $subs->orderBy('created DESC'); + + # We ask for an extra one to know if we need to do another page + + $subs->limit(0, SUBSCRIPTIONS + 1); + + $subs_count = $subs->find(); + + common_element_start('div', array('id' => 'subscriptions')); + + common_element('h2', null, _('Subscriptions')); + + if ($subs_count > 0) { + + common_element_start('ul', array('id' => 'subscriptions_avatars')); + + for ($i = 0; $i < min($subs_count, SUBSCRIPTIONS); $i++) { + + if (!$subs->fetch()) { + common_debug('Weirdly, broke out of subscriptions loop early', __FILE__); + break; + } + + $other = Profile::staticGet($subs->subscribed); + + if (!$other) { + common_log_db_error($subs, 'SELECT', __FILE__); + continue; + } + + common_element_start('li', 'vcard'); + common_element_start('a', array('title' => ($other->fullname) ? + $other->fullname : + $other->nickname, + 'href' => $other->profileurl, + 'rel' => 'contact', + 'class' => 'subscription fn url')); + $avatar = $other->getAvatar(AVATAR_MINI_SIZE); + common_element('img', array('src' => (($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_MINI_SIZE)), + 'width' => AVATAR_MINI_SIZE, + 'height' => AVATAR_MINI_SIZE, + 'class' => 'avatar mini photo', + 'alt' => ($other->fullname) ? + $other->fullname : + $other->nickname)); + common_element_end('a'); + common_element_end('li'); + } + + common_element_end('ul'); + } + + if ($subs_count > SUBSCRIPTIONS) { + common_element_start('p', array('id' => 'subscriptions_viewall')); + + common_element('a', array('href' => common_local_url('subscriptions', + array('nickname' => $profile->nickname)), + 'class' => 'moresubscriptions'), + _('All subscriptions')); + common_element_end('p'); + } + + common_element_end('div'); + } + + function show_statistics($profile) + { + + // XXX: WORM cache this + $subs = DB_DataObject::factory('subscription'); + $subs->subscriber = $profile->id; + $subs_count = (int) $subs->count() - 1; + + $subbed = DB_DataObject::factory('subscription'); + $subbed->subscribed = $profile->id; + $subbed_count = (int) $subbed->count() - 1; + + $notices = DB_DataObject::factory('notice'); + $notices->profile_id = $profile->id; + $notice_count = (int) $notices->count(); + + common_element_start('div', 'statistics'); + common_element('h2', 'statistics', _('Statistics')); + + # Other stats...? + common_element_start('dl', 'statistics'); + common_element('dt', 'membersince', _('Member since')); + common_element('dd', 'membersince', date('j M Y', + strtotime($profile->created))); + + common_element_start('dt', 'subscriptions'); + common_element('a', array('href' => common_local_url('subscriptions', + array('nickname' => $profile->nickname))), + _('Subscriptions')); + common_element_end('dt'); + common_element('dd', 'subscriptions', (is_int($subs_count)) ? $subs_count : '0'); + common_element_start('dt', 'subscribers'); + common_element('a', array('href' => common_local_url('subscribers', + array('nickname' => $profile->nickname))), + _('Subscribers')); + common_element_end('dt'); + common_element('dd', 'subscribers', (is_int($subbed_count)) ? $subbed_count : '0'); + common_element('dt', 'notices', _('Notices')); + common_element('dd', 'notices', (is_int($notice_count)) ? $notice_count : '0'); + # XXX: link these to something + common_element('dt', 'tags', _('Tags')); + common_element_start('dd', 'tags'); + $tags = Profile_tag::getTags($profile->id, $profile->id); + + common_element_start('ul', 'tags xoxo'); + foreach ($tags as $tag) { + common_element_start('li'); + common_element('a', array('rel' => 'bookmark tag', + 'href' => common_local_url('peopletag', + array('tag' => $tag))), + $tag); + common_element_end('li'); + } + common_element_end('ul'); + common_element_end('dd'); + + common_element_end('dl'); + + common_element_end('div'); + } + + function show_notices($user) + { + + $page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; + + $notice = $user->getNotices(($page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1); $pnl = new ProfileNoticeList($notice); $cnt = $pnl->show(); - common_pagination($page>1, $cnt>NOTICES_PER_PAGE, $page, - 'showstream', array('nickname' => $user->nickname)); - } - - function show_last_notice($profile) { - - common_element('h2', NULL, _('Currently')); - - $notice = $profile->getCurrentNotice(); - - if ($notice) { - # FIXME: URL, image, video, audio - common_element_start('p', array('class' => 'notice_current')); - if ($notice->rendered) { - common_raw($notice->rendered); - } else { - # XXX: may be some uncooked notices in the DB, - # we cook them right now. This can probably disappear in future - # versions (>> 0.4.x) - common_raw(common_render_content($notice->content, $notice)); - } - common_element_end('p'); - } - } + common_pagination($page>1, $cnt>NOTICES_PER_PAGE, $page, + 'showstream', array('nickname' => $user->nickname)); + } + + function show_last_notice($profile) + { + + common_element('h2', null, _('Currently')); + + $notice = $profile->getCurrentNotice(); + + if ($notice) { + # FIXME: URL, image, video, audio + common_element_start('p', array('class' => 'notice_current')); + if ($notice->rendered) { + common_raw($notice->rendered); + } else { + # XXX: may be some uncooked notices in the DB, + # we cook them right now. This can probably disappear in future + # versions (>> 0.4.x) + common_raw(common_render_content($notice->content, $notice)); + } + common_element_end('p'); + } + } } # We don't show the author for a profile, since we already know who it is! -class ProfileNoticeList extends NoticeList { - function new_list_item($notice) { +class ProfileNoticeList extends NoticeList +{ + function newListItem($notice) + { return new ProfileNoticeListItem($notice); } } -class ProfileNoticeListItem extends NoticeListItem { - function show_author() { +class ProfileNoticeListItem extends NoticeListItem +{ + function showAuthor() + { return; } } diff --git a/actions/smssettings.php b/actions/smssettings.php index 5db26730a..fad71135c 100644 --- a/actions/smssettings.php +++ b/actions/smssettings.php @@ -22,310 +22,322 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/settingsaction.php'); require_once(INSTALLDIR.'/actions/emailsettings.php'); -class SmssettingsAction extends EmailsettingsAction { - - function get_instructions() { - return _('You can receive SMS messages through email from %%site.name%%.'); - } - - function show_form($msg=NULL, $success=false) { - $user = common_current_user(); - $this->form_header(_('SMS Settings'), $msg, $success); - common_element_start('form', array('method' => 'post', - 'id' => 'smssettings', - 'action' => - common_local_url('smssettings'))); - common_hidden('token', common_session_token()); - common_element('h2', NULL, _('Address')); - - if ($user->sms) { - common_element_start('p'); - $carrier = $user->getCarrier(); - common_element('span', 'address confirmed', $user->sms . ' (' . $carrier->name . ')'); - common_element('span', 'input_instructions', - _('Current confirmed SMS-enabled phone number.')); - common_hidden('sms', $user->sms); - common_hidden('carrier', $user->carrier); - common_element_end('p'); - common_submit('remove', _('Remove')); - } else { - $confirm = $this->get_confirmation(); - if ($confirm) { - $carrier = Sms_carrier::staticGet($confirm->address_extra); - common_element_start('p'); - common_element('span', 'address unconfirmed', $confirm->address . ' (' . $carrier->name . ')'); - common_element('span', 'input_instructions', - _('Awaiting confirmation on this phone number.')); - common_hidden('sms', $confirm->address); - common_hidden('carrier', $confirm->address_extra); - common_element_end('p'); - common_submit('cancel', _('Cancel')); - common_input('code', _('Confirmation code'), NULL, - _('Enter the code you received on your phone.')); - common_submit('confirm', _('Confirm')); - } else { - common_input('sms', _('SMS Phone number'), - ($this->arg('sms')) ? $this->arg('sms') : NULL, - _('Phone number, no punctuation or spaces, with area code')); - $this->carrier_select(); - common_submit('add', _('Add')); - } - } - - if ($user->sms) { - common_element('h2', NULL, _('Incoming email')); - - if ($user->incomingemail) { - common_element_start('p'); - common_element('span', 'address', $user->incomingemail); - common_element('span', 'input_instructions', - _('Send email to this address to post new notices.')); - common_element_end('p'); - common_submit('removeincoming', _('Remove')); - } - - common_element_start('p'); - common_element('span', 'input_instructions', - _('Make a new email address for posting to; cancels the old one.')); - common_element_end('p'); - common_submit('newincoming', _('New')); - } - - common_element('h2', NULL, _('Preferences')); - - common_checkbox('smsnotify', - _('Send me notices through SMS; I understand I may incur exorbitant charges from my carrier.'), - $user->smsnotify); - - common_submit('save', _('Save')); - - common_element_end('form'); - common_show_footer(); - } - - function get_confirmation() { - $user = common_current_user(); - $confirm = new Confirm_address(); - $confirm->user_id = $user->id; - $confirm->address_type = 'sms'; - if ($confirm->find(TRUE)) { - return $confirm; - } else { - return NULL; - } - } - - function handle_post() { - - # CSRF protection - - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->show_form(_('There was a problem with your session token. Try again, please.')); - return; - } - - if ($this->arg('save')) { - $this->save_preferences(); - } else if ($this->arg('add')) { - $this->add_address(); - } else if ($this->arg('cancel')) { - $this->cancel_confirmation(); - } else if ($this->arg('remove')) { - $this->remove_address(); - } else if ($this->arg('removeincoming')) { - $this->remove_incoming(); - } else if ($this->arg('newincoming')) { - $this->new_incoming(); - } else if ($this->arg('confirm')) { - $this->confirm_code(); - } else { - $this->show_form(_('Unexpected form submission.')); - } - } - - function save_preferences() { - - $smsnotify = $this->boolean('smsnotify'); - - $user = common_current_user(); - - assert(!is_null($user)); # should already be checked - - $user->query('BEGIN'); - - $original = clone($user); - - $user->smsnotify = $smsnotify; - - $result = $user->update($original); - - if ($result === FALSE) { - common_log_db_error($user, 'UPDATE', __FILE__); - common_server_error(_('Couldn\'t update user.')); - return; - } - - $user->query('COMMIT'); - - $this->show_form(_('Preferences saved.'), true); - } - - function add_address() { - - $user = common_current_user(); - - $sms = $this->trimmed('sms'); - $carrier_id = $this->trimmed('carrier'); - - # Some validation - - if (!$sms) { - $this->show_form(_('No phone number.')); - return; - } - - if (!$carrier_id) { - $this->show_form(_('No carrier selected.')); - return; - } - - $sms = common_canonical_sms($sms); - - if ($user->sms == $sms) { - $this->show_form(_('That is already your phone number.')); - return; - } else if ($this->sms_exists($sms)) { - $this->show_form(_('That phone number already belongs to another user.')); - return; - } - - $confirm = new Confirm_address(); - $confirm->address = $sms; - $confirm->address_extra = $carrier_id; - $confirm->address_type = 'sms'; - $confirm->user_id = $user->id; - $confirm->code = common_confirmation_code(40); - - $result = $confirm->insert(); - - if ($result === FALSE) { - common_log_db_error($confirm, 'INSERT', __FILE__); - common_server_error(_('Couldn\'t insert confirmation code.')); - return; - } - - $carrier = Sms_carrier::staticGet($carrier_id); - - mail_confirm_sms($confirm->code, - $user->nickname, - $carrier->toEmailAddress($sms)); - - $msg = _('A confirmation code was sent to the phone number you added. Check your inbox (and spam box!) for the code and instructions on how to use it.'); - - $this->show_form($msg, TRUE); - } - - function cancel_confirmation() { - - $sms = $this->trimmed('sms'); - $carrier = $this->trimmed('carrier'); - - $confirm = $this->get_confirmation(); - - if (!$confirm) { - $this->show_form(_('No pending confirmation to cancel.')); - return; - } - if ($confirm->address != $sms) { - $this->show_form(_('That is the wrong confirmation number.')); - return; - } +class SmssettingsAction extends EmailsettingsAction +{ + + function get_instructions() + { + return _('You can receive SMS messages through email from %%site.name%%.'); + } + + function show_form($msg=null, $success=false) + { + $user = common_current_user(); + $this->form_header(_('SMS Settings'), $msg, $success); + common_element_start('form', array('method' => 'post', + 'id' => 'smssettings', + 'action' => + common_local_url('smssettings'))); + common_hidden('token', common_session_token()); + common_element('h2', null, _('Address')); + + if ($user->sms) { + common_element_start('p'); + $carrier = $user->getCarrier(); + common_element('span', 'address confirmed', $user->sms . ' (' . $carrier->name . ')'); + common_element('span', 'input_instructions', + _('Current confirmed SMS-enabled phone number.')); + common_hidden('sms', $user->sms); + common_hidden('carrier', $user->carrier); + common_element_end('p'); + common_submit('remove', _('Remove')); + } else { + $confirm = $this->get_confirmation(); + if ($confirm) { + $carrier = Sms_carrier::staticGet($confirm->address_extra); + common_element_start('p'); + common_element('span', 'address unconfirmed', $confirm->address . ' (' . $carrier->name . ')'); + common_element('span', 'input_instructions', + _('Awaiting confirmation on this phone number.')); + common_hidden('sms', $confirm->address); + common_hidden('carrier', $confirm->address_extra); + common_element_end('p'); + common_submit('cancel', _('Cancel')); + common_input('code', _('Confirmation code'), null, + _('Enter the code you received on your phone.')); + common_submit('confirm', _('Confirm')); + } else { + common_input('sms', _('SMS Phone number'), + ($this->arg('sms')) ? $this->arg('sms') : null, + _('Phone number, no punctuation or spaces, with area code')); + $this->carrier_select(); + common_submit('add', _('Add')); + } + } + + if ($user->sms) { + common_element('h2', null, _('Incoming email')); + + if ($user->incomingemail) { + common_element_start('p'); + common_element('span', 'address', $user->incomingemail); + common_element('span', 'input_instructions', + _('Send email to this address to post new notices.')); + common_element_end('p'); + common_submit('removeincoming', _('Remove')); + } + + common_element_start('p'); + common_element('span', 'input_instructions', + _('Make a new email address for posting to; cancels the old one.')); + common_element_end('p'); + common_submit('newincoming', _('New')); + } + + common_element('h2', null, _('Preferences')); + + common_checkbox('smsnotify', + _('Send me notices through SMS; I understand I may incur exorbitant charges from my carrier.'), + $user->smsnotify); + + common_submit('save', _('Save')); + + common_element_end('form'); + common_show_footer(); + } + + function get_confirmation() + { + $user = common_current_user(); + $confirm = new Confirm_address(); + $confirm->user_id = $user->id; + $confirm->address_type = 'sms'; + if ($confirm->find(true)) { + return $confirm; + } else { + return null; + } + } + + function handle_post() + { + + # CSRF protection + + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->show_form(_('There was a problem with your session token. Try again, please.')); + return; + } + + if ($this->arg('save')) { + $this->save_preferences(); + } else if ($this->arg('add')) { + $this->add_address(); + } else if ($this->arg('cancel')) { + $this->cancel_confirmation(); + } else if ($this->arg('remove')) { + $this->remove_address(); + } else if ($this->arg('removeincoming')) { + $this->remove_incoming(); + } else if ($this->arg('newincoming')) { + $this->new_incoming(); + } else if ($this->arg('confirm')) { + $this->confirm_code(); + } else { + $this->show_form(_('Unexpected form submission.')); + } + } + + function save_preferences() + { + + $smsnotify = $this->boolean('smsnotify'); + + $user = common_current_user(); + + assert(!is_null($user)); # should already be checked + + $user->query('BEGIN'); + + $original = clone($user); + + $user->smsnotify = $smsnotify; + + $result = $user->update($original); + + if ($result === false) { + common_log_db_error($user, 'UPDATE', __FILE__); + common_server_error(_('Couldn\'t update user.')); + return; + } + + $user->query('COMMIT'); + + $this->show_form(_('Preferences saved.'), true); + } + + function add_address() + { + + $user = common_current_user(); + + $sms = $this->trimmed('sms'); + $carrier_id = $this->trimmed('carrier'); + + # Some validation + + if (!$sms) { + $this->show_form(_('No phone number.')); + return; + } + + if (!$carrier_id) { + $this->show_form(_('No carrier selected.')); + return; + } + + $sms = common_canonical_sms($sms); + + if ($user->sms == $sms) { + $this->show_form(_('That is already your phone number.')); + return; + } else if ($this->sms_exists($sms)) { + $this->show_form(_('That phone number already belongs to another user.')); + return; + } + + $confirm = new Confirm_address(); + $confirm->address = $sms; + $confirm->address_extra = $carrier_id; + $confirm->address_type = 'sms'; + $confirm->user_id = $user->id; + $confirm->code = common_confirmation_code(40); + + $result = $confirm->insert(); + + if ($result === false) { + common_log_db_error($confirm, 'INSERT', __FILE__); + common_server_error(_('Couldn\'t insert confirmation code.')); + return; + } + + $carrier = Sms_carrier::staticGet($carrier_id); + + mail_confirm_sms($confirm->code, + $user->nickname, + $carrier->toEmailAddress($sms)); + + $msg = _('A confirmation code was sent to the phone number you added. Check your inbox (and spam box!) for the code and instructions on how to use it.'); + + $this->show_form($msg, true); + } + + function cancel_confirmation() + { + + $sms = $this->trimmed('sms'); + $carrier = $this->trimmed('carrier'); + + $confirm = $this->get_confirmation(); + + if (!$confirm) { + $this->show_form(_('No pending confirmation to cancel.')); + return; + } + if ($confirm->address != $sms) { + $this->show_form(_('That is the wrong confirmation number.')); + return; + } $result = $confirm->delete(); if (!$result) { - common_log_db_error($confirm, 'DELETE', __FILE__); + common_log_db_error($confirm, 'DELETE', __FILE__); $this->server_error(_('Couldn\'t delete email confirmation.')); return; } - $this->show_form(_('Confirmation cancelled.'), TRUE); - } - - function remove_address() { - - $user = common_current_user(); - $sms = $this->arg('sms'); - $carrier = $this->arg('carrier'); - - # Maybe an old tab open...? - - if ($user->sms != $sms) { - $this->show_form(_('That is not your phone number.')); - return; - } - - $user->query('BEGIN'); - $original = clone($user); - $user->sms = NULL; - $user->carrier = NULL; - $user->smsemail = NULL; - $result = $user->updateKeys($original); - if (!$result) { - common_log_db_error($user, 'UPDATE', __FILE__); - common_server_error(_('Couldn\'t update user.')); - return; - } - $user->query('COMMIT'); - - $this->show_form(_('The address was removed.'), TRUE); - } - - function sms_exists($sms) { - $user = common_current_user(); - $other = User::staticGet('sms', $sms); - if (!$other) { - return false; - } else { - return $other->id != $user->id; - } - } - - function carrier_select() { - $carrier = new Sms_carrier(); - $cnt = $carrier->find(); - - common_element_start('p'); - common_element('label', array('for' => 'carrier')); - common_element_start('select', array('name' => 'carrier', - 'id' => 'carrier')); - common_element('option', array('value' => 0), - _('Select a carrier')); - while ($carrier->fetch()) { - common_element('option', array('value' => $carrier->id), - $carrier->name); - } - common_element_end('select'); - common_element_end('p'); - common_element('span', 'input_instructions', - sprintf(_('Mobile carrier for your phone. '. - 'If you know a carrier that accepts ' . - 'SMS over email but isn\'t listed here, ' . - 'send email to let us know at %s.'), - common_config('site', 'email'))); - } - - function confirm_code() { - - $code = $this->trimmed('code'); - - if (!$code) { - $this->show_form(_('No code entered')); - return; - } - - common_redirect(common_local_url('confirmaddress', - array('code' => $code))); - } + $this->show_form(_('Confirmation cancelled.'), true); + } + + function remove_address() + { + + $user = common_current_user(); + $sms = $this->arg('sms'); + $carrier = $this->arg('carrier'); + + # Maybe an old tab open...? + + if ($user->sms != $sms) { + $this->show_form(_('That is not your phone number.')); + return; + } + + $user->query('BEGIN'); + $original = clone($user); + $user->sms = null; + $user->carrier = null; + $user->smsemail = null; + $result = $user->updateKeys($original); + if (!$result) { + common_log_db_error($user, 'UPDATE', __FILE__); + common_server_error(_('Couldn\'t update user.')); + return; + } + $user->query('COMMIT'); + + $this->show_form(_('The address was removed.'), true); + } + + function sms_exists($sms) + { + $user = common_current_user(); + $other = User::staticGet('sms', $sms); + if (!$other) { + return false; + } else { + return $other->id != $user->id; + } + } + + function carrier_select() + { + $carrier = new Sms_carrier(); + $cnt = $carrier->find(); + + common_element_start('p'); + common_element('label', array('for' => 'carrier')); + common_element_start('select', array('name' => 'carrier', + 'id' => 'carrier')); + common_element('option', array('value' => 0), + _('Select a carrier')); + while ($carrier->fetch()) { + common_element('option', array('value' => $carrier->id), + $carrier->name); + } + common_element_end('select'); + common_element_end('p'); + common_element('span', 'input_instructions', + sprintf(_('Mobile carrier for your phone. '. + 'If you know a carrier that accepts ' . + 'SMS over email but isn\'t listed here, ' . + 'send email to let us know at %s.'), + common_config('site', 'email'))); + } + + function confirm_code() + { + + $code = $this->trimmed('code'); + + if (!$code) { + $this->show_form(_('No code entered')); + return; + } + + common_redirect(common_local_url('confirmaddress', + array('code' => $code))); + } } diff --git a/actions/subedit.php b/actions/subedit.php index e7505e3fe..1142b7a03 100644 --- a/actions/subedit.php +++ b/actions/subedit.php @@ -19,11 +19,13 @@ if (!defined('LACONICA')) { exit(1); } -class SubeditAction extends Action { +class SubeditAction extends Action +{ - var $profile = NULL; + var $profile = null; - function prepare($args) { + function prepare($args) + { parent::prepare($args); @@ -32,12 +34,12 @@ class SubeditAction extends Action { return false; } - $token = $this->trimmed('token'); + $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->client_error(_('There was a problem with your session token. Try again, please.')); - return; - } + if (!$token || $token != common_session_token()) { + $this->client_error(_('There was a problem with your session token. Try again, please.')); + return; + } $id = $this->trimmed('profile'); @@ -56,7 +58,8 @@ class SubeditAction extends Action { return true; } - function handle($args) { + function handle($args) + { parent::handle($args); if ($_SERVER['REQUEST_METHOD'] == 'POST') { $cur = common_current_user(); diff --git a/actions/subscribe.php b/actions/subscribe.php index 64abda004..f33d1d207 100644 --- a/actions/subscribe.php +++ b/actions/subscribe.php @@ -19,60 +19,62 @@ if (!defined('LACONICA')) { exit(1); } -class SubscribeAction extends Action { +class SubscribeAction extends Action +{ - function handle($args) { - parent::handle($args); + function handle($args) + { + parent::handle($args); - if (!common_logged_in()) { - common_user_error(_('Not logged in.')); - return; - } + if (!common_logged_in()) { + common_user_error(_('Not logged in.')); + return; + } - $user = common_current_user(); + $user = common_current_user(); - if ($_SERVER['REQUEST_METHOD'] != 'POST') { - common_redirect(common_local_url('subscriptions', array('nickname' => $user->nickname))); - return; - } + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + common_redirect(common_local_url('subscriptions', array('nickname' => $user->nickname))); + return; + } - # CSRF protection + # CSRF protection - $token = $this->trimmed('token'); + $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->client_error(_('There was a problem with your session token. Try again, please.')); - return; - } + if (!$token || $token != common_session_token()) { + $this->client_error(_('There was a problem with your session token. Try again, please.')); + return; + } - $other_id = $this->arg('subscribeto'); + $other_id = $this->arg('subscribeto'); $other = User::staticGet('id', $other_id); if (!$other) { - $this->client_error(_('Not a local user.')); - return; + $this->client_error(_('Not a local user.')); + return; } - $result = subs_subscribe_to($user, $other); + $result = subs_subscribe_to($user, $other); - if($result != true) { - common_user_error($result); - return; - } + if($result != true) { + common_user_error($result); + return; + } - if ($this->boolean('ajax')) { - common_start_html('text/xml;charset=utf-8', true); - common_element_start('head'); - common_element('title', null, _('Subscribed')); - common_element_end('head'); - common_element_start('body'); - common_unsubscribe_form($other->getProfile()); - common_element_end('body'); - common_element_end('html'); - } else { - common_redirect(common_local_url('subscriptions', array('nickname' => - $user->nickname))); + if ($this->boolean('ajax')) { + common_start_html('text/xml;charset=utf-8', true); + common_element_start('head'); + common_element('title', null, _('Subscribed')); + common_element_end('head'); + common_element_start('body'); + common_unsubscribe_form($other->getProfile()); + common_element_end('body'); + common_element_end('html'); + } else { + common_redirect(common_local_url('subscriptions', array('nickname' => + $user->nickname))); } - } + } } diff --git a/actions/subscribers.php b/actions/subscribers.php index ae52526e1..31d0468d9 100644 --- a/actions/subscribers.php +++ b/actions/subscribers.php @@ -21,40 +21,49 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/gallery.php'); -class SubscribersAction extends GalleryAction { - - function gallery_type() { - return _('Subscribers'); - } - - function get_instructions(&$profile) { - $user =& common_current_user(); - if ($user && ($user->id == $profile->id)) { - return _('These are the people who listen to your notices.'); - } else { - return sprintf(_('These are the people who listen to %s\'s notices.'), $profile->nickname); - } - } - - function fields() { - return array('subscriber', 'subscribed'); - } - - function div_class() { - return 'subscribers'; - } - - function get_other(&$subs) { - return $subs->subscriber; - } - - function profile_list_class() { +class SubscribersAction extends GalleryAction +{ + + function gallery_type() + { + return _('Subscribers'); + } + + function get_instructions(&$profile) + { + $user =& common_current_user(); + if ($user && ($user->id == $profile->id)) { + return _('These are the people who listen to your notices.'); + } else { + return sprintf(_('These are the people who listen to %s\'s notices.'), $profile->nickname); + } + } + + function fields() + { + return array('subscriber', 'subscribed'); + } + + function div_class() + { + return 'subscribers'; + } + + function get_other(&$subs) + { + return $subs->subscriber; + } + + function profile_list_class() + { return 'SubscribersList'; } } -class SubscribersList extends ProfileList { - function show_owner_controls($profile) { +class SubscribersList extends ProfileList +{ + function show_owner_controls($profile) + { common_block_form($profile, array('action' => 'subscribers', 'nickname' => $this->owner->nickname)); } diff --git a/actions/subscriptions.php b/actions/subscriptions.php index f518a1f92..afe8fb260 100644 --- a/actions/subscriptions.php +++ b/actions/subscriptions.php @@ -21,44 +21,53 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/gallery.php'); -class SubscriptionsAction extends GalleryAction { +class SubscriptionsAction extends GalleryAction +{ - function gallery_type() { - return _('Subscriptions'); - } + function gallery_type() + { + return _('Subscriptions'); + } - function get_instructions(&$profile) { - $user =& common_current_user(); - if ($user && ($user->id == $profile->id)) { - return _('These are the people whose notices you listen to.'); - } else { - return sprintf(_('These are the people whose notices %s listens to.'), $profile->nickname); - } - } + function get_instructions(&$profile) + { + $user =& common_current_user(); + if ($user && ($user->id == $profile->id)) { + return _('These are the people whose notices you listen to.'); + } else { + return sprintf(_('These are the people whose notices %s listens to.'), $profile->nickname); + } + } - function fields() { - return array('subscribed', 'subscriber'); - } + function fields() + { + return array('subscribed', 'subscriber'); + } - function div_class() { - return 'subscriptions'; - } + function div_class() + { + return 'subscriptions'; + } - function get_other(&$subs) { - return $subs->subscribed; - } + function get_other(&$subs) + { + return $subs->subscribed; + } - function profile_list_class() { + function profile_list_class() + { return 'SubscriptionsList'; } } -class SubscriptionsList extends ProfileList { +class SubscriptionsList extends ProfileList +{ - function show_owner_controls($profile) { + function show_owner_controls($profile) + { - $sub = Subscription::pkeyGet(array('subscriber' => $this->owner->id, - 'subscribed' => $profile->id)); + $sub = Subscription::pkeyGet(array('subscriber' => $this->owner->id, + 'subscribed' => $profile->id)); if (!$sub) { return; } diff --git a/actions/sup.php b/actions/sup.php index 887017b2a..6a1897585 100644 --- a/actions/sup.php +++ b/actions/sup.php @@ -19,63 +19,68 @@ if (!defined('LACONICA')) { exit(1); } -class SupAction extends Action { - - function handle($args) { - - parent::handle($args); - - $seconds = $this->trimmed('seconds'); - - if (!$seconds) { - $seconds = 15; - } +class SupAction extends Action +{ + + function handle($args) + { + + parent::handle($args); + + $seconds = $this->trimmed('seconds'); + + if (!$seconds) { + $seconds = 15; + } - $updates = $this->get_updates($seconds); - - header('Content-Type: application/json; charset=utf-8'); - - print json_encode(array('updated_time' => date('c'), - 'since_time' => date('c', time() - $seconds), - 'available_periods' => $this->available_periods(), - 'period' => $seconds, - 'updates' => $updates)); - } - - function available_periods() { - static $periods = array(86400, 43200, 21600, 7200, - 3600, 1800, 600, 300, 120, - 60, 30, 15); - $available = array(); - foreach ($periods as $period) { - $available[$period] = common_local_url('sup', - array('seconds' => $period)); - } - - return $available; - } - - function get_updates($seconds) { - $notice = new Notice(); + $updates = $this->get_updates($seconds); + + header('Content-Type: application/json; charset=utf-8'); + + print json_encode(array('updated_time' => date('c'), + 'since_time' => date('c', time() - $seconds), + 'available_periods' => $this->available_periods(), + 'period' => $seconds, + 'updates' => $updates)); + } + + function available_periods() + { + static $periods = array(86400, 43200, 21600, 7200, + 3600, 1800, 600, 300, 120, + 60, 30, 15); + $available = array(); + foreach ($periods as $period) { + $available[$period] = common_local_url('sup', + array('seconds' => $period)); + } + + return $available; + } + + function get_updates($seconds) + { + $notice = new Notice(); - # XXX: cache this. Depends on how big this protocol becomes; - # Re-doing this query every 15 seconds isn't the end of the world. + # XXX: cache this. Depends on how big this protocol becomes; + # Re-doing this query every 15 seconds isn't the end of the world. - $notice->query('SELECT profile_id, max(id) AS max_id ' . - 'FROM notice ' . - 'WHERE created > (now() - ' . $seconds . ') ' . - 'GROUP BY profile_id'); - - $updates = array(); - - while ($notice->fetch()) { - $updates[] = array($notice->profile_id, $notice->max_id); - } - - return $updates; - } - - function is_readonly() { - return true; - } + $notice->query('SELECT profile_id, max(id) AS max_id ' . + 'FROM notice ' . + 'WHERE created > (now() - ' . $seconds . ') ' . + 'GROUP BY profile_id'); + + $updates = array(); + + while ($notice->fetch()) { + $updates[] = array($notice->profile_id, $notice->max_id); + } + + return $updates; + } + + function is_readonly() + { + return true; + } } diff --git a/actions/tag.php b/actions/tag.php index 25cc853c4..8a3f90c16 100644 --- a/actions/tag.php +++ b/actions/tag.php @@ -22,144 +22,151 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/actions/showstream.php'); define('TAGS_PER_PAGE', 100); -class TagAction extends StreamAction { - - function handle($args) { - - parent::handle($args); - - # Looks like we're good; show the header - - if (isset($args['tag']) && $args['tag']) { - $tag = $args['tag']; - common_show_header(sprintf(_("Notices tagged with %s"), $tag), - array($this, 'show_header'), $tag, - array($this, 'show_top')); - $this->show_notices($tag); - } else { - common_show_header(_("Tags"), - array($this, 'show_header'), '', - array($this, 'show_top')); - $this->show_tags(); - } - - common_show_footer(); - } - - function show_header($tag = false) { - if ($tag) { - common_element('link', array('rel' => 'alternate', - 'href' => common_local_url('tagrss', array('tag' => $tag)), - 'type' => 'application/rss+xml', - 'title' => sprintf(_('Feed for tag %s'), $tag))); - } - } - - function get_instructions() { - return _('Showing most popular tags from the last week'); - } - - function show_top($tag = false) { - if (!$tag) { - $instr = $this->get_instructions(); - $output = common_markup_to_html($instr); - common_element_start('div', 'instructions'); - common_raw($output); - common_element_end('div'); - $this->public_views_menu(); - } - else { - $this->show_feeds_list(array(0=>array('href'=>common_local_url('tagrss'), - 'type' => 'rss', - 'version' => 'RSS 1.0', - 'item' => 'tagrss'))); - } - } - - function show_tags() - { - # This should probably be cached rather than recalculated - $tags = DB_DataObject::factory('Notice_tag'); - - #Need to clear the selection and then only re-add the field - #we are grouping by, otherwise it's not a valid 'group by' - #even though MySQL seems to let it slide... - $tags->selectAdd(); - $tags->selectAdd('tag'); - - #Add the aggregated columns... - $tags->selectAdd('max(notice_id) as last_notice_id'); - if(common_config('db','type')=='pgsql') { - $calc='sum(exp(-extract(epoch from (now()-created))/%s)) as weight'; - } else { - $calc='sum(exp(-(now() - created)/%s)) as weight'; - } - $tags->selectAdd(sprintf($calc, common_config('tag', 'dropoff'))); - $tags->groupBy('tag'); - $tags->orderBy('weight DESC'); - - # $tags->whereAdd('created > "' . strftime('%Y-%m-%d %H:%M:%S', strtotime('-1 MONTH')) . '"'); - - $tags->limit(TAGS_PER_PAGE); - - $cnt = $tags->find(); - - if ($cnt > 0) { - common_element_start('p', 'tagcloud'); - - $tw = array(); - $sum = 0; - while ($tags->fetch()) { - $tw[$tags->tag] = $tags->weight; - $sum += $tags->weight; - } - - ksort($tw); - - foreach ($tw as $tag => $weight) { - $this->show_tag($tag, $weight, $weight/$sum); - } - - common_element_end('p'); - } - } - - function show_tag($tag, $weight, $relative) { - - # XXX: these should probably tune to the size of the site - if ($relative > 0.1) { - $cls = 'largest'; - } else if ($relative > 0.05) { - $cls = 'verylarge'; - } else if ($relative > 0.02) { - $cls = 'large'; - } else if ($relative > 0.01) { - $cls = 'medium'; - } else if ($relative > 0.005) { - $cls = 'small'; - } else if ($relative > 0.002) { - $cls = 'verysmall'; - } else { - $cls = 'smallest'; - } - - common_element('a', array('class' => "$cls weight-$weight relative-$relative", - 'href' => common_local_url('tag', array('tag' => $tag))), - $tag); - common_text(' '); - } - - function show_notices($tag) { - - $cnt = 0; - - $page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; - - $notice = Notice_tag::getStream($tag, (($page-1)*NOTICES_PER_PAGE), NOTICES_PER_PAGE + 1); +class TagAction extends StreamAction +{ + + function handle($args) + { + + parent::handle($args); + + # Looks like we're good; show the header + + if (isset($args['tag']) && $args['tag']) { + $tag = $args['tag']; + common_show_header(sprintf(_("Notices tagged with %s"), $tag), + array($this, 'show_header'), $tag, + array($this, 'show_top')); + $this->show_notices($tag); + } else { + common_show_header(_("Tags"), + array($this, 'show_header'), '', + array($this, 'show_top')); + $this->show_tags(); + } + + common_show_footer(); + } + + function show_header($tag = false) + { + if ($tag) { + common_element('link', array('rel' => 'alternate', + 'href' => common_local_url('tagrss', array('tag' => $tag)), + 'type' => 'application/rss+xml', + 'title' => sprintf(_('Feed for tag %s'), $tag))); + } + } + + function get_instructions() + { + return _('Showing most popular tags from the last week'); + } + + function show_top($tag = false) + { + if (!$tag) { + $instr = $this->get_instructions(); + $output = common_markup_to_html($instr); + common_element_start('div', 'instructions'); + common_raw($output); + common_element_end('div'); + $this->public_views_menu(); + } + else { + $this->show_feeds_list(array(0=>array('href'=>common_local_url('tagrss'), + 'type' => 'rss', + 'version' => 'RSS 1.0', + 'item' => 'tagrss'))); + } + } + + function show_tags() + { + # This should probably be cached rather than recalculated + $tags = DB_DataObject::factory('Notice_tag'); + + #Need to clear the selection and then only re-add the field + #we are grouping by, otherwise it's not a valid 'group by' + #even though MySQL seems to let it slide... + $tags->selectAdd(); + $tags->selectAdd('tag'); + + #Add the aggregated columns... + $tags->selectAdd('max(notice_id) as last_notice_id'); + if(common_config('db','type')=='pgsql') { + $calc='sum(exp(-extract(epoch from (now()-created))/%s)) as weight'; + } else { + $calc='sum(exp(-(now() - created)/%s)) as weight'; + } + $tags->selectAdd(sprintf($calc, common_config('tag', 'dropoff'))); + $tags->groupBy('tag'); + $tags->orderBy('weight DESC'); + + # $tags->whereAdd('created > "' . strftime('%Y-%m-%d %H:%M:%S', strtotime('-1 MONTH')) . '"'); + + $tags->limit(TAGS_PER_PAGE); + + $cnt = $tags->find(); + + if ($cnt > 0) { + common_element_start('p', 'tagcloud'); + + $tw = array(); + $sum = 0; + while ($tags->fetch()) { + $tw[$tags->tag] = $tags->weight; + $sum += $tags->weight; + } + + ksort($tw); + + foreach ($tw as $tag => $weight) { + $this->show_tag($tag, $weight, $weight/$sum); + } + + common_element_end('p'); + } + } + + function show_tag($tag, $weight, $relative) + { + + # XXX: these should probably tune to the size of the site + if ($relative > 0.1) { + $cls = 'largest'; + } else if ($relative > 0.05) { + $cls = 'verylarge'; + } else if ($relative > 0.02) { + $cls = 'large'; + } else if ($relative > 0.01) { + $cls = 'medium'; + } else if ($relative > 0.005) { + $cls = 'small'; + } else if ($relative > 0.002) { + $cls = 'verysmall'; + } else { + $cls = 'smallest'; + } + + common_element('a', array('class' => "$cls weight-$weight relative-$relative", + 'href' => common_local_url('tag', array('tag' => $tag))), + $tag); + common_text(' '); + } + + function show_notices($tag) + { + + $cnt = 0; + + $page = ($this->arg('page')) ? ($this->arg('page')+0) : 1; + + $notice = Notice_tag::getStream($tag, (($page-1)*NOTICES_PER_PAGE), NOTICES_PER_PAGE + 1); $cnt = $this->show_notice_list($notice); - common_pagination($page > 1, $cnt > NOTICES_PER_PAGE, - $page, 'tag', array('tag' => $tag)); - } + common_pagination($page > 1, $cnt > NOTICES_PER_PAGE, + $page, 'tag', array('tag' => $tag)); + } } diff --git a/actions/tagother.php b/actions/tagother.php index a4449dd69..ff6788cc6 100644 --- a/actions/tagother.php +++ b/actions/tagother.php @@ -21,173 +21,178 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/settingsaction.php'); -class TagotherAction extends Action { - - function handle($args) { - - parent::handle($args); - - if (!common_logged_in()) { - $this->client_error(_('Not logged in'), 403); - return; - } - - if ($_SERVER['REQUEST_METHOD'] == 'POST') { - $this->save_tags(); - } else { - $id = $this->trimmed('id'); - if (!$id) { - $this->client_error(_('No id argument.')); - return; - } - $profile = Profile::staticGet('id', $id); - if (!$profile) { - $this->client_error(_('No profile with that ID.')); - return; - } - $this->show_form($profile); - } - } - - function show_form($profile, $error=NULL) { - - $user = common_current_user(); - - common_show_header(_('Tag a person'), - NULL, array($profile, $error), array($this, 'show_top')); - - $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); - - common_element('img', array('src' => ($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_PROFILE_SIZE), - 'class' => 'avatar stream', - 'width' => AVATAR_PROFILE_SIZE, - 'height' => AVATAR_PROFILE_SIZE, - 'alt' => - ($profile->fullname) ? $profile->fullname : - $profile->nickname)); - - common_element('a', array('href' => $profile->profileurl, - 'class' => 'external profile nickname'), - $profile->nickname); - - if ($profile->fullname) { - common_element_start('div', 'fullname'); - if ($profile->homepage) { - common_element('a', array('href' => $profile->homepage), - $profile->fullname); - } else { - common_text($profile->fullname); - } - common_element_end('div'); - } - if ($profile->location) { - common_element('div', 'location', $profile->location); - } - if ($profile->bio) { - common_element('div', 'bio', $profile->bio); - } - - common_element_start('form', array('method' => 'post', - 'id' => 'tag_user', - 'name' => 'tagother', - 'action' => $this->self_url())); - common_hidden('token', common_session_token()); - common_hidden('id', $profile->id); - common_input('tags', _('Tags'), - ($this->arg('tags')) ? $this->arg('tags') : implode(' ', Profile_tag::getTags($user->id, $profile->id)), - _('Tags for this user (letters, numbers, -, ., and _), comma- or space- separated')); - - common_submit('save', _('Save')); - common_element_end('form'); - common_show_footer(); - - } - - function save_tags() { - - $id = $this->trimmed('id'); - $tagstring = $this->trimmed('tags'); - $token = $this->trimmed('token'); - - if (!$token || $token != common_session_token()) { - $this->show_form(_('There was a problem with your session token. Try again, please.')); - return; - } - - $profile = Profile::staticGet('id', $id); - - if (!$profile) { - $this->client_error(_('No such profile.')); - return; - } - - if (is_string($tagstring) && strlen($tagstring) > 0) { - - $tags = array_map('common_canonical_tag', - preg_split('/[\s,]+/', $tagstring)); - - foreach ($tags as $tag) { - if (!common_valid_profile_tag($tag)) { - $this->show_form($profile, sprintf(_('Invalid tag: "%s"'), $tag)); - return; - } - } - } else { - $tags = array(); - } - - $user = common_current_user(); - - if (!Subscription::pkeyGet(array('subscriber' => $user->id, - 'subscribed' => $profile->id)) && - !Subscription::pkeyGet(array('subscriber' => $profile->id, - 'subscribed' => $user->id))) - { - $this->client_error(_('You can only tag people you are subscribed to or who are subscribed to you.')); - return; - } - - $result = Profile_tag::setTags($user->id, $profile->id, $tags); - - if (!$result) { - $this->client_error(_('Could not save tags.')); - return; - } - - $action = $user->isSubscribed($profile) ? 'subscriptions' : 'subscribers'; - - if ($this->boolean('ajax')) { - common_start_html('text/xml'); - common_element_start('head'); - common_element('title', null, _('Tags')); - common_element_end('head'); - common_element_start('body'); - common_element_start('p', 'subtags'); - foreach ($tags as $tag) { - common_element('a', array('href' => common_local_url($action, - array('nickname' => $user->nickname, - 'tag' => $tag))), - $tag); - } - common_element_end('p'); - common_element_end('body'); - common_element_end('html'); - } else { - common_redirect(common_local_url($action, array('nickname' => - $user->nickname))); +class TagotherAction extends Action +{ + + function handle($args) + { + + parent::handle($args); + + if (!common_logged_in()) { + $this->client_error(_('Not logged in'), 403); + return; + } + + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + $this->save_tags(); + } else { + $id = $this->trimmed('id'); + if (!$id) { + $this->client_error(_('No id argument.')); + return; + } + $profile = Profile::staticGet('id', $id); + if (!$profile) { + $this->client_error(_('No profile with that ID.')); + return; + } + $this->show_form($profile); + } + } + + function show_form($profile, $error=null) + { + + $user = common_current_user(); + + common_show_header(_('Tag a person'), + null, array($profile, $error), array($this, 'show_top')); + + $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); + + common_element('img', array('src' => ($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_PROFILE_SIZE), + 'class' => 'avatar stream', + 'width' => AVATAR_PROFILE_SIZE, + 'height' => AVATAR_PROFILE_SIZE, + 'alt' => + ($profile->fullname) ? $profile->fullname : + $profile->nickname)); + + common_element('a', array('href' => $profile->profileurl, + 'class' => 'external profile nickname'), + $profile->nickname); + + if ($profile->fullname) { + common_element_start('div', 'fullname'); + if ($profile->homepage) { + common_element('a', array('href' => $profile->homepage), + $profile->fullname); + } else { + common_text($profile->fullname); + } + common_element_end('div'); + } + if ($profile->location) { + common_element('div', 'location', $profile->location); + } + if ($profile->bio) { + common_element('div', 'bio', $profile->bio); + } + + common_element_start('form', array('method' => 'post', + 'id' => 'tag_user', + 'name' => 'tagother', + 'action' => $this->self_url())); + common_hidden('token', common_session_token()); + common_hidden('id', $profile->id); + common_input('tags', _('Tags'), + ($this->arg('tags')) ? $this->arg('tags') : implode(' ', Profile_tag::getTags($user->id, $profile->id)), + _('Tags for this user (letters, numbers, -, ., and _), comma- or space- separated')); + + common_submit('save', _('Save')); + common_element_end('form'); + common_show_footer(); + + } + + function save_tags() + { + + $id = $this->trimmed('id'); + $tagstring = $this->trimmed('tags'); + $token = $this->trimmed('token'); + + if (!$token || $token != common_session_token()) { + $this->show_form(_('There was a problem with your session token. Try again, please.')); + return; + } + + $profile = Profile::staticGet('id', $id); + + if (!$profile) { + $this->client_error(_('No such profile.')); + return; + } + + if (is_string($tagstring) && strlen($tagstring) > 0) { + + $tags = array_map('common_canonical_tag', + preg_split('/[\s,]+/', $tagstring)); + + foreach ($tags as $tag) { + if (!common_valid_profile_tag($tag)) { + $this->show_form($profile, sprintf(_('Invalid tag: "%s"'), $tag)); + return; + } + } + } else { + $tags = array(); + } + + $user = common_current_user(); + + if (!Subscription::pkeyGet(array('subscriber' => $user->id, + 'subscribed' => $profile->id)) && + !Subscription::pkeyGet(array('subscriber' => $profile->id, + 'subscribed' => $user->id))) + { + $this->client_error(_('You can only tag people you are subscribed to or who are subscribed to you.')); + return; + } + + $result = Profile_tag::setTags($user->id, $profile->id, $tags); + + if (!$result) { + $this->client_error(_('Could not save tags.')); + return; + } + + $action = $user->isSubscribed($profile) ? 'subscriptions' : 'subscribers'; + + if ($this->boolean('ajax')) { + common_start_html('text/xml'); + common_element_start('head'); + common_element('title', null, _('Tags')); + common_element_end('head'); + common_element_start('body'); + common_element_start('p', 'subtags'); + foreach ($tags as $tag) { + common_element('a', array('href' => common_local_url($action, + array('nickname' => $user->nickname, + 'tag' => $tag))), + $tag); + } + common_element_end('p'); + common_element_end('body'); + common_element_end('html'); + } else { + common_redirect(common_local_url($action, array('nickname' => + $user->nickname))); + } + } + + function show_top($arr = null) + { + list($profile, $error) = $arr; + if ($error) { + common_element('p', 'error', $error); + } else { + common_element_start('div', 'instructions'); + common_element('p', null, + _('Use this form to add tags to your subscribers or subscriptions.')); + common_element_end('div'); } - } - - function show_top($arr = NULL) { - list($profile, $error) = $arr; - if ($error) { - common_element('p', 'error', $error); - } else { - common_element_start('div', 'instructions'); - common_element('p', NULL, - _('Use this form to add tags to your subscribers or subscriptions.')); - common_element_end('div'); - } - } + } } diff --git a/actions/tagrss.php b/actions/tagrss.php index 9187bdc87..912d71413 100644 --- a/actions/tagrss.php +++ b/actions/tagrss.php @@ -23,43 +23,47 @@ require_once(INSTALLDIR.'/lib/rssaction.php'); // Formatting of RSS handled by Rss10Action -class TagrssAction extends Rss10Action { +class TagrssAction extends Rss10Action +{ - function init() { - $tag = $this->trimmed('tag'); - $this->tag = Notice_tag::staticGet('tag', $tag); + function init() + { + $tag = $this->trimmed('tag'); + $this->tag = Notice_tag::staticGet('tag', $tag); - if (!$this->tag) { - common_user_error(_('No such tag.')); - return false; - } else { - return true; - } - } + if (!$this->tag) { + common_user_error(_('No such tag.')); + return false; + } else { + return true; + } + } - function get_notices($limit=0) { - $tag = $this->tag; + function get_notices($limit=0) + { + $tag = $this->tag; - if (is_null($tag)) { - return NULL; - } + if (is_null($tag)) { + return null; + } - $notice = Notice_tag::getStream($tag->tag, 0, ($limit == 0) ? NOTICES_PER_PAGE : $limit); + $notice = Notice_tag::getStream($tag->tag, 0, ($limit == 0) ? NOTICES_PER_PAGE : $limit); - while ($notice->fetch()) { - $notices[] = clone($notice); - } + while ($notice->fetch()) { + $notices[] = clone($notice); + } - return $notices; - } + return $notices; + } - function get_channel() { - $tag = $this->tag->tag; + function get_channel() + { + $tag = $this->tag->tag; - $c = array('url' => common_local_url('tagrss', array('tag' => $tagname)), - 'title' => $tagname, - 'link' => common_local_url('tagrss', array('tag' => $tagname)), - 'description' => sprintf(_('Microblog tagged with %s'), $tagname)); - return $c; - } + $c = array('url' => common_local_url('tagrss', array('tag' => $tagname)), + 'title' => $tagname, + 'link' => common_local_url('tagrss', array('tag' => $tagname)), + 'description' => sprintf(_('Microblog tagged with %s'), $tagname)); + return $c; + } } diff --git a/actions/twitapiaccount.php b/actions/twitapiaccount.php index f6e955828..79e1ed990 100644 --- a/actions/twitapiaccount.php +++ b/actions/twitapiaccount.php @@ -21,76 +21,82 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/twitterapi.php'); -class TwitapiaccountAction extends TwitterapiAction { +class TwitapiaccountAction extends TwitterapiAction +{ - function verify_credentials($args, $apidata) { - parent::handle($args); + function verify_credentials($args, $apidata) + { + parent::handle($args); - if (!in_array($apidata['content-type'], array('xml', 'json'))) { - common_user_error(_('API method not found!'), $code = 404); - return; - } + if (!in_array($apidata['content-type'], array('xml', 'json'))) { + common_user_error(_('API method not found!'), $code = 404); + return; + } - $this->show_extended_profile($apidata['user'], $apidata); - } + $this->show_extended_profile($apidata['user'], $apidata); + } - function end_session($args, $apidata) { - parent::handle($args); - common_server_error(_('API method under construction.'), $code=501); - } + function end_session($args, $apidata) + { + parent::handle($args); + common_server_error(_('API method under construction.'), $code=501); + } - function update_location($args, $apidata) { - parent::handle($args); + function update_location($args, $apidata) + { + parent::handle($args); - if ($_SERVER['REQUEST_METHOD'] != 'POST') { - $this->client_error(_('This method requires a POST.'), 400, $apidata['content-type']); - return; - } + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + $this->client_error(_('This method requires a POST.'), 400, $apidata['content-type']); + return; + } - $location = trim($this->arg('location')); + $location = trim($this->arg('location')); - if (!is_null($location) && strlen($location) > 255) { + if (!is_null($location) && strlen($location) > 255) { - // XXX: But Twitter just truncates and runs with it. -- Zach - $this->client_error(_('That\'s too long. Max notice size is 255 chars.'), 406, $apidate['content-type']); - return; - } + // XXX: But Twitter just truncates and runs with it. -- Zach + $this->client_error(_('That\'s too long. Max notice size is 255 chars.'), 406, $apidate['content-type']); + return; + } - $user = $apidata['user']; - $profile = $user->getProfile(); + $user = $apidata['user']; + $profile = $user->getProfile(); - if (!$profile) { - common_server_error(_('User has no profile.')); - return; - } + if (!$profile) { + common_server_error(_('User has no profile.')); + return; + } - $orig_profile = clone($profile); - $profile->location = $location; + $orig_profile = clone($profile); + $profile->location = $location; - $result = $profile->update($orig_profile); + $result = $profile->update($orig_profile); - if (!$result) { - common_log_db_error($profile, 'UPDATE', __FILE__); - common_server_error(_('Couldn\'t save profile.')); - return; - } + if (!$result) { + common_log_db_error($profile, 'UPDATE', __FILE__); + common_server_error(_('Couldn\'t save profile.')); + return; + } - common_broadcast_profile($profile); - $type = $apidata['content-type']; + common_broadcast_profile($profile); + $type = $apidata['content-type']; - $this->init_document($type); - $this->show_profile($profile, $type); - $this->end_document($type); - } + $this->init_document($type); + $this->show_profile($profile, $type); + $this->end_document($type); + } - function update_delivery_device($args, $apidata) { - parent::handle($args); - common_server_error(_('API method under construction.'), $code=501); - } + function update_delivery_device($args, $apidata) + { + parent::handle($args); + common_server_error(_('API method under construction.'), $code=501); + } - function rate_limit_status($args, $apidata) { - parent::handle($args); - common_server_error(_('API method under construction.'), $code=501); - } + function rate_limit_status($args, $apidata) + { + parent::handle($args); + common_server_error(_('API method under construction.'), $code=501); + } }
\ No newline at end of file diff --git a/actions/twitapiblocks.php b/actions/twitapiblocks.php index 4852ff938..5d64f2f7d 100644 --- a/actions/twitapiblocks.php +++ b/actions/twitapiblocks.php @@ -21,17 +21,19 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/twitterapi.php'); -class TwitapiblocksAction extends TwitterapiAction { +class TwitapiblocksAction extends TwitterapiAction +{ - function create($args, $apidata) { + function create($args, $apidata) + { - parent::handle($args); + parent::handle($args); - $blockee = $this->get_user($apidata['api_arg'], $apidata); + $blockee = $this->get_user($apidata['api_arg'], $apidata); if (!$blockee) { - $this->client_error('Not Found', 404, $apidata['content-type']); - return; + $this->client_error('Not Found', 404, $apidata['content-type']); + return; } $user = $apidata['user']; @@ -42,17 +44,18 @@ class TwitapiblocksAction extends TwitterapiAction { $this->show_profile($blockee, $type); $this->end_document($type); } else { - common_server_error(_('Block user failed.')); + common_server_error(_('Block user failed.')); } - } + } - function destroy($args, $apidata) { - parent::handle($args); - $blockee = $this->get_user($apidata['api_arg'], $apidata); + function destroy($args, $apidata) + { + parent::handle($args); + $blockee = $this->get_user($apidata['api_arg'], $apidata); if (!$blockee) { - $this->client_error('Not Found', 404, $apidata['content-type']); - return; + $this->client_error('Not Found', 404, $apidata['content-type']); + return; } $user = $apidata['user']; @@ -63,7 +66,7 @@ class TwitapiblocksAction extends TwitterapiAction { $this->show_profile($blockee, $type); $this->end_document($type); } else { - common_server_error(_('Unblock user failed.')); + common_server_error(_('Unblock user failed.')); } - } + } }
\ No newline at end of file diff --git a/actions/twitapidirect_messages.php b/actions/twitapidirect_messages.php index 535795ca4..e0731f66f 100644 --- a/actions/twitapidirect_messages.php +++ b/actions/twitapidirect_messages.php @@ -21,267 +21,278 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/twitterapi.php'); -class Twitapidirect_messagesAction extends TwitterapiAction { - - function direct_messages($args, $apidata) { - parent::handle($args); - return $this->show_messages($args, $apidata, 'received'); - } - - function sent($args, $apidata) { - parent::handle($args); - return $this->show_messages($args, $apidata, 'sent'); - } - - function show_messages($args, $apidata, $type) { - - $user = $apidata['user']; - - $count = $this->arg('count'); - $since = $this->arg('since'); - $since_id = $this->arg('since_id'); - $before_id = $this->arg('before_id'); - - $page = $this->arg('page'); - - if (!$page) { - $page = 1; - } - - if (!$count) { - $count = 20; - } - - $message = new Message(); - - $title = null; - $subtitle = null; - $link = null; - $server = common_root_url(); - - if ($type == 'received') { - $message->to_profile = $user->id; - $title = sprintf(_("Direct messages to %s"), $user->nickname); - $subtitle = sprintf(_("All the direct messages sent to %s"), $user->nickname); - $link = $server . $user->nickname . '/inbox'; - } else { - $message->from_profile = $user->id; - $title = _('Direct Messages You\'ve Sent'); - $subtitle = sprintf(_("All the direct messages sent from %s"), $user->nickname); - $link = $server . $user->nickname . '/outbox'; - } - - if ($before_id) { - $message->whereAdd("id < $before_id"); - } - - if ($since_id) { - $message->whereAdd("id > $since_id"); - } - - $since = strtotime($this->arg('since')); - - if ($since) { - $d = date('Y-m-d H:i:s', $since); - $message->whereAdd("created > '$d'"); - } - - $message->orderBy('created DESC, id DESC'); - $message->limit((($page-1)*20), $count); - $message->find(); - - switch($apidata['content-type']) { - case 'xml': - $this->show_xml_dmsgs($message); - break; - case 'rss': - $this->show_rss_dmsgs($message, $title, $link, $subtitle); - break; - case 'atom': - $this->show_atom_dmsgs($message, $title, $link, $subtitle); - break; - case 'json': - $this->show_json_dmsgs($message); - break; - default: - common_user_error(_('API method not found!'), $code = 404); - } - - } - - // had to change this from "new" to "create" to avoid PHP reserved word - function create($args, $apidata) { - parent::handle($args); - - if ($_SERVER['REQUEST_METHOD'] != 'POST') { - $this->client_error(_('This method requires a POST.'), 400, $apidata['content-type']); - return; - } - - $user = $apidata['user']; - $source = $this->trimmed('source'); // Not supported by Twitter. +class Twitapidirect_messagesAction extends TwitterapiAction +{ + + function direct_messages($args, $apidata) + { + parent::handle($args); + return $this->show_messages($args, $apidata, 'received'); + } + + function sent($args, $apidata) + { + parent::handle($args); + return $this->show_messages($args, $apidata, 'sent'); + } + + function show_messages($args, $apidata, $type) + { + + $user = $apidata['user']; + + $count = $this->arg('count'); + $since = $this->arg('since'); + $since_id = $this->arg('since_id'); + $before_id = $this->arg('before_id'); + + $page = $this->arg('page'); + + if (!$page) { + $page = 1; + } + + if (!$count) { + $count = 20; + } + + $message = new Message(); + + $title = null; + $subtitle = null; + $link = null; + $server = common_root_url(); + + if ($type == 'received') { + $message->to_profile = $user->id; + $title = sprintf(_("Direct messages to %s"), $user->nickname); + $subtitle = sprintf(_("All the direct messages sent to %s"), $user->nickname); + $link = $server . $user->nickname . '/inbox'; + } else { + $message->from_profile = $user->id; + $title = _('Direct Messages You\'ve Sent'); + $subtitle = sprintf(_("All the direct messages sent from %s"), $user->nickname); + $link = $server . $user->nickname . '/outbox'; + } + + if ($before_id) { + $message->whereAdd("id < $before_id"); + } + + if ($since_id) { + $message->whereAdd("id > $since_id"); + } + + $since = strtotime($this->arg('since')); + + if ($since) { + $d = date('Y-m-d H:i:s', $since); + $message->whereAdd("created > '$d'"); + } + + $message->orderBy('created DESC, id DESC'); + $message->limit((($page-1)*20), $count); + $message->find(); + + switch($apidata['content-type']) { + case 'xml': + $this->show_xml_dmsgs($message); + break; + case 'rss': + $this->show_rss_dmsgs($message, $title, $link, $subtitle); + break; + case 'atom': + $this->show_atom_dmsgs($message, $title, $link, $subtitle); + break; + case 'json': + $this->show_json_dmsgs($message); + break; + default: + common_user_error(_('API method not found!'), $code = 404); + } + + } + + // had to change this from "new" to "create" to avoid PHP reserved word + function create($args, $apidata) + { + parent::handle($args); + + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + $this->client_error(_('This method requires a POST.'), 400, $apidata['content-type']); + return; + } + + $user = $apidata['user']; + $source = $this->trimmed('source'); // Not supported by Twitter. $reserved_sources = array('web', 'omb', 'mail', 'xmpp', 'api'); - if (!$source || in_array($source, $reserved_sources)) { - $source = 'api'; - } - - $content = $this->trimmed('text'); - - if (!$content) { - $this->client_error(_('No message text!'), $code = 406, $apidata['content-type']); - } else { - $content_shortened = common_shorten_links($content); - if (mb_strlen($content_shortened) > 140) { - $this->client_error(_('That\'s too long. Max message size is 140 chars.'), - $code = 406, $apidata['content-type']); - return; - } - } - - $other = $this->get_user($this->trimmed('user')); - - if (!$other) { - $this->client_error(_('Recipient user not found.'), $code = 403, $apidata['content-type']); - return; - } else if (!$user->mutuallySubscribed($other)) { - $this->client_error(_('Can\'t send direct messages to users who aren\'t your friend.'), - $code = 403, $apidata['content-type']); - return; - } else if ($user->id == $other->id) { - // Sending msgs to yourself is allowed by Twitter - $this->client_error(_('Don\'t send a message to yourself; just say it to yourself quietly instead.'), - $code = 403, $apidata['content-type']); - return; - } - - $message = Message::saveNew($user->id, $other->id, - html_entity_decode($content, ENT_NOQUOTES, 'UTF-8'), $source); - - if (is_string($message)) { - $this->server_error($message); - return; - } - - $this->notify($user, $other, $message); - - if ($apidata['content-type'] == 'xml') { - $this->show_single_xml_dmsg($message); - } elseif ($apidata['content-type'] == 'json') { - $this->show_single_json_dmsg($message); - } - - } - - function destroy($args, $apidata) { - parent::handle($args); - common_server_error(_('API method under construction.'), $code=501); - } - - function show_xml_dmsgs($message) { - - $this->init_document('xml'); - common_element_start('direct-messages', array('type' => 'array')); - - if (is_array($messages)) { - foreach ($message as $m) { - $twitter_dm = $this->twitter_dmsg_array($m); - $this->show_twitter_xml_dmsg($twitter_dm); - } - } else { - while ($message->fetch()) { - $twitter_dm = $this->twitter_dmsg_array($message); - $this->show_twitter_xml_dmsg($twitter_dm); - } - } - - common_element_end('direct-messages'); - $this->end_document('xml'); - - } - - function show_json_dmsgs($message) { - - $this->init_document('json'); - - $dmsgs = array(); - - if (is_array($message)) { - foreach ($message as $m) { - $twitter_dm = $this->twitter_dmsg_array($m); - array_push($dmsgs, $twitter_dm); - } - } else { - while ($message->fetch()) { - $twitter_dm = $this->twitter_dmsg_array($message); - array_push($dmsgs, $twitter_dm); - } - } - - $this->show_json_objects($dmsgs); - $this->end_document('json'); - - } - - function show_rss_dmsgs($message, $title, $link, $subtitle) { - - $this->init_document('rss'); - - common_element_start('channel'); - common_element('title', NULL, $title); - - common_element('link', NULL, $link); - common_element('description', NULL, $subtitle); - common_element('language', NULL, 'en-us'); - common_element('ttl', NULL, '40'); - - if (is_array($message)) { - foreach ($message as $m) { - $entry = $this->twitter_rss_dmsg_array($m); - $this->show_twitter_rss_item($entry); - } - } else { - while ($message->fetch()) { - $entry = $this->twitter_rss_dmsg_array($message); - $this->show_twitter_rss_item($entry); - } - } - - common_element_end('channel'); - $this->end_twitter_rss(); - - } - - function show_atom_dmsgs($message, $title, $link, $subtitle) { - - $this->init_document('atom'); - - common_element('title', NULL, $title); - $siteserver = common_config('site', 'server'); - common_element('id', NULL, "tag:$siteserver,2008:DirectMessage"); - common_element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), NULL); - common_element('updated', NULL, common_date_iso8601(strftime('%c'))); - common_element('subtitle', NULL, $subtitle); - - if (is_array($message)) { - foreach ($message as $m) { - $entry = $this->twitter_rss_dmsg_array($m); - $this->show_twitter_atom_entry($entry); - } - } else { - while ($message->fetch()) { - $entry = $this->twitter_rss_dmsg_array($message); - $this->show_twitter_atom_entry($entry); - } - } - - $this->end_document('atom'); - } - - // swiped from MessageAction. Should it be place in util.php? - function notify($from, $to, $message) { - mail_notify_message($message, $from, $to); - # XXX: Jabber, SMS notifications... probably queued - } + if (!$source || in_array($source, $reserved_sources)) { + $source = 'api'; + } + + $content = $this->trimmed('text'); + + if (!$content) { + $this->client_error(_('No message text!'), $code = 406, $apidata['content-type']); + } else { + $content_shortened = common_shorten_links($content); + if (mb_strlen($content_shortened) > 140) { + $this->client_error(_('That\'s too long. Max message size is 140 chars.'), + $code = 406, $apidata['content-type']); + return; + } + } + + $other = $this->get_user($this->trimmed('user')); + + if (!$other) { + $this->client_error(_('Recipient user not found.'), $code = 403, $apidata['content-type']); + return; + } else if (!$user->mutuallySubscribed($other)) { + $this->client_error(_('Can\'t send direct messages to users who aren\'t your friend.'), + $code = 403, $apidata['content-type']); + return; + } else if ($user->id == $other->id) { + // Sending msgs to yourself is allowed by Twitter + $this->client_error(_('Don\'t send a message to yourself; just say it to yourself quietly instead.'), + $code = 403, $apidata['content-type']); + return; + } + + $message = Message::saveNew($user->id, $other->id, + html_entity_decode($content, ENT_NOQUOTES, 'UTF-8'), $source); + + if (is_string($message)) { + $this->server_error($message); + return; + } + + $this->notify($user, $other, $message); + + if ($apidata['content-type'] == 'xml') { + $this->show_single_xml_dmsg($message); + } elseif ($apidata['content-type'] == 'json') { + $this->show_single_json_dmsg($message); + } + + } + + function destroy($args, $apidata) + { + parent::handle($args); + common_server_error(_('API method under construction.'), $code=501); + } + + function show_xml_dmsgs($message) + { + + $this->init_document('xml'); + common_element_start('direct-messages', array('type' => 'array')); + + if (is_array($messages)) { + foreach ($message as $m) { + $twitter_dm = $this->twitter_dmsg_array($m); + $this->show_twitter_xml_dmsg($twitter_dm); + } + } else { + while ($message->fetch()) { + $twitter_dm = $this->twitter_dmsg_array($message); + $this->show_twitter_xml_dmsg($twitter_dm); + } + } + + common_element_end('direct-messages'); + $this->end_document('xml'); + + } + + function show_json_dmsgs($message) + { + + $this->init_document('json'); + + $dmsgs = array(); + + if (is_array($message)) { + foreach ($message as $m) { + $twitter_dm = $this->twitter_dmsg_array($m); + array_push($dmsgs, $twitter_dm); + } + } else { + while ($message->fetch()) { + $twitter_dm = $this->twitter_dmsg_array($message); + array_push($dmsgs, $twitter_dm); + } + } + + $this->show_json_objects($dmsgs); + $this->end_document('json'); + + } + + function show_rss_dmsgs($message, $title, $link, $subtitle) + { + + $this->init_document('rss'); + + common_element_start('channel'); + common_element('title', null, $title); + + common_element('link', null, $link); + common_element('description', null, $subtitle); + common_element('language', null, 'en-us'); + common_element('ttl', null, '40'); + + if (is_array($message)) { + foreach ($message as $m) { + $entry = $this->twitter_rss_dmsg_array($m); + $this->show_twitter_rss_item($entry); + } + } else { + while ($message->fetch()) { + $entry = $this->twitter_rss_dmsg_array($message); + $this->show_twitter_rss_item($entry); + } + } + + common_element_end('channel'); + $this->end_twitter_rss(); + + } + + function show_atom_dmsgs($message, $title, $link, $subtitle) + { + + $this->init_document('atom'); + + common_element('title', null, $title); + $siteserver = common_config('site', 'server'); + common_element('id', null, "tag:$siteserver,2008:DirectMessage"); + common_element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null); + common_element('updated', null, common_date_iso8601(strftime('%c'))); + common_element('subtitle', null, $subtitle); + + if (is_array($message)) { + foreach ($message as $m) { + $entry = $this->twitter_rss_dmsg_array($m); + $this->show_twitter_atom_entry($entry); + } + } else { + while ($message->fetch()) { + $entry = $this->twitter_rss_dmsg_array($message); + $this->show_twitter_atom_entry($entry); + } + } + + $this->end_document('atom'); + } + + // swiped from MessageAction. Should it be place in util.php? + function notify($from, $to, $message) + { + mail_notify_message($message, $from, $to); + # XXX: Jabber, SMS notifications... probably queued + } } diff --git a/actions/twitapifavorites.php b/actions/twitapifavorites.php index 3eaff327a..55e04732f 100644 --- a/actions/twitapifavorites.php +++ b/actions/twitapifavorites.php @@ -21,155 +21,161 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/twitterapi.php'); -class TwitapifavoritesAction extends TwitterapiAction { - - function favorites($args, $apidata) { - parent::handle($args); - - $this->auth_user = $apidata['user']; - $user = $this->get_user($apidata['api_arg'], $apidata); - - if (!$user) { - $this->client_error('Not Found', 404, $apidata['content-type']); - return; - } - - $profile = $user->getProfile(); - - if (!$profile) { - common_server_error(_('User has no profile.')); - return; - } - - $page = $this->arg('page'); - - if (!$page) { - $page = 1; - } - - if (!$count) { - $count = 20; - } - - $notice = $user->favoriteNotices((($page-1)*20), $count); - - if (!$notice) { - common_server_error(_('Could not retrieve favorite notices.')); - return; - } - - $sitename = common_config('site', 'name'); - $siteserver = common_config('site', 'server'); - - $title = sprintf(_('%s / Favorites from %s'), $sitename, $user->nickname); - $id = "tag:$siteserver:favorites:".$user->id; - $link = common_local_url('favorites', array('nickname' => $user->nickname)); - $subtitle = sprintf(_('%s updates favorited by %s / %s.'), $sitename, $profile->getBestName(), $user->nickname); - - switch($apidata['content-type']) { - case 'xml': - $this->show_xml_timeline($notice); - break; - case 'rss': - $this->show_rss_timeline($notice, $title, $link, $subtitle); - break; - case 'atom': - $this->show_atom_timeline($notice, $title, $id, $link, $subtitle); - break; - case 'json': - $this->show_json_timeline($notice); - break; - default: - common_user_error(_('API method not found!'), $code = 404); - } - - } - - function create($args, $apidata) { - parent::handle($args); - - // Check for RESTfulness - if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) { - // XXX: Twitter just prints the err msg, no XML / JSON. - $this->client_error(_('This method requires a POST or DELETE.'), 400, $apidata['content-type']); - return; - } - - if (!in_array($apidata['content-type'], array('xml', 'json'))) { - common_user_error(_('API method not found!'), $code = 404); - return; - } - - $this->auth_user = $apidata['user']; - $user = $this->auth_user; - $notice_id = $apidata['api_arg']; - $notice = Notice::staticGet($notice_id); - - if (!$notice) { - $this->client_error(_('No status found with that ID.'), 404, $apidata['content-type']); - return; - } - - // XXX: Twitter lets you fave things repeatedly via api. - if ($user->hasFave($notice)) { - $this->client_error(_('This notice is already a favorite!'), 403, $apidata['content-type']); - return; - } - - $fave = Fave::addNew($user, $notice); - - if (!$fave) { - common_server_error(_('Could not create favorite.')); - return; - } - - $this->notify($fave, $notice, $user); - $user->blowFavesCache(); - - if ($apidata['content-type'] == 'xml') { - $this->show_single_xml_status($notice); - } elseif ($apidata['content-type'] == 'json') { - $this->show_single_json_status($notice); - } - - } - - function destroy($args, $apidata) { - parent::handle($args); - common_server_error(_('API method under construction.'), $code=501); - } - - // XXX: these two funcs swiped from faves. Maybe put in util.php, or some common base class? - - function notify($fave, $notice, $user) { - $other = User::staticGet('id', $notice->profile_id); - if ($other && $other->id != $user->id) { - if ($other->email && $other->emailnotifyfav) { - $this->notify_mail($other, $user, $notice); - } - # XXX: notify by IM - # XXX: notify by SMS - } - } - - function notify_mail($other, $user, $notice) { - $profile = $user->getProfile(); - $bestname = $profile->getBestName(); - $subject = sprintf(_('%s added your notice as a favorite'), $bestname); - $body = sprintf(_("%1\$s just added your notice from %2\$s as one of their favorites.\n\n" . - "In case you forgot, you can see the text of your notice here:\n\n" . - "%3\$s\n\n" . - "You can see the list of %1\$s's favorites here:\n\n" . - "%4\$s\n\n" . - "Faithfully yours,\n" . - "%5\$s\n"), - $bestname, - common_exact_date($notice->created), - common_local_url('shownotice', array('notice' => $notice->id)), - common_local_url('showfavorites', array('nickname' => $user->nickname)), - common_config('site', 'name')); - - mail_to_user($other, $subject, $body); - } +class TwitapifavoritesAction extends TwitterapiAction +{ + + function favorites($args, $apidata) + { + parent::handle($args); + + $this->auth_user = $apidata['user']; + $user = $this->get_user($apidata['api_arg'], $apidata); + + if (!$user) { + $this->client_error('Not Found', 404, $apidata['content-type']); + return; + } + + $profile = $user->getProfile(); + + if (!$profile) { + common_server_error(_('User has no profile.')); + return; + } + + $page = $this->arg('page'); + + if (!$page) { + $page = 1; + } + + if (!$count) { + $count = 20; + } + + $notice = $user->favoriteNotices((($page-1)*20), $count); + + if (!$notice) { + common_server_error(_('Could not retrieve favorite notices.')); + return; + } + + $sitename = common_config('site', 'name'); + $siteserver = common_config('site', 'server'); + + $title = sprintf(_('%s / Favorites from %s'), $sitename, $user->nickname); + $id = "tag:$siteserver:favorites:".$user->id; + $link = common_local_url('favorites', array('nickname' => $user->nickname)); + $subtitle = sprintf(_('%s updates favorited by %s / %s.'), $sitename, $profile->getBestName(), $user->nickname); + + switch($apidata['content-type']) { + case 'xml': + $this->show_xml_timeline($notice); + break; + case 'rss': + $this->show_rss_timeline($notice, $title, $link, $subtitle); + break; + case 'atom': + $this->show_atom_timeline($notice, $title, $id, $link, $subtitle); + break; + case 'json': + $this->show_json_timeline($notice); + break; + default: + common_user_error(_('API method not found!'), $code = 404); + } + + } + + function create($args, $apidata) + { + parent::handle($args); + + // Check for RESTfulness + if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) { + // XXX: Twitter just prints the err msg, no XML / JSON. + $this->client_error(_('This method requires a POST or DELETE.'), 400, $apidata['content-type']); + return; + } + + if (!in_array($apidata['content-type'], array('xml', 'json'))) { + common_user_error(_('API method not found!'), $code = 404); + return; + } + + $this->auth_user = $apidata['user']; + $user = $this->auth_user; + $notice_id = $apidata['api_arg']; + $notice = Notice::staticGet($notice_id); + + if (!$notice) { + $this->client_error(_('No status found with that ID.'), 404, $apidata['content-type']); + return; + } + + // XXX: Twitter lets you fave things repeatedly via api. + if ($user->hasFave($notice)) { + $this->client_error(_('This notice is already a favorite!'), 403, $apidata['content-type']); + return; + } + + $fave = Fave::addNew($user, $notice); + + if (!$fave) { + common_server_error(_('Could not create favorite.')); + return; + } + + $this->notify($fave, $notice, $user); + $user->blowFavesCache(); + + if ($apidata['content-type'] == 'xml') { + $this->show_single_xml_status($notice); + } elseif ($apidata['content-type'] == 'json') { + $this->show_single_json_status($notice); + } + + } + + function destroy($args, $apidata) + { + parent::handle($args); + common_server_error(_('API method under construction.'), $code=501); + } + + // XXX: these two funcs swiped from faves. Maybe put in util.php, or some common base class? + + function notify($fave, $notice, $user) + { + $other = User::staticGet('id', $notice->profile_id); + if ($other && $other->id != $user->id) { + if ($other->email && $other->emailnotifyfav) { + $this->notify_mail($other, $user, $notice); + } + # XXX: notify by IM + # XXX: notify by SMS + } + } + + function notify_mail($other, $user, $notice) + { + $profile = $user->getProfile(); + $bestname = $profile->getBestName(); + $subject = sprintf(_('%s added your notice as a favorite'), $bestname); + $body = sprintf(_("%1\$s just added your notice from %2\$s as one of their favorites.\n\n" . + "In case you forgot, you can see the text of your notice here:\n\n" . + "%3\$s\n\n" . + "You can see the list of %1\$s's favorites here:\n\n" . + "%4\$s\n\n" . + "Faithfully yours,\n" . + "%5\$s\n"), + $bestname, + common_exact_date($notice->created), + common_local_url('shownotice', array('notice' => $notice->id)), + common_local_url('showfavorites', array('nickname' => $user->nickname)), + common_config('site', 'name')); + + mail_to_user($other, $subject, $body); + } }
\ No newline at end of file diff --git a/actions/twitapifriendships.php b/actions/twitapifriendships.php index e4b49cbe4..ba4afe441 100644 --- a/actions/twitapifriendships.php +++ b/actions/twitapifriendships.php @@ -21,135 +21,139 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/twitterapi.php'); -class TwitapifriendshipsAction extends TwitterapiAction { +class TwitapifriendshipsAction extends TwitterapiAction +{ - function create($args, $apidata) { - parent::handle($args); + function create($args, $apidata) + { + parent::handle($args); - if ($_SERVER['REQUEST_METHOD'] != 'POST') { - $this->client_error(_('This method requires a POST.'), 400, $apidata['content-type']); - return; - } + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + $this->client_error(_('This method requires a POST.'), 400, $apidata['content-type']); + return; + } - $id = $apidata['api_arg']; + $id = $apidata['api_arg']; - $other = $this->get_user($id); + $other = $this->get_user($id); - if (!$other) { - $this->client_error(_('Could not follow user: User not found.'), 403, $apidata['content-type']); - return; - } + if (!$other) { + $this->client_error(_('Could not follow user: User not found.'), 403, $apidata['content-type']); + return; + } - $user = $apidata['user']; + $user = $apidata['user']; - if ($user->isSubscribed($other)) { - $errmsg = sprintf(_('Could not follow user: %s is already on your list.'), $other->nickname); - $this->client_error($errmsg, 403, $apidata['content-type']); - return; - } + if ($user->isSubscribed($other)) { + $errmsg = sprintf(_('Could not follow user: %s is already on your list.'), $other->nickname); + $this->client_error($errmsg, 403, $apidata['content-type']); + return; + } - $sub = new Subscription(); + $sub = new Subscription(); - $sub->query('BEGIN'); + $sub->query('BEGIN'); - $sub->subscriber = $user->id; - $sub->subscribed = $other->id; - $sub->created = DB_DataObject_Cast::dateTime(); # current time + $sub->subscriber = $user->id; + $sub->subscribed = $other->id; + $sub->created = DB_DataObject_Cast::dateTime(); # current time - $result = $sub->insert(); + $result = $sub->insert(); - if (!$result) { - $errmsg = sprintf(_('Could not follow user: %s is already on your list.'), $other->nickname); - $this->client_error($errmsg, 400, $apidata['content-type']); - return; - } + if (!$result) { + $errmsg = sprintf(_('Could not follow user: %s is already on your list.'), $other->nickname); + $this->client_error($errmsg, 400, $apidata['content-type']); + return; + } - $sub->query('COMMIT'); + $sub->query('COMMIT'); - mail_subscribe_notify($other, $user); + mail_subscribe_notify($other, $user); - $type = $apidata['content-type']; - $this->init_document($type); - $this->show_profile($other, $type); - $this->end_document($type); + $type = $apidata['content-type']; + $this->init_document($type); + $this->show_profile($other, $type); + $this->end_document($type); - } + } - function destroy($args, $apidata) { - parent::handle($args); + function destroy($args, $apidata) + { + parent::handle($args); - if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) { - $this->client_error(_('This method requires a POST or DELETE.'), 400, $apidata['content-type']); - return; - } + if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) { + $this->client_error(_('This method requires a POST or DELETE.'), 400, $apidata['content-type']); + return; + } - $id = $apidata['api_arg']; + $id = $apidata['api_arg']; - # We can't subscribe to a remote person, but we can unsub + # We can't subscribe to a remote person, but we can unsub - $other = $this->get_profile($id); - $user = $apidata['user']; + $other = $this->get_profile($id); + $user = $apidata['user']; - $sub = new Subscription(); - $sub->subscriber = $user->id; - $sub->subscribed = $other->id; + $sub = new Subscription(); + $sub->subscriber = $user->id; + $sub->subscribed = $other->id; - if ($sub->find(TRUE)) { - $sub->query('BEGIN'); - $sub->delete(); - $sub->query('COMMIT'); - } else { - $this->client_error(_('You are not friends with the specified user.'), 403, $apidata['content-type']); - return; - } + if ($sub->find(true)) { + $sub->query('BEGIN'); + $sub->delete(); + $sub->query('COMMIT'); + } else { + $this->client_error(_('You are not friends with the specified user.'), 403, $apidata['content-type']); + return; + } - $type = $apidata['content-type']; - $this->init_document($type); - $this->show_profile($other, $type); - $this->end_document($type); + $type = $apidata['content-type']; + $this->init_document($type); + $this->show_profile($other, $type); + $this->end_document($type); - } + } - function exists($args, $apidata) { - parent::handle($args); + function exists($args, $apidata) + { + parent::handle($args); - if (!in_array($apidata['content-type'], array('xml', 'json'))) { - common_user_error(_('API method not found!'), $code = 404); - return; - } + if (!in_array($apidata['content-type'], array('xml', 'json'))) { + common_user_error(_('API method not found!'), $code = 404); + return; + } - $user_a_id = $this->trimmed('user_a'); - $user_b_id = $this->trimmed('user_b'); + $user_a_id = $this->trimmed('user_a'); + $user_b_id = $this->trimmed('user_b'); - $user_a = $this->get_user($user_a_id); - $user_b = $this->get_user($user_b_id); + $user_a = $this->get_user($user_a_id); + $user_b = $this->get_user($user_b_id); - if (!$user_a || !$user_b) { - $this->client_error(_('Two user ids or screen_names must be supplied.'), 400, $apidata['content-type']); - return; - } + if (!$user_a || !$user_b) { + $this->client_error(_('Two user ids or screen_names must be supplied.'), 400, $apidata['content-type']); + return; + } - if ($user_a->isSubscribed($user_b)) { - $result = 'true'; - } else { - $result = 'false'; - } - - switch ($apidata['content-type']) { - case 'xml': - $this->init_document('xml'); - common_element('friends', NULL, $result); - $this->end_document('xml'); - break; - case 'json': - $this->init_document('json'); - print json_encode($result); - $this->end_document('json'); - break; - default: - break; - } - - } + if ($user_a->isSubscribed($user_b)) { + $result = 'true'; + } else { + $result = 'false'; + } + + switch ($apidata['content-type']) { + case 'xml': + $this->init_document('xml'); + common_element('friends', null, $result); + $this->end_document('xml'); + break; + case 'json': + $this->init_document('json'); + print json_encode($result); + $this->end_document('json'); + break; + default: + break; + } + + } }
\ No newline at end of file diff --git a/actions/twitapihelp.php b/actions/twitapihelp.php index c5d503e11..1b84cb11b 100644 --- a/actions/twitapihelp.php +++ b/actions/twitapihelp.php @@ -21,32 +21,35 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/twitterapi.php'); -class TwitapihelpAction extends TwitterapiAction { - - /* Returns the string "ok" in the requested format with a 200 OK HTTP status code. - * URL:http://identi.ca/api/help/test.format - * Formats: xml, json - */ - function test($args, $apidata) { - parent::handle($args); - - if ($apidata['content-type'] == 'xml') { - $this->init_document('xml'); - common_element('ok', NULL, 'true'); - $this->end_document('xml'); - } elseif ($apidata['content-type'] == 'json') { - $this->init_document('json'); - print '"ok"'; - $this->end_document('json'); - } else { - common_user_error(_('API method not found!'), $code=404); - } - - } - - function downtime_schedule($args, $apidata) { - parent::handle($args); - common_server_error(_('API method under construction.'), $code=501); - } +class TwitapihelpAction extends TwitterapiAction +{ + + /* Returns the string "ok" in the requested format with a 200 OK HTTP status code. + * URL:http://identi.ca/api/help/test.format + * Formats: xml, json + */ + function test($args, $apidata) + { + parent::handle($args); + + if ($apidata['content-type'] == 'xml') { + $this->init_document('xml'); + common_element('ok', null, 'true'); + $this->end_document('xml'); + } elseif ($apidata['content-type'] == 'json') { + $this->init_document('json'); + print '"ok"'; + $this->end_document('json'); + } else { + common_user_error(_('API method not found!'), $code=404); + } + + } + + function downtime_schedule($args, $apidata) + { + parent::handle($args); + common_server_error(_('API method under construction.'), $code=501); + } }
\ No newline at end of file diff --git a/actions/twitapilaconica.php b/actions/twitapilaconica.php new file mode 100644 index 000000000..722423fae --- /dev/null +++ b/actions/twitapilaconica.php @@ -0,0 +1,174 @@ +<?php +/** + * Laconica, the distributed open-source microblogging tool + * + * Laconica-only extensions to the Twitter-like API + * + * 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 Twitter + * @package Laconica + * @author Evan Prodromou <evan@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); +} + +require_once INSTALLDIR.'/lib/twitterapi.php'; + +/** + * Laconica-specific API methods + * + * This class handles all /laconica/ API methods. + * + * @category Twitter + * @package Laconica + * @author Evan Prodromou <evan@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/ + */ + +class TwitapilaconicaAction extends TwitterapiAction +{ + /** + * A version stamp for the API + * + * Returns a version number for this version of Laconica, which + * should make things a bit easier for upgrades. + * URL: http://identi.ca/api/laconica/version.(xml|json) + * Formats: xml, json + * + * @param array $args Web arguments + * @param array $apidata Twitter API data + * + * @return void + * + * @see ApiAction::process_command() + */ + + function version($args, $apidata) + { + parent::handle($args); + switch ($apidata['content-type']) { + case 'xml': + $this->init_document('xml'); + common_element('version', null, LACONICA_VERSION); + $this->end_document('xml'); + break; + case 'json': + $this->init_document('json'); + print '"'.LACONICA_VERSION.'"'; + $this->end_document('json'); + break; + default: + $this->client_error(_('API method not found!'), $code=404); + } + } + + /** + * Dump of configuration variables + * + * Gives a full dump of configuration variables for this instance + * of Laconica, minus variables that may be security-sensitive (like + * passwords). + * URL: http://identi.ca/api/laconica/config.(xml|json) + * Formats: xml, json + * + * @param array $args Web arguments + * @param array $apidata Twitter API data + * + * @return void + * + * @see ApiAction::process_command() + */ + + function config($args, $apidata) + { + static $keys = array('site' => array('name', 'server', 'theme', 'path', 'fancy', 'language', + 'email', 'broughtby', 'broughtbyurl', 'closed', + 'inviteonly', 'private'), + 'license' => array('url', 'title', 'image'), + 'nickname' => array('featured'), + 'throttle' => array('enabled', 'count', 'timespan'), + 'xmpp' => array('enabled', 'server', 'user')); + + parent::handle($args); + + switch ($apidata['content-type']) { + case 'xml': + $this->init_document('xml'); + common_element_start('config'); + // XXX: check that all sections and settings are legal XML elements + foreach ($keys as $section => $settings) { + common_element_start($section); + foreach ($settings as $setting) { + $value = common_config($section, $setting); + if (is_array($value)) { + $value = implode(',', $value); + } else if ($value === false) { + $value = 'false'; + } else if ($value === true) { + $value = 'true'; + } + common_element($setting, null, $value); + } + common_element_end($section); + } + common_element_end('config'); + $this->end_document('xml'); + break; + case 'json': + $result = array(); + foreach ($keys as $section => $settings) { + $result[$section] = array(); + foreach ($settings as $setting) { + $result[$section][$setting] = common_config($section, $setting); + } + } + $this->init_document('json'); + $this->show_json_objects($result); + $this->end_document('json'); + break; + default: + $this->client_error(_('API method not found!'), $code=404); + } + } + + /** + * WADL description of the API + * + * Gives a WADL description of the API provided by this version of the + * software. + * + * @param array $args Web arguments + * @param array $apidata Twitter API data + * + * @return void + * + * @see ApiAction::process_command() + */ + + function wadl($args, $apidata) + { + parent::handle($args); + common_server_error(_('API method under construction.'), 501); + } +} diff --git a/actions/twitapinotifications.php b/actions/twitapinotifications.php index 8d93309a2..a19d652c3 100644 --- a/actions/twitapinotifications.php +++ b/actions/twitapinotifications.php @@ -22,16 +22,19 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/twitterapi.php'); # This naming convention looks real sick -class TwitapinotificationsAction extends TwitterapiAction { +class TwitapinotificationsAction extends TwitterapiAction +{ - function follow($args, $apidata) { - parent::handle($args); - common_server_error(_('API method under construction.'), $code=501); - } + function follow($args, $apidata) + { + parent::handle($args); + common_server_error(_('API method under construction.'), $code=501); + } - function leave($args, $apidata) { - parent::handle($args); - common_server_error(_('API method under construction.'), $code=501); - } + function leave($args, $apidata) + { + parent::handle($args); + common_server_error(_('API method under construction.'), $code=501); + } }
\ No newline at end of file diff --git a/actions/twitapistatuses.php b/actions/twitapistatuses.php index 7b6598b10..e629d5cc4 100644 --- a/actions/twitapistatuses.php +++ b/actions/twitapistatuses.php @@ -21,543 +21,557 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/twitterapi.php'); -class TwitapistatusesAction extends TwitterapiAction { - - function public_timeline($args, $apidata) { - parent::handle($args); - - $sitename = common_config('site', 'name'); - $siteserver = common_config('site', 'server'); - $title = sprintf(_("%s public timeline"), $sitename); - $id = "tag:$siteserver:Statuses"; - $link = common_root_url(); - $subtitle = sprintf(_("%s updates from everyone!"), $sitename); - - // Number of public statuses to return by default -- Twitter sends 20 - $MAX_PUBSTATUSES = 20; - - // FIXME: To really live up to the spec we need to build a list - // of notices by users who have custom avatars, so fix this SQL -- Zach - - $page = $this->arg('page'); - $since_id = $this->arg('since_id'); - $before_id = $this->arg('before_id'); - - // NOTE: page, since_id, and before_id are extensions to Twitter API -- TB - if (!$page) { - $page = 1; - } - if (!$since_id) { - $since_id = 0; - } - if (!$before_id) { - $before_id = 0; - } - - $since = strtotime($this->arg('since')); - - $notice = Notice::publicStream((($page-1)*$MAX_PUBSTATUSES), $MAX_PUBSTATUSES, $since_id, $before_id, $since); - - if ($notice) { - - switch($apidata['content-type']) { - case 'xml': - $this->show_xml_timeline($notice); - break; - case 'rss': - $this->show_rss_timeline($notice, $title, $link, $subtitle); - break; - case 'atom': - $this->show_atom_timeline($notice, $title, $id, $link, $subtitle); - break; - case 'json': - $this->show_json_timeline($notice); - break; - default: - common_user_error(_('API method not found!'), $code = 404); - break; - } - - } else { - common_server_error(_('Couldn\'t find any statuses.'), $code = 503); - } - - } - - function friends_timeline($args, $apidata) { - parent::handle($args); - - $since = $this->arg('since'); - $since_id = $this->arg('since_id'); - $count = $this->arg('count'); - $page = $this->arg('page'); - $before_id = $this->arg('before_id'); - - if (!$page) { - $page = 1; - } - - if (!$count) { - $count = 20; - } - - if (!$since_id) { - $since_id = 0; - } - - // NOTE: before_id is an extension to Twitter API -- TB - if (!$before_id) { - $before_id = 0; - } - - $since = strtotime($this->arg('since')); - - $user = $this->get_user(NULL, $apidata); - $this->auth_user = $user; - - $profile = $user->getProfile(); - - $sitename = common_config('site', 'name'); - $siteserver = common_config('site', 'server'); - - $title = sprintf(_("%s and friends"), $user->nickname); - $id = "tag:$siteserver:friends:" . $user->id; - $link = common_local_url('all', array('nickname' => $user->nickname)); - $subtitle = sprintf(_('Updates from %1$s and friends on %2$s!'), $user->nickname, $sitename); - - $notice = $user->noticesWithFriends(($page-1)*20, $count, $since_id, $before_id, $since); - - switch($apidata['content-type']) { - case 'xml': - $this->show_xml_timeline($notice); - break; - case 'rss': - $this->show_rss_timeline($notice, $title, $link, $subtitle); - break; - case 'atom': - $this->show_atom_timeline($notice, $title, $id, $link, $subtitle); - break; - case 'json': - $this->show_json_timeline($notice); - break; - default: - common_user_error(_('API method not found!'), $code = 404); - } - - } - - function user_timeline($args, $apidata) { - parent::handle($args); - - $this->auth_user = $apidata['user']; - $user = $this->get_user($apidata['api_arg'], $apidata); - - if (!$user) { - $this->client_error('Not Found', 404, $apidata['content-type']); - return; - } - - $profile = $user->getProfile(); - - if (!$profile) { - common_server_error(_('User has no profile.')); - return; - } - - $count = $this->arg('count'); - $since = $this->arg('since'); - $since_id = $this->arg('since_id'); - $page = $this->arg('page'); - $before_id = $this->arg('before_id'); - - if (!$page) { - $page = 1; - } - - if (!$count) { - $count = 20; - } - - if (!$since_id) { - $since_id = 0; - } - - // NOTE: before_id is an extensions to Twitter API -- TB - if (!$before_id) { - $before_id = 0; - } - - $since = strtotime($this->arg('since')); - - $sitename = common_config('site', 'name'); - $siteserver = common_config('site', 'server'); - - $title = sprintf(_("%s timeline"), $user->nickname); - $id = "tag:$siteserver:user:".$user->id; - $link = common_local_url('showstream', array('nickname' => $user->nickname)); - $subtitle = sprintf(_('Updates from %1$s on %2$s!'), $user->nickname, $sitename); - - # FriendFeed's SUP protocol - # Also added RSS and Atom feeds - - $suplink = common_local_url('sup', NULL, $user->id); - header('X-SUP-ID: '.$suplink); - - # XXX: since - - $notice = $user->getNotices((($page-1)*20), $count, $since_id, $before_id, $since); - - switch($apidata['content-type']) { - case 'xml': - $this->show_xml_timeline($notice); - break; - case 'rss': - $this->show_rss_timeline($notice, $title, $link, $subtitle, $suplink); - break; - case 'atom': - $this->show_atom_timeline($notice, $title, $id, $link, $subtitle, $suplink); - break; - case 'json': - $this->show_json_timeline($notice); - break; - default: - common_user_error(_('API method not found!'), $code = 404); - } - - } - - function update($args, $apidata) { - - parent::handle($args); - - if (!in_array($apidata['content-type'], array('xml', 'json'))) { - common_user_error(_('API method not found!'), $code = 404); - return; - } - - if ($_SERVER['REQUEST_METHOD'] != 'POST') { - $this->client_error(_('This method requires a POST.'), 400, $apidata['content-type']); - return; - } - - $this->auth_user = $apidata['user']; - $user = $this->auth_user; - $status = $this->trimmed('status'); - $source = $this->trimmed('source'); - $in_reply_to_status_id = intval($this->trimmed('in_reply_to_status_id')); +class TwitapistatusesAction extends TwitterapiAction +{ + + function public_timeline($args, $apidata) + { + parent::handle($args); + + $sitename = common_config('site', 'name'); + $siteserver = common_config('site', 'server'); + $title = sprintf(_("%s public timeline"), $sitename); + $id = "tag:$siteserver:Statuses"; + $link = common_root_url(); + $subtitle = sprintf(_("%s updates from everyone!"), $sitename); + + // Number of public statuses to return by default -- Twitter sends 20 + $MAX_PUBSTATUSES = 20; + + // FIXME: To really live up to the spec we need to build a list + // of notices by users who have custom avatars, so fix this SQL -- Zach + + $page = $this->arg('page'); + $since_id = $this->arg('since_id'); + $before_id = $this->arg('before_id'); + + // NOTE: page, since_id, and before_id are extensions to Twitter API -- TB + if (!$page) { + $page = 1; + } + if (!$since_id) { + $since_id = 0; + } + if (!$before_id) { + $before_id = 0; + } + + $since = strtotime($this->arg('since')); + + $notice = Notice::publicStream((($page-1)*$MAX_PUBSTATUSES), $MAX_PUBSTATUSES, $since_id, $before_id, $since); + + if ($notice) { + + switch($apidata['content-type']) { + case 'xml': + $this->show_xml_timeline($notice); + break; + case 'rss': + $this->show_rss_timeline($notice, $title, $link, $subtitle); + break; + case 'atom': + $this->show_atom_timeline($notice, $title, $id, $link, $subtitle); + break; + case 'json': + $this->show_json_timeline($notice); + break; + default: + common_user_error(_('API method not found!'), $code = 404); + break; + } + + } else { + common_server_error(_('Couldn\'t find any statuses.'), $code = 503); + } + + } + + function friends_timeline($args, $apidata) + { + parent::handle($args); + + $since = $this->arg('since'); + $since_id = $this->arg('since_id'); + $count = $this->arg('count'); + $page = $this->arg('page'); + $before_id = $this->arg('before_id'); + + if (!$page) { + $page = 1; + } + + if (!$count) { + $count = 20; + } + + if (!$since_id) { + $since_id = 0; + } + + // NOTE: before_id is an extension to Twitter API -- TB + if (!$before_id) { + $before_id = 0; + } + + $since = strtotime($this->arg('since')); + + $user = $this->get_user(null, $apidata); + $this->auth_user = $user; + + $profile = $user->getProfile(); + + $sitename = common_config('site', 'name'); + $siteserver = common_config('site', 'server'); + + $title = sprintf(_("%s and friends"), $user->nickname); + $id = "tag:$siteserver:friends:" . $user->id; + $link = common_local_url('all', array('nickname' => $user->nickname)); + $subtitle = sprintf(_('Updates from %1$s and friends on %2$s!'), $user->nickname, $sitename); + + $notice = $user->noticesWithFriends(($page-1)*20, $count, $since_id, $before_id, $since); + + switch($apidata['content-type']) { + case 'xml': + $this->show_xml_timeline($notice); + break; + case 'rss': + $this->show_rss_timeline($notice, $title, $link, $subtitle); + break; + case 'atom': + $this->show_atom_timeline($notice, $title, $id, $link, $subtitle); + break; + case 'json': + $this->show_json_timeline($notice); + break; + default: + common_user_error(_('API method not found!'), $code = 404); + } + + } + + function user_timeline($args, $apidata) + { + parent::handle($args); + + $this->auth_user = $apidata['user']; + $user = $this->get_user($apidata['api_arg'], $apidata); + + if (!$user) { + $this->client_error('Not Found', 404, $apidata['content-type']); + return; + } + + $profile = $user->getProfile(); + + if (!$profile) { + common_server_error(_('User has no profile.')); + return; + } + + $count = $this->arg('count'); + $since = $this->arg('since'); + $since_id = $this->arg('since_id'); + $page = $this->arg('page'); + $before_id = $this->arg('before_id'); + + if (!$page) { + $page = 1; + } + + if (!$count) { + $count = 20; + } + + if (!$since_id) { + $since_id = 0; + } + + // NOTE: before_id is an extensions to Twitter API -- TB + if (!$before_id) { + $before_id = 0; + } + + $since = strtotime($this->arg('since')); + + $sitename = common_config('site', 'name'); + $siteserver = common_config('site', 'server'); + + $title = sprintf(_("%s timeline"), $user->nickname); + $id = "tag:$siteserver:user:".$user->id; + $link = common_local_url('showstream', array('nickname' => $user->nickname)); + $subtitle = sprintf(_('Updates from %1$s on %2$s!'), $user->nickname, $sitename); + + # FriendFeed's SUP protocol + # Also added RSS and Atom feeds + + $suplink = common_local_url('sup', null, $user->id); + header('X-SUP-ID: '.$suplink); + + # XXX: since + + $notice = $user->getNotices((($page-1)*20), $count, $since_id, $before_id, $since); + + switch($apidata['content-type']) { + case 'xml': + $this->show_xml_timeline($notice); + break; + case 'rss': + $this->show_rss_timeline($notice, $title, $link, $subtitle, $suplink); + break; + case 'atom': + $this->show_atom_timeline($notice, $title, $id, $link, $subtitle, $suplink); + break; + case 'json': + $this->show_json_timeline($notice); + break; + default: + common_user_error(_('API method not found!'), $code = 404); + } + + } + + function update($args, $apidata) + { + + parent::handle($args); + + if (!in_array($apidata['content-type'], array('xml', 'json'))) { + common_user_error(_('API method not found!'), $code = 404); + return; + } + + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + $this->client_error(_('This method requires a POST.'), 400, $apidata['content-type']); + return; + } + + $this->auth_user = $apidata['user']; + $user = $this->auth_user; + $status = $this->trimmed('status'); + $source = $this->trimmed('source'); + $in_reply_to_status_id = intval($this->trimmed('in_reply_to_status_id')); $reserved_sources = array('web', 'omb', 'mail', 'xmpp', 'api'); - if (!$source || in_array($source, $reserved_sources)) { - $source = 'api'; - } + if (!$source || in_array($source, $reserved_sources)) { + $source = 'api'; + } - if (!$status) { + if (!$status) { - // XXX: Note: In this case, Twitter simply returns '200 OK' - // No error is given, but the status is not posted to the - // user's timeline. Seems bad. Shouldn't we throw an - // errror? -- Zach - return; + // XXX: Note: In this case, Twitter simply returns '200 OK' + // No error is given, but the status is not posted to the + // user's timeline. Seems bad. Shouldn't we throw an + // errror? -- Zach + return; - } else { + } else { - $status_shortened = common_shorten_links($status); + $status_shortened = common_shorten_links($status); - if (mb_strlen($status_shortened) > 140) { + if (mb_strlen($status_shortened) > 140) { - // XXX: Twitter truncates anything over 140, flags the status - // as "truncated." Sending this error may screw up some clients - // that assume Twitter will truncate for them. Should we just - // truncate too? -- Zach - $this->client_error(_('That\'s too long. Max notice size is 140 chars.'), $code = 406, $apidata['content-type']); - return; + // XXX: Twitter truncates anything over 140, flags the status + // as "truncated." Sending this error may screw up some clients + // that assume Twitter will truncate for them. Should we just + // truncate too? -- Zach + $this->client_error(_('That\'s too long. Max notice size is 140 chars.'), $code = 406, $apidata['content-type']); + return; - } - } + } + } - // Check for commands - $inter = new CommandInterpreter(); - $cmd = $inter->handle_command($user, $status_shortened); + // Check for commands + $inter = new CommandInterpreter(); + $cmd = $inter->handle_command($user, $status_shortened); - if ($cmd) { + if ($cmd) { - if ($this->supported($cmd)) { - $cmd->execute(new Channel()); - } - - // cmd not supported? Twitter just returns your latest status. - // And, it returns your last status whether the cmd was successful - // or not! - $n = $user->getCurrentNotice(); - $apidata['api_arg'] = $n->id; - } else { + if ($this->supported($cmd)) { + $cmd->execute(new Channel()); + } + + // cmd not supported? Twitter just returns your latest status. + // And, it returns your last status whether the cmd was successful + // or not! + $n = $user->getCurrentNotice(); + $apidata['api_arg'] = $n->id; + } else { + + $reply_to = null; - $reply_to = NULL; - - if ($in_reply_to_status_id) { - - // check whether notice actually exists - $reply = Notice::staticGet($in_reply_to_status_id); - - if ($reply) { - $reply_to = $in_reply_to_status_id; - } else { - $this->client_error(_('Not found'), $code = 404, $apidata['content-type']); - return; - } - } - - $notice = Notice::saveNew($user->id, html_entity_decode($status, ENT_NOQUOTES, 'UTF-8'), - $source, 1, $reply_to); - - if (is_string($notice)) { - $this->server_error($notice); - return; - } - - common_broadcast_notice($notice); - $apidata['api_arg'] = $notice->id; - } - - $this->show($args, $apidata); - } - - function replies($args, $apidata) { - - parent::handle($args); - - $since = $this->arg('since'); - $count = $this->arg('count'); - $page = $this->arg('page'); - $since_id = $this->arg('since_id'); - $before_id = $this->arg('before_id'); - - $this->auth_user = $apidata['user']; - $user = $this->auth_user; - $profile = $user->getProfile(); - - $sitename = common_config('site', 'name'); - $siteserver = common_config('site', 'server'); - - $title = sprintf(_('%1$s / Updates replying to %2$s'), $sitename, $user->nickname); - $id = "tag:$siteserver:replies:".$user->id; - $link = common_local_url('replies', array('nickname' => $user->nickname)); - $subtitle = sprintf(_('%1$s updates that reply to updates from %2$s / %3$s.'), $sitename, $user->nickname, $profile->getBestName()); - - if (!$page) { - $page = 1; - } - - if (!$count) { - $count = 20; - } - - if (!$since_id) { - $since_id = 0; - } - - // NOTE: before_id is an extension to Twitter API -- TB - if (!$before_id) { - $before_id = 0; - } - - $since = strtotime($this->arg('since')); - - $notice = $user->getReplies((($page-1)*20), $count, $since_id, $before_id, $since); - $notices = array(); - - while ($notice->fetch()) { - $notices[] = clone($notice); - } - - switch($apidata['content-type']) { - case 'xml': - $this->show_xml_timeline($notices); - break; - case 'rss': - $this->show_rss_timeline($notices, $title, $link, $subtitle); - break; - case 'atom': - $this->show_atom_timeline($notices, $title, $id, $link, $subtitle); - break; - case 'json': - $this->show_json_timeline($notices); - break; - default: - common_user_error(_('API method not found!'), $code = 404); - } - - } - - function show($args, $apidata) { - parent::handle($args); - - if (!in_array($apidata['content-type'], array('xml', 'json'))) { - common_user_error(_('API method not found!'), $code = 404); - return; - } - - $this->auth_user = $apidata['user']; - $notice_id = $apidata['api_arg']; - $notice = Notice::staticGet($notice_id); - - if ($notice) { - if ($apidata['content-type'] == 'xml') { - $this->show_single_xml_status($notice); - } elseif ($apidata['content-type'] == 'json') { - $this->show_single_json_status($notice); - } - } else { - // XXX: Twitter just sets a 404 header and doens't bother to return an err msg - $this->client_error(_('No status with that ID found.'), 404, $apidata['content-type']); - } - - } - - function destroy($args, $apidata) { - - parent::handle($args); - - if (!in_array($apidata['content-type'], array('xml', 'json'))) { - common_user_error(_('API method not found!'), $code = 404); - return; - } - - // Check for RESTfulness - if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) { - // XXX: Twitter just prints the err msg, no XML / JSON. - $this->client_error(_('This method requires a POST or DELETE.'), 400, $apidata['content-type']); - return; - } - - $this->auth_user = $apidata['user']; - $user = $this->auth_user; - $notice_id = $apidata['api_arg']; - $notice = Notice::staticGet($notice_id); - - if (!$notice) { - $this->client_error(_('No status found with that ID.'), 404, $apidata['content-type']); - return; - } - - if ($user->id == $notice->profile_id) { - $replies = new Reply; - $replies->get('notice_id', $notice_id); - common_dequeue_notice($notice); - $replies->delete(); - $notice->delete(); - - if ($apidata['content-type'] == 'xml') { - $this->show_single_xml_status($notice); - } elseif ($apidata['content-type'] == 'json') { - $this->show_single_json_status($notice); - } - } else { - $this->client_error(_('You may not delete another user\'s status.'), 403, $apidata['content-type']); - } - - } - - function friends($args, $apidata) { - parent::handle($args); - return $this->subscriptions($apidata, 'subscribed', 'subscriber'); - } - - function followers($args, $apidata) { - parent::handle($args); - - return $this->subscriptions($apidata, 'subscriber', 'subscribed'); - } - - function subscriptions($apidata, $other_attr, $user_attr) { - - # XXX: lite - - $this->auth_user = $apidate['user']; - $user = $this->get_user($apidata['api_arg'], $apidata); - - if (!$user) { - $this->client_error('Not Found', 404, $apidata['content-type']); - return; - } - - $page = $this->trimmed('page'); - - if (!$page || !is_numeric($page)) { - $page = 1; - } - - $profile = $user->getProfile(); - - if (!$profile) { - common_server_error(_('User has no profile.')); - return; - } - - $sub = new Subscription(); - $sub->$user_attr = $profile->id; - - $since = strtotime($this->trimmed('since')); - - if ($since) { - $d = date('Y-m-d H:i:s', $since); - $sub->whereAdd("created > '$d'"); - } - - $sub->orderBy('created DESC'); - $sub->limit(($page-1)*100, 100); - - $others = array(); - - if ($sub->find()) { - while ($sub->fetch()) { - $others[] = Profile::staticGet($sub->$other_attr); - } - } else { - // user has no followers - } - - $type = $apidata['content-type']; - - $this->init_document($type); - $this->show_profiles($others, $type); - $this->end_document($type); - } - - function show_profiles($profiles, $type) { - switch ($type) { - case 'xml': - common_element_start('users', array('type' => 'array')); - foreach ($profiles as $profile) { - $this->show_profile($profile); - } - common_element_end('users'); - break; - case 'json': - $arrays = array(); - foreach ($profiles as $profile) { - $arrays[] = $this->twitter_user_array($profile, true); - } - print json_encode($arrays); - break; - default: - $this->client_error(_('unsupported file type')); - } - } - - function featured($args, $apidata) { - parent::handle($args); - common_server_error(_('API method under construction.'), $code=501); - } - - function supported($cmd) { - - $cmdlist = array('MessageCommand', 'SubCommand', 'UnsubCommand', 'FavCommand', 'OnCommand', 'OffCommand'); - - if (in_array(get_class($cmd), $cmdlist)) { - return true; - } - - return false; - } + if ($in_reply_to_status_id) { + + // check whether notice actually exists + $reply = Notice::staticGet($in_reply_to_status_id); + + if ($reply) { + $reply_to = $in_reply_to_status_id; + } else { + $this->client_error(_('Not found'), $code = 404, $apidata['content-type']); + return; + } + } + + $notice = Notice::saveNew($user->id, html_entity_decode($status, ENT_NOQUOTES, 'UTF-8'), + $source, 1, $reply_to); + + if (is_string($notice)) { + $this->server_error($notice); + return; + } + + common_broadcast_notice($notice); + $apidata['api_arg'] = $notice->id; + } + + $this->show($args, $apidata); + } + + function replies($args, $apidata) + { + + parent::handle($args); + + $since = $this->arg('since'); + $count = $this->arg('count'); + $page = $this->arg('page'); + $since_id = $this->arg('since_id'); + $before_id = $this->arg('before_id'); + + $this->auth_user = $apidata['user']; + $user = $this->auth_user; + $profile = $user->getProfile(); + + $sitename = common_config('site', 'name'); + $siteserver = common_config('site', 'server'); + + $title = sprintf(_('%1$s / Updates replying to %2$s'), $sitename, $user->nickname); + $id = "tag:$siteserver:replies:".$user->id; + $link = common_local_url('replies', array('nickname' => $user->nickname)); + $subtitle = sprintf(_('%1$s updates that reply to updates from %2$s / %3$s.'), $sitename, $user->nickname, $profile->getBestName()); + + if (!$page) { + $page = 1; + } + + if (!$count) { + $count = 20; + } + + if (!$since_id) { + $since_id = 0; + } + + // NOTE: before_id is an extension to Twitter API -- TB + if (!$before_id) { + $before_id = 0; + } + + $since = strtotime($this->arg('since')); + + $notice = $user->getReplies((($page-1)*20), $count, $since_id, $before_id, $since); + $notices = array(); + + while ($notice->fetch()) { + $notices[] = clone($notice); + } + + switch($apidata['content-type']) { + case 'xml': + $this->show_xml_timeline($notices); + break; + case 'rss': + $this->show_rss_timeline($notices, $title, $link, $subtitle); + break; + case 'atom': + $this->show_atom_timeline($notices, $title, $id, $link, $subtitle); + break; + case 'json': + $this->show_json_timeline($notices); + break; + default: + common_user_error(_('API method not found!'), $code = 404); + } + + } + + function show($args, $apidata) + { + parent::handle($args); + + if (!in_array($apidata['content-type'], array('xml', 'json'))) { + common_user_error(_('API method not found!'), $code = 404); + return; + } + + $this->auth_user = $apidata['user']; + $notice_id = $apidata['api_arg']; + $notice = Notice::staticGet($notice_id); + + if ($notice) { + if ($apidata['content-type'] == 'xml') { + $this->show_single_xml_status($notice); + } elseif ($apidata['content-type'] == 'json') { + $this->show_single_json_status($notice); + } + } else { + // XXX: Twitter just sets a 404 header and doens't bother to return an err msg + $this->client_error(_('No status with that ID found.'), 404, $apidata['content-type']); + } + + } + + function destroy($args, $apidata) + { + + parent::handle($args); + + if (!in_array($apidata['content-type'], array('xml', 'json'))) { + common_user_error(_('API method not found!'), $code = 404); + return; + } + + // Check for RESTfulness + if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) { + // XXX: Twitter just prints the err msg, no XML / JSON. + $this->client_error(_('This method requires a POST or DELETE.'), 400, $apidata['content-type']); + return; + } + + $this->auth_user = $apidata['user']; + $user = $this->auth_user; + $notice_id = $apidata['api_arg']; + $notice = Notice::staticGet($notice_id); + + if (!$notice) { + $this->client_error(_('No status found with that ID.'), 404, $apidata['content-type']); + return; + } + + if ($user->id == $notice->profile_id) { + $replies = new Reply; + $replies->get('notice_id', $notice_id); + common_dequeue_notice($notice); + $replies->delete(); + $notice->delete(); + + if ($apidata['content-type'] == 'xml') { + $this->show_single_xml_status($notice); + } elseif ($apidata['content-type'] == 'json') { + $this->show_single_json_status($notice); + } + } else { + $this->client_error(_('You may not delete another user\'s status.'), 403, $apidata['content-type']); + } + + } + + function friends($args, $apidata) + { + parent::handle($args); + return $this->subscriptions($apidata, 'subscribed', 'subscriber'); + } + + function followers($args, $apidata) + { + parent::handle($args); + + return $this->subscriptions($apidata, 'subscriber', 'subscribed'); + } + + function subscriptions($apidata, $other_attr, $user_attr) + { + + # XXX: lite + + $this->auth_user = $apidate['user']; + $user = $this->get_user($apidata['api_arg'], $apidata); + + if (!$user) { + $this->client_error('Not Found', 404, $apidata['content-type']); + return; + } + + $page = $this->trimmed('page'); + + if (!$page || !is_numeric($page)) { + $page = 1; + } + + $profile = $user->getProfile(); + + if (!$profile) { + common_server_error(_('User has no profile.')); + return; + } + + $sub = new Subscription(); + $sub->$user_attr = $profile->id; + + $since = strtotime($this->trimmed('since')); + + if ($since) { + $d = date('Y-m-d H:i:s', $since); + $sub->whereAdd("created > '$d'"); + } + + $sub->orderBy('created DESC'); + $sub->limit(($page-1)*100, 100); + + $others = array(); + + if ($sub->find()) { + while ($sub->fetch()) { + $others[] = Profile::staticGet($sub->$other_attr); + } + } else { + // user has no followers + } + + $type = $apidata['content-type']; + + $this->init_document($type); + $this->show_profiles($others, $type); + $this->end_document($type); + } + + function show_profiles($profiles, $type) + { + switch ($type) { + case 'xml': + common_element_start('users', array('type' => 'array')); + foreach ($profiles as $profile) { + $this->show_profile($profile); + } + common_element_end('users'); + break; + case 'json': + $arrays = array(); + foreach ($profiles as $profile) { + $arrays[] = $this->twitter_user_array($profile, true); + } + print json_encode($arrays); + break; + default: + $this->client_error(_('unsupported file type')); + } + } + + function featured($args, $apidata) + { + parent::handle($args); + common_server_error(_('API method under construction.'), $code=501); + } + + function supported($cmd) + { + + $cmdlist = array('MessageCommand', 'SubCommand', 'UnsubCommand', 'FavCommand', 'OnCommand', 'OffCommand'); + + if (in_array(get_class($cmd), $cmdlist)) { + return true; + } + + return false; + } } diff --git a/actions/twitapiusers.php b/actions/twitapiusers.php index 9e06efa64..409986985 100644 --- a/actions/twitapiusers.php +++ b/actions/twitapiusers.php @@ -10,43 +10,45 @@ * * 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 + * 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/>. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/twitterapi.php'); -class TwitapiusersAction extends TwitterapiAction { +class TwitapiusersAction extends TwitterapiAction +{ - function show($args, $apidata) { - parent::handle($args); + function show($args, $apidata) + { + parent::handle($args); - if (!in_array($apidata['content-type'], array('xml', 'json'))) { - common_user_error(_('API method not found!'), $code = 404); - return; - } + if (!in_array($apidata['content-type'], array('xml', 'json'))) { + common_user_error(_('API method not found!'), $code = 404); + return; + } - $user = null; - $email = $this->arg('email'); + $user = null; + $email = $this->arg('email'); - if ($email) { - $user = User::staticGet('email', $email); - } elseif (isset($apidata['api_arg'])) { - $user = $this->get_user($apidata['api_arg']); - } + if ($email) { + $user = User::staticGet('email', $email); + } elseif (isset($apidata['api_arg'])) { + $user = $this->get_user($apidata['api_arg']); + } - if (!$user) { - // XXX: Twitter returns a random(?) user instead of throwing and err! -- Zach - $this->client_error(_('Not found.'), 404, $apidata['content-type']); - return; - } + if (!$user) { + // XXX: Twitter returns a random(?) user instead of throwing and err! -- Zach + $this->client_error(_('Not found.'), 404, $apidata['content-type']); + return; + } - $this->show_extended_profile($user, $apidata); - } + $this->show_extended_profile($user, $apidata); + } } diff --git a/actions/twittersettings.php b/actions/twittersettings.php index ae3aff877..d66427339 100644 --- a/actions/twittersettings.php +++ b/actions/twittersettings.php @@ -23,356 +23,345 @@ require_once(INSTALLDIR.'/lib/settingsaction.php'); define('SUBSCRIPTIONS', 80); -class TwittersettingsAction extends SettingsAction { +class TwittersettingsAction extends SettingsAction +{ - function get_instructions() { - return _('Add your Twitter account to automatically send your notices to Twitter, ' . - 'and subscribe to Twitter friends already here.'); - } + function get_instructions() + { + return _('Add your Twitter account to automatically send your notices to Twitter, ' . + 'and subscribe to Twitter friends already here.'); + } - function show_form($msg=NULL, $success=false) { - $user = common_current_user(); - $profile = $user->getProfile(); - $fuser = NULL; - $flink = Foreign_link::getByUserID($user->id, 1); // 1 == Twitter + function show_form($msg=null, $success=false) + { + $user = common_current_user(); + $profile = $user->getProfile(); + $fuser = null; + $flink = Foreign_link::getByUserID($user->id, 1); // 1 == Twitter - if ($flink) { - $fuser = $flink->getForeignUser(); - } + if ($flink) { + $fuser = $flink->getForeignUser(); + } - $this->form_header(_('Twitter settings'), $msg, $success); - common_element_start('form', array('method' => 'post', - 'id' => 'twittersettings', - 'action' => - common_local_url('twittersettings'))); - common_hidden('token', common_session_token()); + $this->form_header(_('Twitter settings'), $msg, $success); + common_element_start('form', array('method' => 'post', + 'id' => 'twittersettings', + 'action' => + common_local_url('twittersettings'))); + common_hidden('token', common_session_token()); - common_element('h2', NULL, _('Twitter Account')); + common_element('h2', null, _('Twitter Account')); - if ($fuser) { - common_element_start('p'); + if ($fuser) { + common_element_start('p'); - common_element('span', 'twitter_user', $fuser->nickname); - common_element('a', array('href' => $fuser->uri), $fuser->uri); - common_element('span', 'input_instructions', - _('Current verified Twitter account.')); - common_hidden('flink_foreign_id', $flink->foreign_id); - common_element_end('p'); - common_submit('remove', _('Remove')); - } else { - common_input('twitter_username', _('Twitter user name'), - ($this->arg('twitter_username')) ? $this->arg('twitter_username') : $profile->nickname, - _('No spaces, please.')); // hey, it's what Twitter says + common_element('span', 'twitter_user', $fuser->nickname); + common_element('a', array('href' => $fuser->uri), $fuser->uri); + common_element('span', 'input_instructions', + _('Current verified Twitter account.')); + common_hidden('flink_foreign_id', $flink->foreign_id); + common_element_end('p'); + common_submit('remove', _('Remove')); + } else { + common_input('twitter_username', _('Twitter user name'), + ($this->arg('twitter_username')) ? $this->arg('twitter_username') : $profile->nickname, + _('No spaces, please.')); // hey, it's what Twitter says - common_password('twitter_password', _('Twitter password')); - } + common_password('twitter_password', _('Twitter password')); + } - common_element('h2', NULL, _('Preferences')); + common_element('h2', null, _('Preferences')); - common_checkbox('noticesync', _('Automatically send my notices to Twitter.'), - ($flink) ? ($flink->noticesync & FOREIGN_NOTICE_SEND) : true); + common_checkbox('noticesync', _('Automatically send my notices to Twitter.'), + ($flink) ? ($flink->noticesync & FOREIGN_NOTICE_SEND) : true); - common_checkbox('replysync', _('Send local "@" replies to Twitter.'), - ($flink) ? ($flink->noticesync & FOREIGN_NOTICE_SEND_REPLY) : true); + common_checkbox('replysync', _('Send local "@" replies to Twitter.'), + ($flink) ? ($flink->noticesync & FOREIGN_NOTICE_SEND_REPLY) : true); - common_checkbox('friendsync', _('Subscribe to my Twitter friends here.'), - ($flink) ? ($flink->friendsync & FOREIGN_FRIEND_RECV) : false); + common_checkbox('friendsync', _('Subscribe to my Twitter friends here.'), + ($flink) ? ($flink->friendsync & FOREIGN_FRIEND_RECV) : false); - if ($flink) { - common_submit('save', _('Save')); - } else { - common_submit('add', _('Add')); - } + if ($flink) { + common_submit('save', _('Save')); + } else { + common_submit('add', _('Add')); + } - $this->show_twitter_subscriptions(); + $this->show_twitter_subscriptions(); - common_element_end('form'); + common_element_end('form'); - common_show_footer(); - } + common_show_footer(); + } - function subscribed_twitter_users() { + function subscribed_twitter_users() + { - $current_user = common_current_user(); + $current_user = common_current_user(); + + $qry = 'SELECT user.* ' . + 'FROM subscription ' . + 'JOIN user ON subscription.subscribed = user.id ' . + 'JOIN foreign_link ON foreign_link.user_id = user.id ' . + 'WHERE subscriber = %d ' . + 'ORDER BY user.nickname'; + + $user = new User(); + + $user->query(sprintf($qry, $current_user->id)); + + $users = array(); + + while ($user->fetch()) { + + // Don't include the user's own self-subscription + if ($user->id != $current_user->id) { + $users[] = clone($user); + } + } + + return $users; + } + + function show_twitter_subscriptions() + { + + $friends = $this->subscribed_twitter_users(); + $friends_count = count($friends); + + if ($friends_count > 0) { + + common_element('h3', null, _('Twitter Friends')); + common_element_start('div', array('id' => 'subscriptions')); + common_element_start('ul', array('id' => 'subscriptions_avatars')); + + for ($i = 0; $i < min($friends_count, SUBSCRIPTIONS); $i++) { + + $other = Profile::staticGet($friends[$i]->id); + + if (!$other) { + common_log_db_error($subs, 'SELECT', __FILE__); + continue; + } + + common_element_start('li'); + common_element_start('a', array('title' => ($other->fullname) ? + $other->fullname : + $other->nickname, + 'href' => $other->profileurl, + 'rel' => 'contact', + 'class' => 'subscription')); + $avatar = $other->getAvatar(AVATAR_MINI_SIZE); + common_element('img', array('src' => (($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_MINI_SIZE)), + 'width' => AVATAR_MINI_SIZE, + 'height' => AVATAR_MINI_SIZE, + 'class' => 'avatar mini', + 'alt' => ($other->fullname) ? + $other->fullname : + $other->nickname)); + common_element_end('a'); + common_element_end('li'); + + } + + common_element_end('ul'); + common_element_end('div'); - $qry = 'SELECT user.* ' . - 'FROM subscription ' . - 'JOIN user ON subscription.subscribed = user.id ' . - 'JOIN foreign_link ON foreign_link.user_id = user.id ' . - 'WHERE subscriber = %d ' . - 'ORDER BY user.nickname'; + } - $user = new User(); + // XXX Figure out a way to show all Twitter friends... ? - $user->query(sprintf($qry, $current_user->id)); + /* + if ($subs_count > SUBSCRIPTIONS) { + common_element_start('p', array('id' => 'subscriptions_viewall')); - $users = array(); + common_element('a', array('href' => common_local_url('subscriptions', + array('nickname' => $profile->nickname)), + 'class' => 'moresubscriptions'), + _('All subscriptions')); + common_element_end('p'); + } + */ + + } + + function handle_post() + { + + # CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $this->show_form(_('There was a problem with your session token. Try again, please.')); + return; + } + + if ($this->arg('save')) { + $this->save_preferences(); + } else if ($this->arg('add')) { + $this->add_twitter_acct(); + } else if ($this->arg('remove')) { + $this->remove_twitter_acct(); + } else { + $this->show_form(_('Unexpected form submission.')); + } + } + + function add_twitter_acct() + { + + $screen_name = $this->trimmed('twitter_username'); + $password = $this->trimmed('twitter_password'); + $noticesync = $this->boolean('noticesync'); + $replysync = $this->boolean('replysync'); + $friendsync = $this->boolean('friendsync'); + + if (!Validate::string($screen_name, + array( 'min_length' => 1, + 'max_length' => 15, + 'format' => VALIDATE_NUM . VALIDATE_ALPHA . '_'))) { + $this->show_form( + _('Username must have only numbers, upper- and lowercase letters, and underscore (_). 15 chars max.')); + return; + } + + if (!$this->verify_credentials($screen_name, $password)) { + $this->show_form(_('Could not verify your Twitter credentials!')); + return; + } + + $twit_user = twitter_user_info($screen_name, $password); + + if (!$twit_user) { + $this->show_form(sprintf(_('Unable to retrieve account information for "%s" from Twitter.'), + $screen_name)); + return; + } + + if (!save_twitter_user($twit_user->id, $screen_name)) { + $this->show_form(_('Unable to save your Twitter settings!')); + return; + } + + $user = common_current_user(); + + $flink = DB_DataObject::factory('foreign_link'); + $flink->user_id = $user->id; + $flink->foreign_id = $twit_user->id; + $flink->service = 1; // Twitter + $flink->credentials = $password; + $flink->created = common_sql_now(); + + $flink->set_flags($noticesync, $replysync, $friendsync); + + $flink_id = $flink->insert(); + + if (!$flink_id) { + common_log_db_error($flink, 'INSERT', __FILE__); + $this->show_form(_('Unable to save your Twitter settings!')); + return; + } - while ($user->fetch()) { + if ($friendsync) { + save_twitter_friends($user, $twit_user->id, $screen_name, $password); + } - // Don't include the user's own self-subscription - if ($user->id != $current_user->id) { - $users[] = clone($user); - } - } - - return $users; - } - - function show_twitter_subscriptions() { - - $friends = $this->subscribed_twitter_users(); - $friends_count = count($friends); - - if ($friends_count > 0) { - - common_element('h3', NULL, _('Twitter Friends')); - common_element_start('div', array('id' => 'subscriptions')); - common_element_start('ul', array('id' => 'subscriptions_avatars')); - - for ($i = 0; $i < min($friends_count, SUBSCRIPTIONS); $i++) { - - $other = Profile::staticGet($friends[$i]->id); - - if (!$other) { - common_log_db_error($subs, 'SELECT', __FILE__); - continue; - } - - common_element_start('li'); - common_element_start('a', array('title' => ($other->fullname) ? - $other->fullname : - $other->nickname, - 'href' => $other->profileurl, - 'rel' => 'contact', - 'class' => 'subscription')); - $avatar = $other->getAvatar(AVATAR_MINI_SIZE); - common_element('img', array('src' => (($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_MINI_SIZE)), - 'width' => AVATAR_MINI_SIZE, - 'height' => AVATAR_MINI_SIZE, - 'class' => 'avatar mini', - 'alt' => ($other->fullname) ? - $other->fullname : - $other->nickname)); - common_element_end('a'); - common_element_end('li'); - - } + $this->show_form(_('Twitter settings saved.'), true); + } + + function remove_twitter_acct() + { + + $user = common_current_user(); + $flink = Foreign_link::getByUserID($user->id, 1); + $flink_foreign_id = $this->arg('flink_foreign_id'); + + # Maybe an old tab open...? + if ($flink->foreign_id != $flink_foreign_id) { + $this->show_form(_('That is not your Twitter account.')); + return; + } - common_element_end('ul'); - common_element_end('div'); + $result = $flink->delete(); - } + if (!$result) { + common_log_db_error($flink, 'DELETE', __FILE__); + common_server_error(_('Couldn\'t remove Twitter user.')); + return; + } + + $this->show_form(_('Twitter account removed.'), true); + } + + function save_preferences() + { - // XXX Figure out a way to show all Twitter friends... ? + $noticesync = $this->boolean('noticesync'); + $friendsync = $this->boolean('friendsync'); + $replysync = $this->boolean('replysync'); - /* - if ($subs_count > SUBSCRIPTIONS) { - common_element_start('p', array('id' => 'subscriptions_viewall')); + $user = common_current_user(); - common_element('a', array('href' => common_local_url('subscriptions', - array('nickname' => $profile->nickname)), - 'class' => 'moresubscriptions'), - _('All subscriptions')); - common_element_end('p'); - } - */ + $flink = Foreign_link::getByUserID($user->id, 1); - } - - function handle_post() { + if (!$flink) { + common_log_db_error($flink, 'SELECT', __FILE__); + $this->show_form(_('Couldn\'t save Twitter preferences.')); + return; + } - # CSRF protection - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->show_form(_('There was a problem with your session token. Try again, please.')); - return; - } - - if ($this->arg('save')) { - $this->save_preferences(); - } else if ($this->arg('add')) { - $this->add_twitter_acct(); - } else if ($this->arg('remove')) { - $this->remove_twitter_acct(); - } else { - $this->show_form(_('Unexpected form submission.')); - } - } + $twitter_id = $flink->foreign_id; + $password = $flink->credentials; - function add_twitter_acct() { + $fuser = $flink->getForeignUser(); - $screen_name = $this->trimmed('twitter_username'); - $password = $this->trimmed('twitter_password'); - $noticesync = $this->boolean('noticesync'); - $replysync = $this->boolean('replysync'); - $friendsync = $this->boolean('friendsync'); - - if (!Validate::string($screen_name, - array( 'min_length' => 1, - 'max_length' => 15, - 'format' => VALIDATE_NUM . VALIDATE_ALPHA . '_'))) { - $this->show_form( - _('Username must have only numbers, upper- and lowercase letters, and underscore (_). 15 chars max.')); - return; - } - - if (!$this->verify_credentials($screen_name, $password)) { - $this->show_form(_('Could not verify your Twitter credentials!')); - return; - } - - $twit_user = twitter_user_info($screen_name, $password); - - if (!$twit_user) { - $this->show_form(sprintf(_('Unable to retrieve account information for "%s" from Twitter.'), - $screen_name)); - return; - } - - if (!save_twitter_user($twit_user->id, $screen_name)) { - $this->show_form(_('Unable to save your Twitter settings!')); - return; - } - - $user = common_current_user(); - - $flink = DB_DataObject::factory('foreign_link'); - $flink->user_id = $user->id; - $flink->foreign_id = $twit_user->id; - $flink->service = 1; // Twitter - $flink->credentials = $password; - $flink->created = common_sql_now(); - - $this->set_flags($flink, $noticesync, $replysync, $friendsync); - - $flink_id = $flink->insert(); - - if (!$flink_id) { - common_log_db_error($flink, 'INSERT', __FILE__); - $this->show_form(_('Unable to save your Twitter settings!')); - return; - } + if (!$fuser) { + common_log_db_error($fuser, 'SELECT', __FILE__); + $this->show_form(_('Couldn\'t save Twitter preferences.')); + return; + } - if ($friendsync) { - save_twitter_friends($user, $twit_user->id, $screen_name, $password); - } + $screen_name = $fuser->nickname; - $this->show_form(_('Twitter settings saved.'), true); - } + $original = clone($flink); + $flink->set_flags($noticesync, $replysync, $friendsync); + $result = $flink->update($original); - function remove_twitter_acct() { - - $user = common_current_user(); - $flink = Foreign_link::getByUserID($user->id, 1); - $flink_foreign_id = $this->arg('flink_foreign_id'); - - # Maybe an old tab open...? - if ($flink->foreign_id != $flink_foreign_id) { - $this->show_form(_('That is not your Twitter account.')); - return; - } - - $result = $flink->delete(); - - if (!$result) { - common_log_db_error($flink, 'DELETE', __FILE__); - common_server_error(_('Couldn\'t remove Twitter user.')); - return; - } - - $this->show_form(_('Twitter account removed.'), TRUE); - } - - function save_preferences() { - - $noticesync = $this->boolean('noticesync'); - $friendsync = $this->boolean('friendsync'); - $replysync = $this->boolean('replysync'); - - $user = common_current_user(); - - $flink = Foreign_link::getByUserID($user->id, 1); - - if (!$flink) { - common_log_db_error($flink, 'SELECT', __FILE__); - $this->show_form(_('Couldn\'t save Twitter preferences.')); - return; - } + if ($result === false) { + common_log_db_error($flink, 'UPDATE', __FILE__); + $this->show_form(_('Couldn\'t save Twitter preferences.')); + return; + } - $twitter_id = $flink->foreign_id; - $password = $flink->credentials; - - $fuser = $flink->getForeignUser(); + if ($friendsync) { + save_twitter_friends($user, $flink->foreign_id, $screen_name, $password); + } - if (!$fuser) { - common_log_db_error($fuser, 'SELECT', __FILE__); - $this->show_form(_('Couldn\'t save Twitter preferences.')); - return; - } + $this->show_form(_('Twitter preferences saved.')); + } - $screen_name = $fuser->nickname; + function verify_credentials($screen_name, $password) + { + $uri = 'http://twitter.com/account/verify_credentials.json'; + $data = get_twitter_data($uri, $screen_name, $password); - $original = clone($flink); - $this->set_flags($flink, $noticesync, $replysync, $friendsync); - $result = $flink->update($original); + if (!$data) { + return false; + } - if ($result === FALSE) { - common_log_db_error($flink, 'UPDATE', __FILE__); - $this->show_form(_('Couldn\'t save Twitter preferences.')); - return; - } + $user = json_decode($data); - if ($friendsync) { - save_twitter_friends($user, $flink->foreign_id, $screen_name, $password); - } + if (!$user) { + return false; + } - $this->show_form(_('Twitter preferences saved.')); - } + $twitter_id = $user->status->id; - function verify_credentials($screen_name, $password) { - $uri = 'http://twitter.com/account/verify_credentials.json'; - $data = get_twitter_data($uri, $screen_name, $password); + if ($twitter_id) { + return $twitter_id; + } - if (!$data) { - return false; - } + return false; + } - $user = json_decode($data); - - if (!$user) { - return false; - } - - $twitter_id = $user->status->id; - - if ($twitter_id) { - return $twitter_id; - } - - return false; - } - - function set_flags(&$flink, $noticesync, $replysync, $friendsync) { - if ($noticesync) { - $flink->noticesync |= FOREIGN_NOTICE_SEND; - } else { - $flink->noticesync &= ~FOREIGN_NOTICE_SEND; - } - - if ($replysync) { - $flink->noticesync |= FOREIGN_NOTICE_SEND_REPLY; - } else { - $flink->noticesync &= ~FOREIGN_NOTICE_SEND_REPLY; - } - - if ($friendsync) { - $flink->friendsync |= FOREIGN_FRIEND_RECV; - } else { - $flink->friendsync &= ~FOREIGN_FRIEND_RECV; - } - - $flink->profilesync = 0; - } }
\ No newline at end of file diff --git a/actions/unblock.php b/actions/unblock.php index d60cc7088..112304f71 100644 --- a/actions/unblock.php +++ b/actions/unblock.php @@ -19,11 +19,13 @@ if (!defined('LACONICA')) { exit(1); } -class UnblockAction extends Action { +class UnblockAction extends Action +{ - var $profile = NULL; + var $profile = null; - function prepare($args) { + function prepare($args) + { parent::prepare($args); @@ -32,12 +34,12 @@ class UnblockAction extends Action { return false; } - $token = $this->trimmed('token'); + $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->client_error(_('There was a problem with your session token. Try again, please.')); - return; - } + if (!$token || $token != common_session_token()) { + $this->client_error(_('There was a problem with your session token. Try again, please.')); + return; + } $id = $this->trimmed('unblockto'); @@ -56,14 +58,16 @@ class UnblockAction extends Action { return true; } - function handle($args) { + function handle($args) + { parent::handle($args); if ($_SERVER['REQUEST_METHOD'] == 'POST') { $this->unblock_profile(); } } - function unblock_profile() { + function unblock_profile() + { $cur = common_current_user(); diff --git a/actions/unsubscribe.php b/actions/unsubscribe.php index 98291e897..1c2e13635 100644 --- a/actions/unsubscribe.php +++ b/actions/unsubscribe.php @@ -17,32 +17,34 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -class UnsubscribeAction extends Action { +class UnsubscribeAction extends Action +{ - function handle($args) { - parent::handle($args); - if (!common_logged_in()) { - common_user_error(_('Not logged in.')); - return; - } + function handle($args) + { + parent::handle($args); + if (!common_logged_in()) { + common_user_error(_('Not logged in.')); + return; + } - $user = common_current_user(); + $user = common_current_user(); - if ($_SERVER['REQUEST_METHOD'] != 'POST') { - common_redirect(common_local_url('subscriptions', array('nickname' => $user->nickname))); - return; - } + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + common_redirect(common_local_url('subscriptions', array('nickname' => $user->nickname))); + return; + } - # CSRF protection + # CSRF protection - $token = $this->trimmed('token'); + $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $this->client_error(_('There was a problem with your session token. Try again, please.')); - return; - } + if (!$token || $token != common_session_token()) { + $this->client_error(_('There was a problem with your session token. Try again, please.')); + return; + } - $other_id = $this->arg('unsubscribeto'); + $other_id = $this->arg('unsubscribeto'); if (!$other_id) { $this->client_error(_('No profile id in request.')); @@ -56,25 +58,25 @@ class UnsubscribeAction extends Action { return; } - $result = subs_unsubscribe_to($user, $other); + $result = subs_unsubscribe_to($user, $other); - if ($result != true) { - common_user_error($result); - return; - } + if ($result != true) { + common_user_error($result); + return; + } - if ($this->boolean('ajax')) { - common_start_html('text/xml;charset=utf-8', true); - common_element_start('head'); - common_element('title', null, _('Unsubscribed')); - common_element_end('head'); - common_element_start('body'); - common_subscribe_form($other); - common_element_end('body'); - common_element_end('html'); - } else { - common_redirect(common_local_url('subscriptions', array('nickname' => - $user->nickname))); + if ($this->boolean('ajax')) { + common_start_html('text/xml;charset=utf-8', true); + common_element_start('head'); + common_element('title', null, _('Unsubscribed')); + common_element_end('head'); + common_element_start('body'); + common_subscribe_form($other); + common_element_end('body'); + common_element_end('html'); + } else { + common_redirect(common_local_url('subscriptions', array('nickname' => + $user->nickname))); } - } + } } diff --git a/actions/updateprofile.php b/actions/updateprofile.php index 921e88e63..abb034c81 100644 --- a/actions/updateprofile.php +++ b/actions/updateprofile.php @@ -21,154 +21,157 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/omb.php'); -class UpdateprofileAction extends Action { - - function handle($args) { - parent::handle($args); - try { - common_remove_magic_from_request(); - $req = OAuthRequest::from_request(); - # Note: server-to-server function! - $server = omb_oauth_server(); - list($consumer, $token) = $server->verify_request($req); - if ($this->update_profile($req, $consumer, $token)) { - print "omb_version=".OMB_VERSION_01; - } - } catch (OAuthException $e) { - $this->server_error($e->getMessage()); - return; - } - } +class UpdateprofileAction extends Action +{ + + function handle($args) + { + parent::handle($args); + try { + common_remove_magic_from_request(); + $req = OAuthRequest::from_request(); + # Note: server-to-server function! + $server = omb_oauth_server(); + list($consumer, $token) = $server->verify_request($req); + if ($this->update_profile($req, $consumer, $token)) { + print "omb_version=".OMB_VERSION_01; + } + } catch (OAuthException $e) { + $this->server_error($e->getMessage()); + return; + } + } - function update_profile($req, $consumer, $token) { - $version = $req->get_parameter('omb_version'); - if ($version != OMB_VERSION_01) { - $this->client_error(_('Unsupported OMB version'), 400); - return false; - } - # First, check to see if listenee exists - $listenee = $req->get_parameter('omb_listenee'); - $remote = Remote_profile::staticGet('uri', $listenee); - if (!$remote) { - $this->client_error(_('Profile unknown'), 404); - return false; - } - # Second, check to see if they should be able to post updates! - # We see if there are any subscriptions to that remote user with - # the given token. + function update_profile($req, $consumer, $token) + { + $version = $req->get_parameter('omb_version'); + if ($version != OMB_VERSION_01) { + $this->client_error(_('Unsupported OMB version'), 400); + return false; + } + # First, check to see if listenee exists + $listenee = $req->get_parameter('omb_listenee'); + $remote = Remote_profile::staticGet('uri', $listenee); + if (!$remote) { + $this->client_error(_('Profile unknown'), 404); + return false; + } + # Second, check to see if they should be able to post updates! + # We see if there are any subscriptions to that remote user with + # the given token. - $sub = new Subscription(); - $sub->subscribed = $remote->id; - $sub->token = $token->key; - if (!$sub->find(true)) { - $this->client_error(_('You did not send us that profile'), 403); - return false; - } + $sub = new Subscription(); + $sub->subscribed = $remote->id; + $sub->token = $token->key; + if (!$sub->find(true)) { + $this->client_error(_('You did not send us that profile'), 403); + return false; + } - $profile = Profile::staticGet('id', $remote->id); - if (!$profile) { - # This one is our fault - $this->server_error(_('Remote profile with no matching profile'), 500); - return false; - } - $nickname = $req->get_parameter('omb_listenee_nickname'); - if ($nickname && !Validate::string($nickname, array('min_length' => 1, - 'max_length' => 64, - 'format' => VALIDATE_NUM . VALIDATE_ALPHA_LOWER))) { - $this->client_error(_('Nickname must have only lowercase letters and numbers and no spaces.')); - return false; - } - $license = $req->get_parameter('omb_listenee_license'); - if ($license && !common_valid_http_url($license)) { - $this->client_error(sprintf(_("Invalid license URL '%s'"), $license)); - return false; - } - $profile_url = $req->get_parameter('omb_listenee_profile'); - if ($profile_url && !common_valid_http_url($profile_url)) { - $this->client_error(sprintf(_("Invalid profile URL '%s'."), $profile_url)); - return false; - } - # optional stuff - $fullname = $req->get_parameter('omb_listenee_fullname'); - if ($fullname && strlen($fullname) > 255) { - $this->client_error(_("Full name is too long (max 255 chars).")); - return false; - } - $homepage = $req->get_parameter('omb_listenee_homepage'); - if ($homepage && (!common_valid_http_url($homepage) || strlen($homepage) > 255)) { - $this->client_error(sprintf(_("Invalid homepage '%s'"), $homepage)); - return false; - } - $bio = $req->get_parameter('omb_listenee_bio'); - if ($bio && strlen($bio) > 140) { - $this->client_error(_("Bio is too long (max 140 chars).")); - return false; - } - $location = $req->get_parameter('omb_listenee_location'); - if ($location && strlen($location) > 255) { - $this->client_error(_("Location is too long (max 255 chars).")); - return false; - } - $avatar = $req->get_parameter('omb_listenee_avatar'); - if ($avatar) { - if (!common_valid_http_url($avatar) || strlen($avatar) > 255) { - $this->client_error(sprintf(_("Invalid avatar URL '%s'"), $avatar)); - return false; - } - $size = @getimagesize($avatar); - if (!$size) { - $this->client_error(sprintf(_("Can't read avatar URL '%s'"), $avatar)); - return false; - } - if ($size[0] != AVATAR_PROFILE_SIZE || $size[1] != AVATAR_PROFILE_SIZE) { - $this->client_error(sprintf(_("Wrong size image at '%s'"), $avatar)); - return false; - } - if (!in_array($size[2], array(IMAGETYPE_GIF, IMAGETYPE_JPEG, - IMAGETYPE_PNG))) { - $this->client_error(sprintf(_("Wrong image type for '%s'"), $avatar)); - return false; - } - } + $profile = Profile::staticGet('id', $remote->id); + if (!$profile) { + # This one is our fault + $this->server_error(_('Remote profile with no matching profile'), 500); + return false; + } + $nickname = $req->get_parameter('omb_listenee_nickname'); + if ($nickname && !Validate::string($nickname, array('min_length' => 1, + 'max_length' => 64, + 'format' => VALIDATE_NUM . VALIDATE_ALPHA_LOWER))) { + $this->client_error(_('Nickname must have only lowercase letters and numbers and no spaces.')); + return false; + } + $license = $req->get_parameter('omb_listenee_license'); + if ($license && !common_valid_http_url($license)) { + $this->client_error(sprintf(_("Invalid license URL '%s'"), $license)); + return false; + } + $profile_url = $req->get_parameter('omb_listenee_profile'); + if ($profile_url && !common_valid_http_url($profile_url)) { + $this->client_error(sprintf(_("Invalid profile URL '%s'."), $profile_url)); + return false; + } + # optional stuff + $fullname = $req->get_parameter('omb_listenee_fullname'); + if ($fullname && strlen($fullname) > 255) { + $this->client_error(_("Full name is too long (max 255 chars).")); + return false; + } + $homepage = $req->get_parameter('omb_listenee_homepage'); + if ($homepage && (!common_valid_http_url($homepage) || strlen($homepage) > 255)) { + $this->client_error(sprintf(_("Invalid homepage '%s'"), $homepage)); + return false; + } + $bio = $req->get_parameter('omb_listenee_bio'); + if ($bio && strlen($bio) > 140) { + $this->client_error(_("Bio is too long (max 140 chars).")); + return false; + } + $location = $req->get_parameter('omb_listenee_location'); + if ($location && strlen($location) > 255) { + $this->client_error(_("Location is too long (max 255 chars).")); + return false; + } + $avatar = $req->get_parameter('omb_listenee_avatar'); + if ($avatar) { + if (!common_valid_http_url($avatar) || strlen($avatar) > 255) { + $this->client_error(sprintf(_("Invalid avatar URL '%s'"), $avatar)); + return false; + } + $size = @getimagesize($avatar); + if (!$size) { + $this->client_error(sprintf(_("Can't read avatar URL '%s'"), $avatar)); + return false; + } + if ($size[0] != AVATAR_PROFILE_SIZE || $size[1] != AVATAR_PROFILE_SIZE) { + $this->client_error(sprintf(_("Wrong size image at '%s'"), $avatar)); + return false; + } + if (!in_array($size[2], array(IMAGETYPE_GIF, IMAGETYPE_JPEG, + IMAGETYPE_PNG))) { + $this->client_error(sprintf(_("Wrong image type for '%s'"), $avatar)); + return false; + } + } - $orig_profile = clone($profile); + $orig_profile = clone($profile); - if ($nickname) { - $profile->nickname = $nickname; - } - if ($profile_url) { - $profile->profileurl = $profile_url; - } - if ($fullname) { - $profile->fullname = $fullname; - } - if ($homepage) { - $profile->homepage = $homepage; - } - if ($bio) { - $profile->bio = $bio; - } - if ($location) { - $profile->location = $location; - } + if ($nickname) { + $profile->nickname = $nickname; + } + if ($profile_url) { + $profile->profileurl = $profile_url; + } + if ($fullname) { + $profile->fullname = $fullname; + } + if ($homepage) { + $profile->homepage = $homepage; + } + if ($bio) { + $profile->bio = $bio; + } + if ($location) { + $profile->location = $location; + } - if (!$profile->update($orig_profile)) { - $this->server_error(_('Could not save new profile info'), 500); - return false; - } else { - if ($avatar) { - $temp_filename = tempnam(sys_get_temp_dir(), 'listenee_avatar'); - copy($avatar, $temp_filename); - if (!$profile->setOriginal($temp_filename)) { - $this->server_error(_('Could not save avatar info'), 500); - return false; - } - } - header('HTTP/1.1 200 OK'); - header('Content-type: text/plain'); - print 'Updated profile'; - print "\n"; - return true; - } - } + if (!$profile->update($orig_profile)) { + $this->server_error(_('Could not save new profile info'), 500); + return false; + } else { + if ($avatar) { + $temp_filename = tempnam(sys_get_temp_dir(), 'listenee_avatar'); + copy($avatar, $temp_filename); + if (!$profile->setOriginal($temp_filename)) { + $this->server_error(_('Could not save avatar info'), 500); + return false; + } + } + header('HTTP/1.1 200 OK'); + header('Content-type: text/plain'); + print 'Updated profile'; + print "\n"; + return true; + } + } } diff --git a/actions/userauthorization.php b/actions/userauthorization.php index ac0a0728c..05efbc16c 100644 --- a/actions/userauthorization.php +++ b/actions/userauthorization.php @@ -22,558 +22,580 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/omb.php'); define('TIMESTAMP_THRESHOLD', 300); -class UserauthorizationAction extends Action { - - function handle($args) { - parent::handle($args); - - if ($_SERVER['REQUEST_METHOD'] == 'POST') { - # CSRF protection - $token = $this->trimmed('token'); - if (!$token || $token != common_session_token()) { - $req = $this->get_stored_request(); - $this->show_form(_('There was a problem with your session token. Try again, please.'), $req); - return; - } - # We've shown the form, now post user's choice - $this->send_authorization(); - } else { - if (!common_logged_in()) { - # Go log in, and then come back - common_debug('saving URL for returnto', __FILE__); - common_set_returnto($_SERVER['REQUEST_URI']); - - common_debug('redirecting to login', __FILE__); - common_redirect(common_local_url('login')); - return; - } - try { - # this must be a new request - common_debug('getting new request', __FILE__); - $req = $this->get_new_request(); - if (!$req) { - $this->client_error(_('No request found!')); - } - common_debug('validating request', __FILE__); - # XXX: only validate new requests, since nonce is one-time use - $this->validate_request($req); - common_debug('showing form', __FILE__); - $this->store_request($req); - $this->show_form($req); - } catch (OAuthException $e) { - $this->clear_request(); - $this->client_error($e->getMessage()); - return; - } - - } - } - - function show_form($req) { - - $nickname = $req->get_parameter('omb_listenee_nickname'); - $profile = $req->get_parameter('omb_listenee_profile'); - $license = $req->get_parameter('omb_listenee_license'); - $fullname = $req->get_parameter('omb_listenee_fullname'); - $homepage = $req->get_parameter('omb_listenee_homepage'); - $bio = $req->get_parameter('omb_listenee_bio'); - $location = $req->get_parameter('omb_listenee_location'); - $avatar = $req->get_parameter('omb_listenee_avatar'); - - common_show_header(_('Authorize subscription')); - common_element('p', NULL, _('Please check these details to make sure '. - 'that you want to subscribe to this user\'s notices. '. - 'If you didn\'t just ask to subscribe to someone\'s notices, '. - 'click "Cancel".')); - common_element_start('div', 'profile'); - if ($avatar) { - common_element('img', array('src' => $avatar, - 'class' => 'avatar profile', - 'width' => AVATAR_PROFILE_SIZE, - 'height' => AVATAR_PROFILE_SIZE, - 'alt' => $nickname)); - } - common_element('a', array('href' => $profile, - 'class' => 'external profile nickname'), - $nickname); - if ($fullname) { - common_element_start('div', 'fullname'); - if ($homepage) { - common_element('a', array('href' => $homepage), - $fullname); - } else { - common_text($fullname); - } - common_element_end('div'); - } - if ($location) { - common_element('div', 'location', $location); - } - if ($bio) { - common_element('div', 'bio', $bio); - } - common_element_start('div', 'license'); - common_element('a', array('href' => $license, - 'class' => 'license'), - $license); - common_element_end('div'); - common_element_end('div'); - common_element_start('form', array('method' => 'post', - 'id' => 'userauthorization', - 'name' => 'userauthorization', - 'action' => common_local_url('userauthorization'))); - common_hidden('token', common_session_token()); - common_submit('accept', _('Accept')); - common_submit('reject', _('Reject')); - common_element_end('form'); - common_show_footer(); - } - - function send_authorization() { - $req = $this->get_stored_request(); - - if (!$req) { - common_user_error(_('No authorization request!')); - return; - } - - $callback = $req->get_parameter('oauth_callback'); - - if ($this->arg('accept')) { - if (!$this->authorize_token($req)) { - $this->client_error(_('Error authorizing token')); - } - if (!$this->save_remote_profile($req)) { - $this->client_error(_('Error saving remote profile')); - } - if (!$callback) { - $this->show_accept_message($req->get_parameter('oauth_token')); - } else { - $params = array(); - $params['oauth_token'] = $req->get_parameter('oauth_token'); - $params['omb_version'] = OMB_VERSION_01; - $user = User::staticGet('uri', $req->get_parameter('omb_listener')); - $profile = $user->getProfile(); - if (!$profile) { - common_log_db_error($user, 'SELECT', __FILE__); - $this->server_error(_('User without matching profile')); - return; - } - $params['omb_listener_nickname'] = $user->nickname; - $params['omb_listener_profile'] = common_local_url('showstream', - array('nickname' => $user->nickname)); - if ($profile->fullname) { - $params['omb_listener_fullname'] = $profile->fullname; - } - if ($profile->homepage) { - $params['omb_listener_homepage'] = $profile->homepage; - } - if ($profile->bio) { - $params['omb_listener_bio'] = $profile->bio; - } - if ($profile->location) { - $params['omb_listener_location'] = $profile->location; - } - $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); - if ($avatar) { - $params['omb_listener_avatar'] = $avatar->url; - } - $parts = array(); - foreach ($params as $k => $v) { - $parts[] = $k . '=' . OAuthUtil::urlencodeRFC3986($v); - } - $query_string = implode('&', $parts); - $parsed = parse_url($callback); - $url = $callback . (($parsed['query']) ? '&' : '?') . $query_string; - common_redirect($url, 303); - } - } else { - if (!$callback) { - $this->show_reject_message(); - } else { - # XXX: not 100% sure how to signal failure... just redirect without token? - common_redirect($callback, 303); - } - } - } - - function authorize_token(&$req) { - $consumer_key = $req->get_parameter('oauth_consumer_key'); - $token_field = $req->get_parameter('oauth_token'); - common_debug('consumer key = "'.$consumer_key.'"', __FILE__); - common_debug('token field = "'.$token_field.'"', __FILE__); - $rt = new Token(); - $rt->consumer_key = $consumer_key; - $rt->tok = $token_field; - $rt->type = 0; - $rt->state = 0; - common_debug('request token to look up: "'.print_r($rt,TRUE).'"'); - if ($rt->find(true)) { - common_debug('found request token to authorize', __FILE__); - $orig_rt = clone($rt); - $rt->state = 1; # Authorized but not used - if ($rt->update($orig_rt)) { - common_debug('updated request token so it is authorized', __FILE__); - return true; - } - } - return FALSE; - } - - # XXX: refactor with similar code in finishremotesubscribe.php - - function save_remote_profile(&$req) { - # FIXME: we should really do this when the consumer comes - # back for an access token. If they never do, we've got stuff in a - # weird state. - - $nickname = $req->get_parameter('omb_listenee_nickname'); - $fullname = $req->get_parameter('omb_listenee_fullname'); - $profile_url = $req->get_parameter('omb_listenee_profile'); - $homepage = $req->get_parameter('omb_listenee_homepage'); - $bio = $req->get_parameter('omb_listenee_bio'); - $location = $req->get_parameter('omb_listenee_location'); - $avatar_url = $req->get_parameter('omb_listenee_avatar'); - - $listenee = $req->get_parameter('omb_listenee'); - $remote = Remote_profile::staticGet('uri', $listenee); - - if ($remote) { - $exists = true; - $profile = Profile::staticGet($remote->id); - $orig_remote = clone($remote); - $orig_profile = clone($profile); - } else { - $exists = false; - $remote = new Remote_profile(); - $remote->uri = $listenee; - $profile = new Profile(); - } - - $profile->nickname = $nickname; - $profile->profileurl = $profile_url; - - if ($fullname) { - $profile->fullname = $fullname; - } - if ($homepage) { - $profile->homepage = $homepage; - } - if ($bio) { - $profile->bio = $bio; - } - if ($location) { - $profile->location = $location; - } - - if ($exists) { - $profile->update($orig_profile); - } else { - $profile->created = DB_DataObject_Cast::dateTime(); # current time - $id = $profile->insert(); - if (!$id) { - return FALSE; - } - $remote->id = $id; - } - - if ($exists) { - if (!$remote->update($orig_remote)) { - return FALSE; - } - } else { - $remote->created = DB_DataObject_Cast::dateTime(); # current time - if (!$remote->insert()) { - return FALSE; - } - } - - if ($avatar_url) { - if (!$this->add_avatar($profile, $avatar_url)) { - return FALSE; - } - } - - $user = common_current_user(); - $datastore = omb_oauth_datastore(); - $consumer = $this->get_consumer($datastore, $req); - $token = $this->get_token($datastore, $req, $consumer); - - $sub = new Subscription(); - $sub->subscriber = $user->id; - $sub->subscribed = $remote->id; - $sub->token = $token->key; # NOTE: request token, not valid for use! - $sub->created = DB_DataObject_Cast::dateTime(); # current time - - if (!$sub->insert()) { - return FALSE; - } - - return TRUE; - } - - function add_avatar($profile, $url) { - $temp_filename = tempnam(sys_get_temp_dir(), 'listenee_avatar'); - copy($url, $temp_filename); - return $profile->setOriginal($temp_filename); - } - - function show_accept_message($tok) { - common_show_header(_('Subscription authorized')); - common_element('p', NULL, - _('The subscription has been authorized, but no '. - 'callback URL was passed. Check with the site\'s instructions for '. - 'details on how to authorize the subscription. Your subscription token is:')); - common_element('blockquote', 'token', $tok); - common_show_footer(); - } - - function show_reject_message($tok) { - common_show_header(_('Subscription rejected')); - common_element('p', NULL, - _('The subscription has been rejected, but no '. - 'callback URL was passed. Check with the site\'s instructions for '. - 'details on how to fully reject the subscription.')); - common_show_footer(); - } - - function store_request($req) { - common_ensure_session(); - $_SESSION['userauthorizationrequest'] = $req; - } - - function clear_request() { - common_ensure_session(); - unset($_SESSION['userauthorizationrequest']); - } - - function get_stored_request() { - common_ensure_session(); - $req = $_SESSION['userauthorizationrequest']; - return $req; - } - - function get_new_request() { - common_remove_magic_from_request(); - $req = OAuthRequest::from_request(); - return $req; - } - - # Throws an OAuthException if anything goes wrong - - function validate_request(&$req) { - # OAuth stuff -- have to copy from OAuth.php since they're - # all private methods, and there's no user-authentication method - common_debug('checking version', __FILE__); - $this->check_version($req); - common_debug('getting datastore', __FILE__); - $datastore = omb_oauth_datastore(); - common_debug('getting consumer', __FILE__); - $consumer = $this->get_consumer($datastore, $req); - common_debug('getting token', __FILE__); - $token = $this->get_token($datastore, $req, $consumer); - common_debug('checking timestamp', __FILE__); - $this->check_timestamp($req); - common_debug('checking nonce', __FILE__); - $this->check_nonce($datastore, $req, $consumer, $token); - common_debug('checking signature', __FILE__); - $this->check_signature($req, $consumer, $token); - common_debug('validating omb stuff', __FILE__); - $this->validate_omb($req); - common_debug('done validating', __FILE__); - return true; - } - - function validate_omb(&$req) { - foreach (array('omb_version', 'omb_listener', 'omb_listenee', - 'omb_listenee_profile', 'omb_listenee_nickname', - 'omb_listenee_license') as $param) - { - if (!$req->get_parameter($param)) { - throw new OAuthException("Required parameter '$param' not found"); - } - } - # Now, OMB stuff - $version = $req->get_parameter('omb_version'); - if ($version != OMB_VERSION_01) { - throw new OAuthException("OpenMicroBlogging version '$version' not supported"); - } - $listener = $req->get_parameter('omb_listener'); - $user = User::staticGet('uri', $listener); - if (!$user) { - throw new OAuthException("Listener URI '$listener' not found here"); - } - $cur = common_current_user(); - if ($cur->id != $user->id) { - throw new OAuthException("Can't add for another user!"); - } - $listenee = $req->get_parameter('omb_listenee'); - if (!Validate::uri($listenee) && - !common_valid_tag($listenee)) { - throw new OAuthException("Listenee URI '$listenee' not a recognizable URI"); - } - if (strlen($listenee) > 255) { - throw new OAuthException("Listenee URI '$listenee' too long"); - } - - $other = User::staticGet('uri', $listenee); - if ($other) { - throw new OAuthException("Listenee URI '$listenee' is local user"); - } - - $remote = Remote_profile::staticGet('uri', $listenee); - if ($remote) { - $sub = new Subscription(); - $sub->subscriber = $user->id; - $sub->subscribed = $remote->id; - if ($sub->find(TRUE)) { - throw new OAuthException("Already subscribed to user!"); - } - } - $nickname = $req->get_parameter('omb_listenee_nickname'); - if (!Validate::string($nickname, array('min_length' => 1, - 'max_length' => 64, - 'format' => VALIDATE_NUM . VALIDATE_ALPHA_LOWER))) { - throw new OAuthException('Nickname must have only letters and numbers and no spaces.'); - } - $profile = $req->get_parameter('omb_listenee_profile'); - if (!common_valid_http_url($profile)) { - throw new OAuthException("Invalid profile URL '$profile'."); - } - - if ($profile == common_local_url('showstream', array('nickname' => $nickname))) { - throw new OAuthException("Profile URL '$profile' is for a local user."); - } - - $license = $req->get_parameter('omb_listenee_license'); - if (!common_valid_http_url($license)) { - throw new OAuthException("Invalid license URL '$license'."); - } - $site_license = common_config('license', 'url'); - if (!common_compatible_license($license, $site_license)) { - throw new OAuthException("Listenee stream license '$license' not compatible with site license '$site_license'."); - } - # optional stuff - $fullname = $req->get_parameter('omb_listenee_fullname'); - if ($fullname && strlen($fullname) > 255) { - throw new OAuthException("Full name '$fullname' too long."); - } - $homepage = $req->get_parameter('omb_listenee_homepage'); - if ($homepage && (!common_valid_http_url($homepage) || strlen($homepage) > 255)) { - throw new OAuthException("Invalid homepage '$homepage'"); - } - $bio = $req->get_parameter('omb_listenee_bio'); - if ($bio && strlen($bio) > 140) { - throw new OAuthException("Bio too long '$bio'"); - } - $location = $req->get_parameter('omb_listenee_location'); - if ($location && strlen($location) > 255) { - throw new OAuthException("Location too long '$location'"); - } - $avatar = $req->get_parameter('omb_listenee_avatar'); - if ($avatar) { - if (!common_valid_http_url($avatar) || strlen($avatar) > 255) { - throw new OAuthException("Invalid avatar URL '$avatar'"); - } - $size = @getimagesize($avatar); - if (!$size) { - throw new OAuthException("Can't read avatar URL '$avatar'"); - } - if ($size[0] != AVATAR_PROFILE_SIZE || $size[1] != AVATAR_PROFILE_SIZE) { - throw new OAuthException("Wrong size image at '$avatar'"); - } - if (!in_array($size[2], array(IMAGETYPE_GIF, IMAGETYPE_JPEG, - IMAGETYPE_PNG))) { - throw new OAuthException("Wrong image type for '$avatar'"); - } - } - $callback = $req->get_parameter('oauth_callback'); - if ($callback && !common_valid_http_url($callback)) { - throw new OAuthException("Invalid callback URL '$callback'"); - } - if ($callback && $callback == common_local_url('finishremotesubscribe')) { - throw new OAuthException("Callback URL '$callback' is for local site."); - } - } - - # Snagged from OAuthServer - - function check_version(&$req) { - $version = $req->get_parameter("oauth_version"); - if (!$version) { - $version = 1.0; - } - if ($version != 1.0) { - throw new OAuthException("OAuth version '$version' not supported"); - } - return $version; - } - - # Snagged from OAuthServer - - function get_consumer($datastore, $req) { - $consumer_key = @$req->get_parameter("oauth_consumer_key"); - if (!$consumer_key) { - throw new OAuthException("Invalid consumer key"); - } - - $consumer = $datastore->lookup_consumer($consumer_key); - if (!$consumer) { - throw new OAuthException("Invalid consumer"); - } - return $consumer; - } - - # Mostly cadged from OAuthServer - - function get_token($datastore, &$req, $consumer) {/*{{{*/ - $token_field = @$req->get_parameter('oauth_token'); - $token = $datastore->lookup_token($consumer, 'request', $token_field); - if (!$token) { - throw new OAuthException("Invalid $token_type token: $token_field"); - } - return $token; - } - - function check_timestamp(&$req) { - $timestamp = @$req->get_parameter('oauth_timestamp'); - $now = time(); - if ($now - $timestamp > TIMESTAMP_THRESHOLD) { - throw new OAuthException("Expired timestamp, yours $timestamp, ours $now"); - } - } - - # NOTE: don't call twice on the same request; will fail! - function check_nonce(&$datastore, &$req, $consumer, $token) { - $timestamp = @$req->get_parameter('oauth_timestamp'); - $nonce = @$req->get_parameter('oauth_nonce'); - $found = $datastore->lookup_nonce($consumer, $token, $nonce, $timestamp); - if ($found) { - throw new OAuthException("Nonce already used"); - } - return true; - } - - function check_signature(&$req, $consumer, $token) { - $signature_method = $this->get_signature_method($req); - $signature = $req->get_parameter('oauth_signature'); - $valid_sig = $signature_method->check_signature($req, - $consumer, - $token, - $signature); - if (!$valid_sig) { - throw new OAuthException("Invalid signature"); - } - } - - function get_signature_method(&$req) { - $signature_method = @$req->get_parameter("oauth_signature_method"); - if (!$signature_method) { - $signature_method = "PLAINTEXT"; - } - if ($signature_method != 'HMAC-SHA1') { - throw new OAuthException("Signature method '$signature_method' not supported."); - } - return omb_hmac_sha1(); - } +class UserauthorizationAction extends Action +{ + + function handle($args) + { + parent::handle($args); + + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + # CSRF protection + $token = $this->trimmed('token'); + if (!$token || $token != common_session_token()) { + $req = $this->get_stored_request(); + $this->show_form(_('There was a problem with your session token. Try again, please.'), $req); + return; + } + # We've shown the form, now post user's choice + $this->send_authorization(); + } else { + if (!common_logged_in()) { + # Go log in, and then come back + common_debug('saving URL for returnto', __FILE__); + common_set_returnto($_SERVER['REQUEST_URI']); + + common_debug('redirecting to login', __FILE__); + common_redirect(common_local_url('login')); + return; + } + try { + # this must be a new request + common_debug('getting new request', __FILE__); + $req = $this->get_new_request(); + if (!$req) { + $this->client_error(_('No request found!')); + } + common_debug('validating request', __FILE__); + # XXX: only validate new requests, since nonce is one-time use + $this->validate_request($req); + common_debug('showing form', __FILE__); + $this->store_request($req); + $this->show_form($req); + } catch (OAuthException $e) { + $this->clear_request(); + $this->client_error($e->getMessage()); + return; + } + + } + } + + function show_form($req) + { + + $nickname = $req->get_parameter('omb_listenee_nickname'); + $profile = $req->get_parameter('omb_listenee_profile'); + $license = $req->get_parameter('omb_listenee_license'); + $fullname = $req->get_parameter('omb_listenee_fullname'); + $homepage = $req->get_parameter('omb_listenee_homepage'); + $bio = $req->get_parameter('omb_listenee_bio'); + $location = $req->get_parameter('omb_listenee_location'); + $avatar = $req->get_parameter('omb_listenee_avatar'); + + common_show_header(_('Authorize subscription')); + common_element('p', null, _('Please check these details to make sure '. + 'that you want to subscribe to this user\'s notices. '. + 'If you didn\'t just ask to subscribe to someone\'s notices, '. + 'click "Cancel".')); + common_element_start('div', 'profile'); + if ($avatar) { + common_element('img', array('src' => $avatar, + 'class' => 'avatar profile', + 'width' => AVATAR_PROFILE_SIZE, + 'height' => AVATAR_PROFILE_SIZE, + 'alt' => $nickname)); + } + common_element('a', array('href' => $profile, + 'class' => 'external profile nickname'), + $nickname); + if ($fullname) { + common_element_start('div', 'fullname'); + if ($homepage) { + common_element('a', array('href' => $homepage), + $fullname); + } else { + common_text($fullname); + } + common_element_end('div'); + } + if ($location) { + common_element('div', 'location', $location); + } + if ($bio) { + common_element('div', 'bio', $bio); + } + common_element_start('div', 'license'); + common_element('a', array('href' => $license, + 'class' => 'license'), + $license); + common_element_end('div'); + common_element_end('div'); + common_element_start('form', array('method' => 'post', + 'id' => 'userauthorization', + 'name' => 'userauthorization', + 'action' => common_local_url('userauthorization'))); + common_hidden('token', common_session_token()); + common_submit('accept', _('Accept')); + common_submit('reject', _('Reject')); + common_element_end('form'); + common_show_footer(); + } + + function send_authorization() + { + $req = $this->get_stored_request(); + + if (!$req) { + common_user_error(_('No authorization request!')); + return; + } + + $callback = $req->get_parameter('oauth_callback'); + + if ($this->arg('accept')) { + if (!$this->authorize_token($req)) { + $this->client_error(_('Error authorizing token')); + } + if (!$this->save_remote_profile($req)) { + $this->client_error(_('Error saving remote profile')); + } + if (!$callback) { + $this->show_accept_message($req->get_parameter('oauth_token')); + } else { + $params = array(); + $params['oauth_token'] = $req->get_parameter('oauth_token'); + $params['omb_version'] = OMB_VERSION_01; + $user = User::staticGet('uri', $req->get_parameter('omb_listener')); + $profile = $user->getProfile(); + if (!$profile) { + common_log_db_error($user, 'SELECT', __FILE__); + $this->server_error(_('User without matching profile')); + return; + } + $params['omb_listener_nickname'] = $user->nickname; + $params['omb_listener_profile'] = common_local_url('showstream', + array('nickname' => $user->nickname)); + if ($profile->fullname) { + $params['omb_listener_fullname'] = $profile->fullname; + } + if ($profile->homepage) { + $params['omb_listener_homepage'] = $profile->homepage; + } + if ($profile->bio) { + $params['omb_listener_bio'] = $profile->bio; + } + if ($profile->location) { + $params['omb_listener_location'] = $profile->location; + } + $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); + if ($avatar) { + $params['omb_listener_avatar'] = $avatar->url; + } + $parts = array(); + foreach ($params as $k => $v) { + $parts[] = $k . '=' . OAuthUtil::urlencodeRFC3986($v); + } + $query_string = implode('&', $parts); + $parsed = parse_url($callback); + $url = $callback . (($parsed['query']) ? '&' : '?') . $query_string; + common_redirect($url, 303); + } + } else { + if (!$callback) { + $this->show_reject_message(); + } else { + # XXX: not 100% sure how to signal failure... just redirect without token? + common_redirect($callback, 303); + } + } + } + + function authorize_token(&$req) + { + $consumer_key = $req->get_parameter('oauth_consumer_key'); + $token_field = $req->get_parameter('oauth_token'); + common_debug('consumer key = "'.$consumer_key.'"', __FILE__); + common_debug('token field = "'.$token_field.'"', __FILE__); + $rt = new Token(); + $rt->consumer_key = $consumer_key; + $rt->tok = $token_field; + $rt->type = 0; + $rt->state = 0; + common_debug('request token to look up: "'.print_r($rt,true).'"'); + if ($rt->find(true)) { + common_debug('found request token to authorize', __FILE__); + $orig_rt = clone($rt); + $rt->state = 1; # Authorized but not used + if ($rt->update($orig_rt)) { + common_debug('updated request token so it is authorized', __FILE__); + return true; + } + } + return false; + } + + # XXX: refactor with similar code in finishremotesubscribe.php + + function save_remote_profile(&$req) + { + # FIXME: we should really do this when the consumer comes + # back for an access token. If they never do, we've got stuff in a + # weird state. + + $nickname = $req->get_parameter('omb_listenee_nickname'); + $fullname = $req->get_parameter('omb_listenee_fullname'); + $profile_url = $req->get_parameter('omb_listenee_profile'); + $homepage = $req->get_parameter('omb_listenee_homepage'); + $bio = $req->get_parameter('omb_listenee_bio'); + $location = $req->get_parameter('omb_listenee_location'); + $avatar_url = $req->get_parameter('omb_listenee_avatar'); + + $listenee = $req->get_parameter('omb_listenee'); + $remote = Remote_profile::staticGet('uri', $listenee); + + if ($remote) { + $exists = true; + $profile = Profile::staticGet($remote->id); + $orig_remote = clone($remote); + $orig_profile = clone($profile); + } else { + $exists = false; + $remote = new Remote_profile(); + $remote->uri = $listenee; + $profile = new Profile(); + } + + $profile->nickname = $nickname; + $profile->profileurl = $profile_url; + + if ($fullname) { + $profile->fullname = $fullname; + } + if ($homepage) { + $profile->homepage = $homepage; + } + if ($bio) { + $profile->bio = $bio; + } + if ($location) { + $profile->location = $location; + } + + if ($exists) { + $profile->update($orig_profile); + } else { + $profile->created = DB_DataObject_Cast::dateTime(); # current time + $id = $profile->insert(); + if (!$id) { + return false; + } + $remote->id = $id; + } + + if ($exists) { + if (!$remote->update($orig_remote)) { + return false; + } + } else { + $remote->created = DB_DataObject_Cast::dateTime(); # current time + if (!$remote->insert()) { + return false; + } + } + + if ($avatar_url) { + if (!$this->add_avatar($profile, $avatar_url)) { + return false; + } + } + + $user = common_current_user(); + $datastore = omb_oauth_datastore(); + $consumer = $this->get_consumer($datastore, $req); + $token = $this->get_token($datastore, $req, $consumer); + + $sub = new Subscription(); + $sub->subscriber = $user->id; + $sub->subscribed = $remote->id; + $sub->token = $token->key; # NOTE: request token, not valid for use! + $sub->created = DB_DataObject_Cast::dateTime(); # current time + + if (!$sub->insert()) { + return false; + } + + return true; + } + + function add_avatar($profile, $url) + { + $temp_filename = tempnam(sys_get_temp_dir(), 'listenee_avatar'); + copy($url, $temp_filename); + return $profile->setOriginal($temp_filename); + } + + function show_accept_message($tok) + { + common_show_header(_('Subscription authorized')); + common_element('p', null, + _('The subscription has been authorized, but no '. + 'callback URL was passed. Check with the site\'s instructions for '. + 'details on how to authorize the subscription. Your subscription token is:')); + common_element('blockquote', 'token', $tok); + common_show_footer(); + } + + function show_reject_message($tok) + { + common_show_header(_('Subscription rejected')); + common_element('p', null, + _('The subscription has been rejected, but no '. + 'callback URL was passed. Check with the site\'s instructions for '. + 'details on how to fully reject the subscription.')); + common_show_footer(); + } + + function store_request($req) + { + common_ensure_session(); + $_SESSION['userauthorizationrequest'] = $req; + } + + function clear_request() + { + common_ensure_session(); + unset($_SESSION['userauthorizationrequest']); + } + + function get_stored_request() + { + common_ensure_session(); + $req = $_SESSION['userauthorizationrequest']; + return $req; + } + + function get_new_request() + { + common_remove_magic_from_request(); + $req = OAuthRequest::from_request(); + return $req; + } + + # Throws an OAuthException if anything goes wrong + + function validate_request(&$req) + { + # OAuth stuff -- have to copy from OAuth.php since they're + # all private methods, and there's no user-authentication method + common_debug('checking version', __FILE__); + $this->check_version($req); + common_debug('getting datastore', __FILE__); + $datastore = omb_oauth_datastore(); + common_debug('getting consumer', __FILE__); + $consumer = $this->get_consumer($datastore, $req); + common_debug('getting token', __FILE__); + $token = $this->get_token($datastore, $req, $consumer); + common_debug('checking timestamp', __FILE__); + $this->check_timestamp($req); + common_debug('checking nonce', __FILE__); + $this->check_nonce($datastore, $req, $consumer, $token); + common_debug('checking signature', __FILE__); + $this->check_signature($req, $consumer, $token); + common_debug('validating omb stuff', __FILE__); + $this->validate_omb($req); + common_debug('done validating', __FILE__); + return true; + } + + function validate_omb(&$req) + { + foreach (array('omb_version', 'omb_listener', 'omb_listenee', + 'omb_listenee_profile', 'omb_listenee_nickname', + 'omb_listenee_license') as $param) + { + if (!$req->get_parameter($param)) { + throw new OAuthException("Required parameter '$param' not found"); + } + } + # Now, OMB stuff + $version = $req->get_parameter('omb_version'); + if ($version != OMB_VERSION_01) { + throw new OAuthException("OpenMicroBlogging version '$version' not supported"); + } + $listener = $req->get_parameter('omb_listener'); + $user = User::staticGet('uri', $listener); + if (!$user) { + throw new OAuthException("Listener URI '$listener' not found here"); + } + $cur = common_current_user(); + if ($cur->id != $user->id) { + throw new OAuthException("Can't add for another user!"); + } + $listenee = $req->get_parameter('omb_listenee'); + if (!Validate::uri($listenee) && + !common_valid_tag($listenee)) { + throw new OAuthException("Listenee URI '$listenee' not a recognizable URI"); + } + if (strlen($listenee) > 255) { + throw new OAuthException("Listenee URI '$listenee' too long"); + } + + $other = User::staticGet('uri', $listenee); + if ($other) { + throw new OAuthException("Listenee URI '$listenee' is local user"); + } + + $remote = Remote_profile::staticGet('uri', $listenee); + if ($remote) { + $sub = new Subscription(); + $sub->subscriber = $user->id; + $sub->subscribed = $remote->id; + if ($sub->find(true)) { + throw new OAuthException("Already subscribed to user!"); + } + } + $nickname = $req->get_parameter('omb_listenee_nickname'); + if (!Validate::string($nickname, array('min_length' => 1, + 'max_length' => 64, + 'format' => VALIDATE_NUM . VALIDATE_ALPHA_LOWER))) { + throw new OAuthException('Nickname must have only letters and numbers and no spaces.'); + } + $profile = $req->get_parameter('omb_listenee_profile'); + if (!common_valid_http_url($profile)) { + throw new OAuthException("Invalid profile URL '$profile'."); + } + + if ($profile == common_local_url('showstream', array('nickname' => $nickname))) { + throw new OAuthException("Profile URL '$profile' is for a local user."); + } + + $license = $req->get_parameter('omb_listenee_license'); + if (!common_valid_http_url($license)) { + throw new OAuthException("Invalid license URL '$license'."); + } + $site_license = common_config('license', 'url'); + if (!common_compatible_license($license, $site_license)) { + throw new OAuthException("Listenee stream license '$license' not compatible with site license '$site_license'."); + } + # optional stuff + $fullname = $req->get_parameter('omb_listenee_fullname'); + if ($fullname && strlen($fullname) > 255) { + throw new OAuthException("Full name '$fullname' too long."); + } + $homepage = $req->get_parameter('omb_listenee_homepage'); + if ($homepage && (!common_valid_http_url($homepage) || strlen($homepage) > 255)) { + throw new OAuthException("Invalid homepage '$homepage'"); + } + $bio = $req->get_parameter('omb_listenee_bio'); + if ($bio && strlen($bio) > 140) { + throw new OAuthException("Bio too long '$bio'"); + } + $location = $req->get_parameter('omb_listenee_location'); + if ($location && strlen($location) > 255) { + throw new OAuthException("Location too long '$location'"); + } + $avatar = $req->get_parameter('omb_listenee_avatar'); + if ($avatar) { + if (!common_valid_http_url($avatar) || strlen($avatar) > 255) { + throw new OAuthException("Invalid avatar URL '$avatar'"); + } + $size = @getimagesize($avatar); + if (!$size) { + throw new OAuthException("Can't read avatar URL '$avatar'"); + } + if ($size[0] != AVATAR_PROFILE_SIZE || $size[1] != AVATAR_PROFILE_SIZE) { + throw new OAuthException("Wrong size image at '$avatar'"); + } + if (!in_array($size[2], array(IMAGETYPE_GIF, IMAGETYPE_JPEG, + IMAGETYPE_PNG))) { + throw new OAuthException("Wrong image type for '$avatar'"); + } + } + $callback = $req->get_parameter('oauth_callback'); + if ($callback && !common_valid_http_url($callback)) { + throw new OAuthException("Invalid callback URL '$callback'"); + } + if ($callback && $callback == common_local_url('finishremotesubscribe')) { + throw new OAuthException("Callback URL '$callback' is for local site."); + } + } + + # Snagged from OAuthServer + + function check_version(&$req) + { + $version = $req->get_parameter("oauth_version"); + if (!$version) { + $version = 1.0; + } + if ($version != 1.0) { + throw new OAuthException("OAuth version '$version' not supported"); + } + return $version; + } + + # Snagged from OAuthServer + + function get_consumer($datastore, $req) + { + $consumer_key = @$req->get_parameter("oauth_consumer_key"); + if (!$consumer_key) { + throw new OAuthException("Invalid consumer key"); + } + + $consumer = $datastore->lookup_consumer($consumer_key); + if (!$consumer) { + throw new OAuthException("Invalid consumer"); + } + return $consumer; + } + + # Mostly cadged from OAuthServer + + function get_token($datastore, &$req, $consumer) + {/*{{{*/ + $token_field = @$req->get_parameter('oauth_token'); + $token = $datastore->lookup_token($consumer, 'request', $token_field); + if (!$token) { + throw new OAuthException("Invalid $token_type token: $token_field"); + } + return $token; + } + + function check_timestamp(&$req) + { + $timestamp = @$req->get_parameter('oauth_timestamp'); + $now = time(); + if ($now - $timestamp > TIMESTAMP_THRESHOLD) { + throw new OAuthException("Expired timestamp, yours $timestamp, ours $now"); + } + } + + # NOTE: don't call twice on the same request; will fail! + function check_nonce(&$datastore, &$req, $consumer, $token) + { + $timestamp = @$req->get_parameter('oauth_timestamp'); + $nonce = @$req->get_parameter('oauth_nonce'); + $found = $datastore->lookup_nonce($consumer, $token, $nonce, $timestamp); + if ($found) { + throw new OAuthException("Nonce already used"); + } + return true; + } + + function check_signature(&$req, $consumer, $token) + { + $signature_method = $this->get_signature_method($req); + $signature = $req->get_parameter('oauth_signature'); + $valid_sig = $signature_method->check_signature($req, + $consumer, + $token, + $signature); + if (!$valid_sig) { + throw new OAuthException("Invalid signature"); + } + } + + function get_signature_method(&$req) + { + $signature_method = @$req->get_parameter("oauth_signature_method"); + if (!$signature_method) { + $signature_method = "PLAINTEXT"; + } + if ($signature_method != 'HMAC-SHA1') { + throw new OAuthException("Signature method '$signature_method' not supported."); + } + return omb_hmac_sha1(); + } } diff --git a/actions/userbyid.php b/actions/userbyid.php index 38bff2ede..d57ed21a5 100644 --- a/actions/userbyid.php +++ b/actions/userbyid.php @@ -19,31 +19,34 @@ if (!defined('LACONICA')) { exit(1); } -class UserbyidAction extends Action { - - function is_readonly() { - return true; - } - - function handle($args) { +class UserbyidAction extends Action +{ + + function is_readonly() + { + return true; + } + + function handle($args) + { parent::handle($args); $id = $this->trimmed('id'); if (!$id) { - $this->client_error(_('No id.')); - } - $user =& User::staticGet($id); - if (!$user) { - $this->client_error(_('No such user.')); + $this->client_error(_('No id.')); + } + $user =& User::staticGet($id); + if (!$user) { + $this->client_error(_('No such user.')); } // support redirecting to FOAF rdf/xml if the agent prefers it $page_prefs = 'application/rdf+xml,text/html,application/xhtml+xml,application/xml;q=0.3,text/xml;q=0.2'; - $httpaccept = isset($_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : NULL; - $type = common_negotiate_type(common_accept_to_prefs($httpaccept), + $httpaccept = isset($_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : null; + $type = common_negotiate_type(common_accept_to_prefs($httpaccept), common_accept_to_prefs($page_prefs)); - $page = $type == 'application/rdf+xml' ? 'foaf' : 'showstream'; + $page = $type == 'application/rdf+xml' ? 'foaf' : 'showstream'; - $url = common_local_url($page, array('nickname' => $user->nickname)); - common_redirect($url, 303); - } + $url = common_local_url($page, array('nickname' => $user->nickname)); + common_redirect($url, 303); + } } diff --git a/actions/userrss.php b/actions/userrss.php index e57f86105..1e9fe121f 100644 --- a/actions/userrss.php +++ b/actions/userrss.php @@ -23,68 +23,74 @@ require_once(INSTALLDIR.'/lib/rssaction.php'); // Formatting of RSS handled by Rss10Action -class UserrssAction extends Rss10Action { +class UserrssAction extends Rss10Action +{ - var $user = NULL; + var $user = null; - function init() { - $nickname = $this->trimmed('nickname'); - $this->user = User::staticGet('nickname', $nickname); + function init() + { + $nickname = $this->trimmed('nickname'); + $this->user = User::staticGet('nickname', $nickname); - if (!$this->user) { - common_user_error(_('No such user.')); - return false; - } else { - return true; - } - } + if (!$this->user) { + common_user_error(_('No such user.')); + return false; + } else { + return true; + } + } - function get_notices($limit=0) { + function get_notices($limit=0) + { - $user = $this->user; - - if (is_null($user)) { - return NULL; - } - - $notice = $user->getNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit); - - while ($notice->fetch()) { - $notices[] = clone($notice); - } + $user = $this->user; + + if (is_null($user)) { + return null; + } + + $notice = $user->getNotices(0, ($limit == 0) ? NOTICES_PER_PAGE : $limit); + + while ($notice->fetch()) { + $notices[] = clone($notice); + } - return $notices; - } + return $notices; + } - function get_channel() { - $user = $this->user; - $profile = $user->getProfile(); - $c = array('url' => common_local_url('userrss', - array('nickname' => - $user->nickname)), - 'title' => $user->nickname, - 'link' => $profile->profileurl, - 'description' => sprintf(_('Microblog by %s'), $user->nickname)); - return $c; - } + function get_channel() + { + $user = $this->user; + $profile = $user->getProfile(); + $c = array('url' => common_local_url('userrss', + array('nickname' => + $user->nickname)), + 'title' => $user->nickname, + 'link' => $profile->profileurl, + 'description' => sprintf(_('Microblog by %s'), $user->nickname)); + return $c; + } - function get_image() { - $user = $this->user; - $profile = $user->getProfile(); - if (!$profile) { - common_log_db_error($user, 'SELECT', __FILE__); - $this->server_error(_('User without matching profile')); - return NULL; - } - $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); - return ($avatar) ? $avatar->url : NULL; - } + function get_image() + { + $user = $this->user; + $profile = $user->getProfile(); + if (!$profile) { + common_log_db_error($user, 'SELECT', __FILE__); + $this->server_error(_('User without matching profile')); + return null; + } + $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); + return ($avatar) ? $avatar->url : null; + } - # override parent to add X-SUP-ID URL - - function init_rss($limit=0) { - $url = common_local_url('sup', NULL, $this->user->id); - header('X-SUP-ID: '.$url); - parent::init_rss($limit); - } + # override parent to add X-SUP-ID URL + + function init_rss($limit=0) + { + $url = common_local_url('sup', null, $this->user->id); + header('X-SUP-ID: '.$url); + parent::init_rss($limit); + } }
\ No newline at end of file diff --git a/actions/xrds.php b/actions/xrds.php index 1d516aab7..7edc6aa39 100644 --- a/actions/xrds.php +++ b/actions/xrds.php @@ -21,112 +21,117 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/omb.php'); -class XrdsAction extends Action { - - function is_readonly() { - return true; - } - - function handle($args) { - parent::handle($args); - $nickname = $this->trimmed('nickname'); - $user = User::staticGet('nickname', $nickname); - if (!$user) { - common_user_error(_('No such user.')); - return; - } - $this->show_xrds($user); - } - - function show_xrds($user) { - - header('Content-Type: application/xrds+xml'); - - common_start_xml(); - common_element_start('XRDS', array('xmlns' => 'xri://$xrds')); - - common_element_start('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)', - 'xml:id' => 'oauth', - 'xmlns:simple' => 'http://xrds-simple.net/core/1.0', - 'version' => '2.0')); - - common_element('Type', NULL, 'xri://$xrds*simple'); - - $this->show_service(OAUTH_ENDPOINT_REQUEST, - common_local_url('requesttoken'), - array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY), - array(OAUTH_HMAC_SHA1), - $user->uri); - - $this->show_service(OAUTH_ENDPOINT_AUTHORIZE, - common_local_url('userauthorization'), - array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY), - array(OAUTH_HMAC_SHA1)); - - $this->show_service(OAUTH_ENDPOINT_ACCESS, - common_local_url('accesstoken'), - array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY), - array(OAUTH_HMAC_SHA1)); - - $this->show_service(OAUTH_ENDPOINT_RESOURCE, - NULL, - array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY), - array(OAUTH_HMAC_SHA1)); - - common_element_end('XRD'); - - # XXX: decide whether to include user's ID/nickname in postNotice URL - - common_element_start('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)', - 'xml:id' => 'omb', - 'xmlns:simple' => 'http://xrds-simple.net/core/1.0', - 'version' => '2.0')); - - common_element('Type', NULL, 'xri://$xrds*simple'); - - $this->show_service(OMB_ENDPOINT_POSTNOTICE, - common_local_url('postnotice')); - - $this->show_service(OMB_ENDPOINT_UPDATEPROFILE, - common_local_url('updateprofile')); - - common_element_end('XRD'); - - common_element_start('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)', - 'version' => '2.0')); - - common_element('Type', NULL, 'xri://$xrds*simple'); - - $this->show_service(OAUTH_DISCOVERY, - '#oauth'); - $this->show_service(OMB_NAMESPACE, - '#omb'); - - common_element_end('XRD'); - - common_element_end('XRDS'); - common_end_xml(); - } - - function show_service($type, $uri, $params=NULL, $sigs=NULL, $localId=NULL) { - common_element_start('Service'); - if ($uri) { - common_element('URI', NULL, $uri); - } - common_element('Type', NULL, $type); - if ($params) { - foreach ($params as $param) { - common_element('Type', NULL, $param); - } - } - if ($sigs) { - foreach ($sigs as $sig) { - common_element('Type', NULL, $sig); - } - } - if ($localId) { - common_element('LocalID', NULL, $localId); - } - common_element_end('Service'); - } +class XrdsAction extends Action +{ + + function is_readonly() + { + return true; + } + + function handle($args) + { + parent::handle($args); + $nickname = $this->trimmed('nickname'); + $user = User::staticGet('nickname', $nickname); + if (!$user) { + common_user_error(_('No such user.')); + return; + } + $this->show_xrds($user); + } + + function show_xrds($user) + { + + header('Content-Type: application/xrds+xml'); + + common_start_xml(); + common_element_start('XRDS', array('xmlns' => 'xri://$xrds')); + + common_element_start('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)', + 'xml:id' => 'oauth', + 'xmlns:simple' => 'http://xrds-simple.net/core/1.0', + 'version' => '2.0')); + + common_element('Type', null, 'xri://$xrds*simple'); + + $this->show_service(OAUTH_ENDPOINT_REQUEST, + common_local_url('requesttoken'), + array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY), + array(OAUTH_HMAC_SHA1), + $user->uri); + + $this->show_service(OAUTH_ENDPOINT_AUTHORIZE, + common_local_url('userauthorization'), + array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY), + array(OAUTH_HMAC_SHA1)); + + $this->show_service(OAUTH_ENDPOINT_ACCESS, + common_local_url('accesstoken'), + array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY), + array(OAUTH_HMAC_SHA1)); + + $this->show_service(OAUTH_ENDPOINT_RESOURCE, + null, + array(OAUTH_AUTH_HEADER, OAUTH_POST_BODY), + array(OAUTH_HMAC_SHA1)); + + common_element_end('XRD'); + + # XXX: decide whether to include user's ID/nickname in postNotice URL + + common_element_start('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)', + 'xml:id' => 'omb', + 'xmlns:simple' => 'http://xrds-simple.net/core/1.0', + 'version' => '2.0')); + + common_element('Type', null, 'xri://$xrds*simple'); + + $this->show_service(OMB_ENDPOINT_POSTNOTICE, + common_local_url('postnotice')); + + $this->show_service(OMB_ENDPOINT_UPDATEPROFILE, + common_local_url('updateprofile')); + + common_element_end('XRD'); + + common_element_start('XRD', array('xmlns' => 'xri://$xrd*($v*2.0)', + 'version' => '2.0')); + + common_element('Type', null, 'xri://$xrds*simple'); + + $this->show_service(OAUTH_DISCOVERY, + '#oauth'); + $this->show_service(OMB_NAMESPACE, + '#omb'); + + common_element_end('XRD'); + + common_element_end('XRDS'); + common_end_xml(); + } + + function show_service($type, $uri, $params=null, $sigs=null, $localId=null) + { + common_element_start('Service'); + if ($uri) { + common_element('URI', null, $uri); + } + common_element('Type', null, $type); + if ($params) { + foreach ($params as $param) { + common_element('Type', null, $param); + } + } + if ($sigs) { + foreach ($sigs as $sig) { + common_element('Type', null, $sig); + } + } + if ($localId) { + common_element('LocalID', null, $localId); + } + common_element_end('Service'); + } }
\ No newline at end of file diff --git a/avatar/.gitignore b/avatar/.gitignore new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/avatar/.gitignore diff --git a/classes/Avatar.php b/classes/Avatar.php index 901c47c51..9ae920647 100644 --- a/classes/Avatar.php +++ b/classes/Avatar.php @@ -21,75 +21,121 @@ class Avatar extends Memcached_DataObject public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Avatar',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Avatar',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - # We clean up the file, too + # We clean up the file, too - function delete() { - $filename = $this->filename; - if (parent::delete()) { - @unlink(common_avatar_path($filename)); - } - } + function delete() + { + $filename = $this->filename; + if (parent::delete()) { + @unlink(common_avatar_path($filename)); + } + } - # Create and save scaled version of this avatar - # XXX: maybe break into different methods + # Create and save scaled version of this avatar + # XXX: maybe break into different methods - function scale($size) { + function scale($size) + { - $image_s = imagecreatetruecolor($size, $size); - $image_a = $this->to_image(); - $square = min($this->width, $this->height); + $image_s = imagecreatetruecolor($size, $size); + $image_a = $this->to_image(); + $square = min($this->width, $this->height); imagecolortransparent($image_s, imagecolorallocate($image_s, 0, 0, 0)); imagealphablending($image_s, false); imagesavealpha($image_s, true); - imagecopyresampled($image_s, $image_a, 0, 0, 0, 0, - $size, $size, $square, $square); - - $ext = ($this->mediattype == 'image/jpeg') ? ".jpeg" : ".png"; - - $filename = common_avatar_filename($this->profile_id, $ext, $size, common_timestamp()); - - if ($this->mediatype == 'image/jpeg') { - imagejpeg($image_s, common_avatar_path($filename)); - } else { - imagepng($image_s, common_avatar_path($filename)); - } - - $scaled = DB_DataObject::factory('avatar'); - $scaled->profile_id = $this->profile_id; - $scaled->width = $size; - $scaled->height = $size; - $scaled->original = false; - $scaled->mediatype = ($this->mediattype == 'image/jpeg') ? 'image/jpeg' : 'image/png'; - $scaled->filename = $filename; - $scaled->url = common_avatar_url($filename); - $scaled->created = DB_DataObject_Cast::dateTime(); # current time - - if ($scaled->insert()) { - return $scaled; - } else { - return NULL; - } - } - - function to_image() { - $filepath = common_avatar_path($this->filename); - if ($this->mediatype == 'image/gif') { - return imagecreatefromgif($filepath); - } else if ($this->mediatype == 'image/jpeg') { - return imagecreatefromjpeg($filepath); - } else if ($this->mediatype == 'image/png') { - return imagecreatefrompng($filepath); - } else { - return NULL; - } - } - - function &pkeyGet($kv) { - return Memcached_DataObject::pkeyGet('Avatar', $kv); - } + imagecopyresampled($image_s, $image_a, 0, 0, 0, 0, + $size, $size, $square, $square); + + $ext = ($this->mediattype == 'image/jpeg') ? ".jpeg" : ".png"; + + $filename = common_avatar_filename($this->profile_id, $ext, $size, common_timestamp()); + + if ($this->mediatype == 'image/jpeg') { + imagejpeg($image_s, common_avatar_path($filename)); + } else { + imagepng($image_s, common_avatar_path($filename)); + } + + $scaled = DB_DataObject::factory('avatar'); + $scaled->profile_id = $this->profile_id; + $scaled->width = $size; + $scaled->height = $size; + $scaled->original = false; + $scaled->mediatype = ($this->mediattype == 'image/jpeg') ? 'image/jpeg' : 'image/png'; + $scaled->filename = $filename; + $scaled->url = common_avatar_url($filename); + $scaled->created = DB_DataObject_Cast::dateTime(); # current time + + if ($scaled->insert()) { + return $scaled; + } else { + return null; + } + } + + function scale_and_crop($size, $x, $y, $w, $h) + { + + $image_s = imagecreatetruecolor($size, $size); + $image_a = $this->to_image(); + + # Retain alpha channel info if possible for .pngs + $background = imagecolorallocate($image_s, 0, 0, 0); + ImageColorTransparent($image_s, $background); + imagealphablending($image_s, false); + + imagecopyresized($image_s, $image_a, 0, 0, $x, $y, $size, $size, $w, $h); + + $ext = ($this->mediattype == 'image/jpeg') ? ".jpeg" : ".png"; + + $filename = common_avatar_filename($this->profile_id, $ext, $size, common_timestamp()); + + if ($this->mediatype == 'image/jpeg') { + imagejpeg($image_s, common_avatar_path($filename)); + } else { + imagepng($image_s, common_avatar_path($filename)); + } + + $cropped = DB_DataObject::factory('avatar'); + $cropped->profile_id = $this->profile_id; + $cropped->width = $size; + $cropped->height = $size; + $cropped->original = false; + $cropped->mediatype = ($this->mediattype == 'image/jpeg') ? 'image/jpeg' : 'image/png'; + $cropped->filename = $filename; + $cropped->url = common_avatar_url($filename); + $cropped->created = DB_DataObject_Cast::dateTime(); # current time + + if ($cropped->insert()) { + return $cropped; + } else { + return NULL; + } + } + + function to_image() + { + $filepath = common_avatar_path($this->filename); + if ($this->mediatype == 'image/gif') { + return imagecreatefromgif($filepath); + } else if ($this->mediatype == 'image/jpeg') { + return imagecreatefromjpeg($filepath); + } else if ($this->mediatype == 'image/png') { + return imagecreatefrompng($filepath); + } else { + return NULL; + } + } + + function &pkeyGet($kv) + { + return Memcached_DataObject::pkeyGet('Avatar', $kv); + } + } diff --git a/classes/Channel.php b/classes/Channel.php index bcc0c36b5..2e3e4e8d4 100644 --- a/classes/Channel.php +++ b/classes/Channel.php @@ -19,182 +19,213 @@ if (!defined('LACONICA')) { exit(1); } -class Channel { - - function on($user) { - return false; - } - - function off($user) { - return false; - } - - function output($user, $text) { - return false; - } - - function error($user, $text) { - return false; - } - - function source() { - return NULL; - } +class Channel +{ + + function on($user) + { + return false; + } + + function off($user) + { + return false; + } + + function output($user, $text) + { + return false; + } + + function error($user, $text) + { + return false; + } + + function source() + { + return null; + } } -class XMPPChannel extends Channel { - - var $conn = NULL; - - function source() { - return 'xmpp'; - } - - function __construct($conn) { - $this->conn = $conn; - } - - function on($user) { - return $this->set_notify($user, 1); - } - - function off($user) { - return $this->set_notify($user, 0); - } - - function output($user, $text) { - $text = '['.common_config('site', 'name') . '] ' . $text; - jabber_send_message($user->jabber, $text); - } - - function error($user, $text) { - $text = '['.common_config('site', 'name') . '] ' . $text; - jabber_send_message($user->jabber, $text); - } - - function set_notify(&$user, $notify) { - $orig = clone($user); - $user->jabbernotify = $notify; - $result = $user->update($orig); - if (!$result) { - $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError'); - common_log(LOG_ERR, - 'Could not set notify flag to ' . $notify . - ' for user ' . common_log_objstring($user) . - ': ' . $last_error->message); - return false; - } else { - common_log(LOG_INFO, - 'User ' . $user->nickname . ' set notify flag to ' . $notify); - return true; - } - } +class XMPPChannel extends Channel +{ + + var $conn = null; + + function source() + { + return 'xmpp'; + } + + function __construct($conn) + { + $this->conn = $conn; + } + + function on($user) + { + return $this->set_notify($user, 1); + } + + function off($user) + { + return $this->set_notify($user, 0); + } + + function output($user, $text) + { + $text = '['.common_config('site', 'name') . '] ' . $text; + jabber_send_message($user->jabber, $text); + } + + function error($user, $text) + { + $text = '['.common_config('site', 'name') . '] ' . $text; + jabber_send_message($user->jabber, $text); + } + + function set_notify(&$user, $notify) + { + $orig = clone($user); + $user->jabbernotify = $notify; + $result = $user->update($orig); + if (!$result) { + $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError'); + common_log(LOG_ERR, + 'Could not set notify flag to ' . $notify . + ' for user ' . common_log_objstring($user) . + ': ' . $last_error->message); + return false; + } else { + common_log(LOG_INFO, + 'User ' . $user->nickname . ' set notify flag to ' . $notify); + return true; + } + } } -class WebChannel extends Channel { - - function source() { - return 'web'; - } - - function on($user) { - return false; - } - - function off($user) { - return false; - } - - function output($user, $text) { - # XXX: buffer all output and send it at the end - # XXX: even better, redirect to appropriate page - # depending on what command was run - common_show_header(_('Command results')); - common_element('p', NULL, $text); - common_show_footer(); - } - - function error($user, $text) { - common_user_error($text); - } +class WebChannel extends Channel +{ + + function source() + { + return 'web'; + } + + function on($user) + { + return false; + } + + function off($user) + { + return false; + } + + function output($user, $text) + { + # XXX: buffer all output and send it at the end + # XXX: even better, redirect to appropriate page + # depending on what command was run + common_show_header(_('Command results')); + common_element('p', null, $text); + common_show_footer(); + } + + function error($user, $text) + { + common_user_error($text); + } } -class AjaxWebChannel extends WebChannel { - - function output($user, $text) { - common_start_html('text/xml;charset=utf-8', true); - common_element_start('head'); - common_element('title', null, _('Command results')); - common_element_end('head'); - common_element_start('body'); - common_element('p', array('id' => 'command_result'), $text); - common_element_end('body'); - common_element_end('html'); - } - - function error($user, $text) { - common_start_html('text/xml;charset=utf-8', true); - common_element_start('head'); - common_element('title', null, _('Ajax Error')); - common_element_end('head'); - common_element_start('body'); - common_element('p', array('id' => 'error'), $text); - common_element_end('body'); - common_element_end('html'); - } +class AjaxWebChannel extends WebChannel +{ + + function output($user, $text) + { + common_start_html('text/xml;charset=utf-8', true); + common_element_start('head'); + common_element('title', null, _('Command results')); + common_element_end('head'); + common_element_start('body'); + common_element('p', array('id' => 'command_result'), $text); + common_element_end('body'); + common_element_end('html'); + } + + function error($user, $text) + { + common_start_html('text/xml;charset=utf-8', true); + common_element_start('head'); + common_element('title', null, _('Ajax Error')); + common_element_end('head'); + common_element_start('body'); + common_element('p', array('id' => 'error'), $text); + common_element_end('body'); + common_element_end('html'); + } } -class MailChannel extends Channel { - - var $addr = NULL; - - function source() { - return 'mail'; - } - - function __construct($addr=NULL) { - $this->addr = $addr; - } - - function on($user) { - return $this->set_notify($user, 1); - } - - function off($user) { - return $this->set_notify($user, 0); - } - - function output($user, $text) { - - $headers['From'] = $user->incomingemail; - $headers['To'] = $this->addr; - - $headers['Subject'] = _('Command complete'); - - return mail_send(array($this->addr), $headers, $text); - } - - function error($user, $text) { - - $headers['From'] = $user->incomingemail; - $headers['To'] = $this->addr; - - $headers['Subject'] = _('Command failed'); - - return mail_send(array($this->addr), $headers, $text); - } - - function set_notify($user, $value) { - $orig = clone($user); - $user->smsnotify = $value; - $result = $user->update($orig); - if (!$result) { - common_log_db_error($user, 'UPDATE', __FILE__); - return false; - } - return true; - } +class MailChannel extends Channel +{ + + var $addr = null; + + function source() + { + return 'mail'; + } + + function __construct($addr=null) + { + $this->addr = $addr; + } + + function on($user) + { + return $this->set_notify($user, 1); + } + + function off($user) + { + return $this->set_notify($user, 0); + } + + function output($user, $text) + { + + $headers['From'] = $user->incomingemail; + $headers['To'] = $this->addr; + + $headers['Subject'] = _('Command complete'); + + return mail_send(array($this->addr), $headers, $text); + } + + function error($user, $text) + { + + $headers['From'] = $user->incomingemail; + $headers['To'] = $this->addr; + + $headers['Subject'] = _('Command failed'); + + return mail_send(array($this->addr), $headers, $text); + } + + function set_notify($user, $value) + { + $orig = clone($user); + $user->smsnotify = $value; + $result = $user->update($orig); + if (!$result) { + common_log_db_error($user, 'UPDATE', __FILE__); + return false; + } + return true; + } } diff --git a/classes/Command.php b/classes/Command.php index c2409d140..eacbdacb3 100644 --- a/classes/Command.php +++ b/classes/Command.php @@ -21,356 +21,399 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/classes/Channel.php'); -class Command { - - var $user = NULL; - - function __construct($user=NULL) { - $this->user = $user; - } - - function execute($channel) { - return false; - } +class Command +{ + + var $user = null; + + function __construct($user=null) + { + $this->user = $user; + } + + function execute($channel) + { + return false; + } } -class UnimplementedCommand extends Command { - function execute($channel) { - $channel->error($this->user, _("Sorry, this command is not yet implemented.")); - } +class UnimplementedCommand extends Command +{ + function execute($channel) + { + $channel->error($this->user, _("Sorry, this command is not yet implemented.")); + } } -class TrackingCommand extends UnimplementedCommand { +class TrackingCommand extends UnimplementedCommand +{ } -class TrackOffCommand extends UnimplementedCommand { +class TrackOffCommand extends UnimplementedCommand +{ } -class TrackCommand extends UnimplementedCommand { - var $word = NULL; - function __construct($user, $word) { - parent::__construct($user); - $this->word = $word; - } +class TrackCommand extends UnimplementedCommand +{ + var $word = null; + function __construct($user, $word) + { + parent::__construct($user); + $this->word = $word; + } } -class UntrackCommand extends UnimplementedCommand { - var $word = NULL; - function __construct($user, $word) { - parent::__construct($user); - $this->word = $word; - } +class UntrackCommand extends UnimplementedCommand +{ + var $word = null; + function __construct($user, $word) + { + parent::__construct($user); + $this->word = $word; + } } -class NudgeCommand extends UnimplementedCommand { - var $other = NULL; - function __construct($user, $other) { - parent::__construct($user); - $this->other = $other; - } +class NudgeCommand extends UnimplementedCommand +{ + var $other = null; + function __construct($user, $other) + { + parent::__construct($user); + $this->other = $other; + } } -class InviteCommand extends UnimplementedCommand { - var $other = NULL; - function __construct($user, $other) { - parent::__construct($user); - $this->other = $other; - } +class InviteCommand extends UnimplementedCommand +{ + var $other = null; + function __construct($user, $other) + { + parent::__construct($user); + $this->other = $other; + } } -class StatsCommand extends Command { - function execute($channel) { +class StatsCommand extends Command +{ + function execute($channel) + { - $subs = new Subscription(); - $subs->subscriber = $this->user->id; - $subs_count = (int) $subs->count() - 1; + $subs = new Subscription(); + $subs->subscriber = $this->user->id; + $subs_count = (int) $subs->count() - 1; - $subbed = new Subscription(); - $subbed->subscribed = $this->user->id; - $subbed_count = (int) $subbed->count() - 1; + $subbed = new Subscription(); + $subbed->subscribed = $this->user->id; + $subbed_count = (int) $subbed->count() - 1; - $notices = new Notice(); - $notices->profile_id = $this->user->id; - $notice_count = (int) $notices->count(); - - $channel->output($this->user, sprintf(_("Subscriptions: %1\$s\n". - "Subscribers: %2\$s\n". - "Notices: %3\$s"), - $subs_count, - $subbed_count, - $notice_count)); - } + $notices = new Notice(); + $notices->profile_id = $this->user->id; + $notice_count = (int) $notices->count(); + + $channel->output($this->user, sprintf(_("Subscriptions: %1\$s\n". + "Subscribers: %2\$s\n". + "Notices: %3\$s"), + $subs_count, + $subbed_count, + $notice_count)); + } } -class FavCommand extends Command { - - var $other = NULL; - - function __construct($user, $other) { - parent::__construct($user); - $this->other = $other; - } - - function execute($channel) { - - $recipient = - common_relative_profile($this->user, common_canonical_nickname($this->other)); - - if (!$recipient) { - $channel->error($this->user, _('No such user.')); - return; - } - $notice = $recipient->getCurrentNotice(); - if (!$notice) { - $channel->error($this->user, _('User has no last notice')); - return; - } - - $fave = Fave::addNew($this->user, $notice); +class FavCommand extends Command +{ + + var $other = null; + + function __construct($user, $other) + { + parent::__construct($user); + $this->other = $other; + } + + function execute($channel) + { + + $recipient = + common_relative_profile($this->user, common_canonical_nickname($this->other)); + + if (!$recipient) { + $channel->error($this->user, _('No such user.')); + return; + } + $notice = $recipient->getCurrentNotice(); + if (!$notice) { + $channel->error($this->user, _('User has no last notice')); + return; + } + + $fave = Fave::addNew($this->user, $notice); - if (!$fave) { - $channel->error($this->user, _('Could not create favorite.')); - return; - } + if (!$fave) { + $channel->error($this->user, _('Could not create favorite.')); + return; + } - $other = User::staticGet('id', $recipient->id); - - if ($other && $other->id != $user->id) { - if ($other->email && $other->emailnotifyfav) { - mail_notify_fave($other, $this->user, $notice); - } - } - - $this->user->blowFavesCache(); - - $channel->output($this->user, _('Notice marked as fave.')); - } + $other = User::staticGet('id', $recipient->id); + + if ($other && $other->id != $user->id) { + if ($other->email && $other->emailnotifyfav) { + mail_notify_fave($other, $this->user, $notice); + } + } + + $this->user->blowFavesCache(); + + $channel->output($this->user, _('Notice marked as fave.')); + } } -class WhoisCommand extends Command { - var $other = NULL; - function __construct($user, $other) { - parent::__construct($user); - $this->other = $other; - } - - function execute($channel) { - $recipient = - common_relative_profile($this->user, common_canonical_nickname($this->other)); - - if (!$recipient) { - $channel->error($this->user, _('No such user.')); - return; - } - - $whois = sprintf(_("%1\$s (%2\$s)"), $recipient->nickname, - $recipient->profileurl); - if ($recipient->fullname) { - $whois .= "\n" . sprintf(_('Fullname: %s'), $recipient->fullname); - } - if ($recipient->location) { - $whois .= "\n" . sprintf(_('Location: %s'), $recipient->location); - } - if ($recipient->homepage) { - $whois .= "\n" . sprintf(_('Homepage: %s'), $recipient->homepage); - } - if ($recipient->bio) { - $whois .= "\n" . sprintf(_('About: %s'), $recipient->bio); - } - $channel->output($this->user, $whois); - } +class WhoisCommand extends Command +{ + var $other = null; + function __construct($user, $other) + { + parent::__construct($user); + $this->other = $other; + } + + function execute($channel) + { + $recipient = + common_relative_profile($this->user, common_canonical_nickname($this->other)); + + if (!$recipient) { + $channel->error($this->user, _('No such user.')); + return; + } + + $whois = sprintf(_("%1\$s (%2\$s)"), $recipient->nickname, + $recipient->profileurl); + if ($recipient->fullname) { + $whois .= "\n" . sprintf(_('Fullname: %s'), $recipient->fullname); + } + if ($recipient->location) { + $whois .= "\n" . sprintf(_('Location: %s'), $recipient->location); + } + if ($recipient->homepage) { + $whois .= "\n" . sprintf(_('Homepage: %s'), $recipient->homepage); + } + if ($recipient->bio) { + $whois .= "\n" . sprintf(_('About: %s'), $recipient->bio); + } + $channel->output($this->user, $whois); + } } -class MessageCommand extends Command { - var $other = NULL; - var $text = NULL; - function __construct($user, $other, $text) { - parent::__construct($user); - $this->other = $other; - $this->text = $text; - } - - function execute($channel) { - $other = User::staticGet('nickname', common_canonical_nickname($this->other)); - $len = mb_strlen($this->text); - if ($len == 0) { - $channel->error($this->user, _('No content!')); - return; - } else if ($len > 140) { - $content = common_shorten_links($content); - if (mb_strlen($content) > 140) { - $channel->error($this->user, sprintf(_('Message too long - maximum is 140 characters, you sent %d'), $len)); - return; - } - } - - if (!$other) { - $channel->error($this->user, _('No such user.')); - return; - } else if (!$this->user->mutuallySubscribed($other)) { - $channel->error($this->user, _('You can\'t send a message to this user.')); - return; - } else if ($this->user->id == $other->id) { - $channel->error($this->user, _('Don\'t send a message to yourself; just say it to yourself quietly instead.')); - return; - } - $message = Message::saveNew($this->user->id, $other->id, $this->text, $channel->source()); - if ($message) { - $channel->output($this->user, sprintf(_('Direct message to %s sent'), $this->other)); - } else { - $channel->error($this->user, _('Error sending direct message.')); - } - } +class MessageCommand extends Command +{ + var $other = null; + var $text = null; + function __construct($user, $other, $text) + { + parent::__construct($user); + $this->other = $other; + $this->text = $text; + } + + function execute($channel) + { + $other = User::staticGet('nickname', common_canonical_nickname($this->other)); + $len = mb_strlen($this->text); + if ($len == 0) { + $channel->error($this->user, _('No content!')); + return; + } else if ($len > 140) { + $content = common_shorten_links($content); + if (mb_strlen($content) > 140) { + $channel->error($this->user, sprintf(_('Message too long - maximum is 140 characters, you sent %d'), $len)); + return; + } + } + + if (!$other) { + $channel->error($this->user, _('No such user.')); + return; + } else if (!$this->user->mutuallySubscribed($other)) { + $channel->error($this->user, _('You can\'t send a message to this user.')); + return; + } else if ($this->user->id == $other->id) { + $channel->error($this->user, _('Don\'t send a message to yourself; just say it to yourself quietly instead.')); + return; + } + $message = Message::saveNew($this->user->id, $other->id, $this->text, $channel->source()); + if ($message) { + $channel->output($this->user, sprintf(_('Direct message to %s sent'), $this->other)); + } else { + $channel->error($this->user, _('Error sending direct message.')); + } + } } -class GetCommand extends Command { - - var $other = NULL; - - function __construct($user, $other) { - parent::__construct($user); - $this->other = $other; - } - - function execute($channel) { - $target_nickname = common_canonical_nickname($this->other); - - $target = - common_relative_profile($this->user, $target_nickname); +class GetCommand extends Command +{ + + var $other = null; + + function __construct($user, $other) + { + parent::__construct($user); + $this->other = $other; + } + + function execute($channel) + { + $target_nickname = common_canonical_nickname($this->other); + + $target = + common_relative_profile($this->user, $target_nickname); - if (!$target) { - $channel->error($this->user, _('No such user.')); - return; - } - $notice = $target->getCurrentNotice(); - if (!$notice) { - $channel->error($this->user, _('User has no last notice')); - return; - } - $notice_content = $notice->content; - - $channel->output($this->user, $target_nickname . ": " . $notice_content); - } + if (!$target) { + $channel->error($this->user, _('No such user.')); + return; + } + $notice = $target->getCurrentNotice(); + if (!$notice) { + $channel->error($this->user, _('User has no last notice')); + return; + } + $notice_content = $notice->content; + + $channel->output($this->user, $target_nickname . ": " . $notice_content); + } } -class SubCommand extends Command { - - var $other = NULL; - - function __construct($user, $other) { - parent::__construct($user); - $this->other = $other; - } - - function execute($channel) { - - if (!$this->other) { - $channel->error($this->user, _('Specify the name of the user to subscribe to')); - return; - } - - $result = subs_subscribe_user($this->user, $this->other); - - if ($result == 'true') { - $channel->output($this->user, sprintf(_('Subscribed to %s'), $this->other)); - } else { - $channel->error($this->user, $result); - } - } +class SubCommand extends Command +{ + + var $other = null; + + function __construct($user, $other) + { + parent::__construct($user); + $this->other = $other; + } + + function execute($channel) + { + + if (!$this->other) { + $channel->error($this->user, _('Specify the name of the user to subscribe to')); + return; + } + + $result = subs_subscribe_user($this->user, $this->other); + + if ($result == 'true') { + $channel->output($this->user, sprintf(_('Subscribed to %s'), $this->other)); + } else { + $channel->error($this->user, $result); + } + } } -class UnsubCommand extends Command { +class UnsubCommand extends Command +{ - var $other = NULL; - - function __construct($user, $other) { - parent::__construct($user); - $this->other = $other; - } + var $other = null; + + function __construct($user, $other) + { + parent::__construct($user); + $this->other = $other; + } - function execute($channel) { - if(!$this->other) { - $channel->error($this->user, _('Specify the name of the user to unsubscribe from')); - return; - } - - $result=subs_unsubscribe_user($this->user, $this->other); - - if ($result) { - $channel->output($this->user, sprintf(_('Unsubscribed from %s'), $this->other)); - } else { - $channel->error($this->user, $result); - } - } + function execute($channel) + { + if(!$this->other) { + $channel->error($this->user, _('Specify the name of the user to unsubscribe from')); + return; + } + + $result=subs_unsubscribe_user($this->user, $this->other); + + if ($result) { + $channel->output($this->user, sprintf(_('Unsubscribed from %s'), $this->other)); + } else { + $channel->error($this->user, $result); + } + } } -class OffCommand extends Command { - var $other = NULL; - function __construct($user, $other=NULL) { - parent::__construct($user); - $this->other = $other; - } - function execute($channel) { - if ($other) { - $channel->error($this->user, _("Command not yet implemented.")); - } else { - if ($channel->off($this->user)) { - $channel->output($this->user, _('Notification off.')); - } else { - $channel->error($this->user, _('Can\'t turn off notification.')); - } - } - } +class OffCommand extends Command +{ + var $other = null; + function __construct($user, $other=null) + { + parent::__construct($user); + $this->other = $other; + } + function execute($channel) + { + if ($other) { + $channel->error($this->user, _("Command not yet implemented.")); + } else { + if ($channel->off($this->user)) { + $channel->output($this->user, _('Notification off.')); + } else { + $channel->error($this->user, _('Can\'t turn off notification.')); + } + } + } } -class OnCommand extends Command { - var $other = NULL; - function __construct($user, $other=NULL) { - parent::__construct($user); - $this->other = $other; - } - - function execute($channel) { - if ($other) { - $channel->error($this->user, _("Command not yet implemented.")); - } else { - if ($channel->on($this->user)) { - $channel->output($this->user, _('Notification on.')); - } else { - $channel->error($this->user, _('Can\'t turn on notification.')); - } - } - } +class OnCommand extends Command +{ + var $other = null; + function __construct($user, $other=null) + { + parent::__construct($user); + $this->other = $other; + } + + function execute($channel) + { + if ($other) { + $channel->error($this->user, _("Command not yet implemented.")); + } else { + if ($channel->on($this->user)) { + $channel->output($this->user, _('Notification on.')); + } else { + $channel->error($this->user, _('Can\'t turn on notification.')); + } + } + } } -class HelpCommand extends Command { - function execute($channel) { - $channel->output($this->user, - _("Commands:\n". - "on - turn on notifications\n". - "off - turn off notifications\n". - "help - show this help\n". - "follow <nickname> - subscribe to user\n". - "leave <nickname> - unsubscribe from user\n". - "d <nickname> <text> - direct message to user\n". - "get <nickname> - get last notice from user\n". - "whois <nickname> - get profile info on user\n". - "fav <nickname> - add user's last notice as a 'fave'\n". - "stats - get your stats\n". - "stop - same as 'off'\n". - "quit - same as 'off'\n". - "sub <nickname> - same as 'follow'\n". - "unsub <nickname> - same as 'leave'\n". - "last <nickname> - same as 'get'\n". - "on <nickname> - not yet implemented.\n". - "off <nickname> - not yet implemented.\n". - "nudge <nickname> - not yet implemented.\n". - "invite <phone number> - not yet implemented.\n". - "track <word> - not yet implemented.\n". - "untrack <word> - not yet implemented.\n". - "track off - not yet implemented.\n". - "untrack all - not yet implemented.\n". - "tracks - not yet implemented.\n". - "tracking - not yet implemented.\n")); - } +class HelpCommand extends Command +{ + function execute($channel) + { + $channel->output($this->user, + _("Commands:\n". + "on - turn on notifications\n". + "off - turn off notifications\n". + "help - show this help\n". + "follow <nickname> - subscribe to user\n". + "leave <nickname> - unsubscribe from user\n". + "d <nickname> <text> - direct message to user\n". + "get <nickname> - get last notice from user\n". + "whois <nickname> - get profile info on user\n". + "fav <nickname> - add user's last notice as a 'fave'\n". + "stats - get your stats\n". + "stop - same as 'off'\n". + "quit - same as 'off'\n". + "sub <nickname> - same as 'follow'\n". + "unsub <nickname> - same as 'leave'\n". + "last <nickname> - same as 'get'\n". + "on <nickname> - not yet implemented.\n". + "off <nickname> - not yet implemented.\n". + "nudge <nickname> - not yet implemented.\n". + "invite <phone number> - not yet implemented.\n". + "track <word> - not yet implemented.\n". + "untrack <word> - not yet implemented.\n". + "track off - not yet implemented.\n". + "untrack all - not yet implemented.\n". + "tracks - not yet implemented.\n". + "tracking - not yet implemented.\n")); + } } diff --git a/classes/CommandInterpreter.php b/classes/CommandInterpreter.php index eae315cb6..0679f5462 100644 --- a/classes/CommandInterpreter.php +++ b/classes/CommandInterpreter.php @@ -21,176 +21,178 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/classes/Command.php'); -class CommandInterpreter { - - function handle_command($user, $text) { - # XXX: localise +class CommandInterpreter +{ - $text = preg_replace('/\s+/', ' ', trim($text)); - list($cmd, $arg) = explode(' ', $text, 2); + function handle_command($user, $text) + { + # XXX: localise - # We try to support all the same commands as Twitter, see - # http://getsatisfaction.com/twitter/topics/what_are_the_twitter_commands - # There are a few compatibility commands from earlier versions of - # Laconica - - switch(strtolower($cmd)) { - case 'help': - if ($arg) { - return NULL; - } - return new HelpCommand($user); - case 'on': - if ($arg) { - list($other, $extra) = explode(' ', $arg, 2); - if ($extra) { - return NULL; - } else { - return new OnCommand($user, $other); - } - } else { - return new OnCommand($user); - } - case 'off': - if ($arg) { - list($other, $extra) = explode(' ', $arg, 2); - if ($extra) { - return NULL; - } else { - return new OffCommand($user, $other); - } - } else { - return new OffCommand($user); - } - case 'stop': - case 'quit': - if ($arg) { - return NULL; - } else { - return new OffCommand($user); - } - case 'follow': - case 'sub': - if (!$arg) { - return NULL; - } - list($other, $extra) = explode(' ', $arg, 2); - if ($extra) { - return NULL; - } else { - return new SubCommand($user, $other); - } - case 'leave': - case 'unsub': - if (!$arg) { - return NULL; - } - list($other, $extra) = explode(' ', $arg, 2); - if ($extra) { - return NULL; - } else { - return new UnsubCommand($user, $other); - } - case 'get': - case 'last': - if (!$arg) { - return NULL; - } - list($other, $extra) = explode(' ', $arg, 2); - if ($extra) { - return NULL; - } else { - return new GetCommand($user, $other); - } - case 'd': - case 'dm': - if (!$arg) { - return NULL; - } - list($other, $extra) = explode(' ', $arg, 2); - if (!$extra) { - return NULL; - } else { - return new MessageCommand($user, $other, $extra); - } - case 'whois': - if (!$arg) { - return NULL; - } - list($other, $extra) = explode(' ', $arg, 2); - if ($extra) { - return NULL; - } else { - return new WhoisCommand($user, $other); - } - case 'fav': - if (!$arg) { - return NULL; - } - list($other, $extra) = explode(' ', $arg, 2); - if ($extra) { - return NULL; - } else { - return new FavCommand($user, $other); - } - case 'nudge': - if (!$arg) { - return NULL; - } - list($other, $extra) = explode(' ', $arg, 2); - if ($extra) { - return NULL; - } else { - return new NudgeCommand($user, $other); - } - case 'stats': - if ($arg) { - return NULL; - } - return new StatsCommand($user); - case 'invite': - if (!$arg) { - return NULL; - } - list($other, $extra) = explode(' ', $arg, 2); - if ($extra) { - return NULL; - } else { - return new InviteCommand($user, $other); - } - case 'track': - if (!$arg) { - return NULL; - } - list($word, $extra) = explode(' ', $arg, 2); - if ($extra) { - return NULL; - } else if ($word == 'off') { - return new TrackOffCommand($user); - } else { - return new TrackCommand($user, $word); - } - case 'untrack': - if (!$arg) { - return NULL; - } - list($word, $extra) = explode(' ', $arg, 2); - if ($extra) { - return NULL; - } else if ($word == 'all') { - return new TrackOffCommand($user); - } else { - return new UntrackCommand($user, $word); - } - case 'tracks': - case 'tracking': - if ($arg) { - return NULL; - } - return new TrackingCommand($user); - default: - return false; - } - } + $text = preg_replace('/\s+/', ' ', trim($text)); + list($cmd, $arg) = explode(' ', $text, 2); + + # We try to support all the same commands as Twitter, see + # http://getsatisfaction.com/twitter/topics/what_are_the_twitter_commands + # There are a few compatibility commands from earlier versions of + # Laconica + + switch(strtolower($cmd)) { + case 'help': + if ($arg) { + return null; + } + return new HelpCommand($user); + case 'on': + if ($arg) { + list($other, $extra) = explode(' ', $arg, 2); + if ($extra) { + return null; + } else { + return new OnCommand($user, $other); + } + } else { + return new OnCommand($user); + } + case 'off': + if ($arg) { + list($other, $extra) = explode(' ', $arg, 2); + if ($extra) { + return null; + } else { + return new OffCommand($user, $other); + } + } else { + return new OffCommand($user); + } + case 'stop': + case 'quit': + if ($arg) { + return null; + } else { + return new OffCommand($user); + } + case 'follow': + case 'sub': + if (!$arg) { + return null; + } + list($other, $extra) = explode(' ', $arg, 2); + if ($extra) { + return null; + } else { + return new SubCommand($user, $other); + } + case 'leave': + case 'unsub': + if (!$arg) { + return null; + } + list($other, $extra) = explode(' ', $arg, 2); + if ($extra) { + return null; + } else { + return new UnsubCommand($user, $other); + } + case 'get': + case 'last': + if (!$arg) { + return null; + } + list($other, $extra) = explode(' ', $arg, 2); + if ($extra) { + return null; + } else { + return new GetCommand($user, $other); + } + case 'd': + case 'dm': + if (!$arg) { + return null; + } + list($other, $extra) = explode(' ', $arg, 2); + if (!$extra) { + return null; + } else { + return new MessageCommand($user, $other, $extra); + } + case 'whois': + if (!$arg) { + return null; + } + list($other, $extra) = explode(' ', $arg, 2); + if ($extra) { + return null; + } else { + return new WhoisCommand($user, $other); + } + case 'fav': + if (!$arg) { + return null; + } + list($other, $extra) = explode(' ', $arg, 2); + if ($extra) { + return null; + } else { + return new FavCommand($user, $other); + } + case 'nudge': + if (!$arg) { + return null; + } + list($other, $extra) = explode(' ', $arg, 2); + if ($extra) { + return null; + } else { + return new NudgeCommand($user, $other); + } + case 'stats': + if ($arg) { + return null; + } + return new StatsCommand($user); + case 'invite': + if (!$arg) { + return null; + } + list($other, $extra) = explode(' ', $arg, 2); + if ($extra) { + return null; + } else { + return new InviteCommand($user, $other); + } + case 'track': + if (!$arg) { + return null; + } + list($word, $extra) = explode(' ', $arg, 2); + if ($extra) { + return null; + } else if ($word == 'off') { + return new TrackOffCommand($user); + } else { + return new TrackCommand($user, $word); + } + case 'untrack': + if (!$arg) { + return null; + } + list($word, $extra) = explode(' ', $arg, 2); + if ($extra) { + return null; + } else if ($word == 'all') { + return new TrackOffCommand($user); + } else { + return new UntrackCommand($user, $word); + } + case 'tracks': + case 'tracking': + if ($arg) { + return null; + } + return new TrackingCommand($user); + default: + return false; + } + } } diff --git a/classes/Confirm_address.php b/classes/Confirm_address.php index 10661ff5c..ed3875d22 100644 --- a/classes/Confirm_address.php +++ b/classes/Confirm_address.php @@ -20,10 +20,12 @@ class Confirm_address extends Memcached_DataObject public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Confirm_address',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Confirm_address',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - function sequenceKey() { return array(false, false); } + function sequenceKey() + { return array(false, false); } } diff --git a/classes/Consumer.php b/classes/Consumer.php index d18e6feeb..d5b7b7e33 100644 --- a/classes/Consumer.php +++ b/classes/Consumer.php @@ -16,7 +16,8 @@ class Consumer extends Memcached_DataObject public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Consumer',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Consumer',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE diff --git a/classes/Fave.php b/classes/Fave.php index 7cc3f585e..24df5938c 100644 --- a/classes/Fave.php +++ b/classes/Fave.php @@ -15,23 +15,25 @@ class Fave extends Memcached_DataObject public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Fave',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Fave',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - static function addNew($user, $notice) { - $fave = new Fave(); - $fave->user_id = $user->id; - $fave->notice_id = $notice->id; - if (!$fave->insert()) { - common_log_db_error($fave, 'INSERT', __FILE__); - return false; - } - return $fave; - } - - function &pkeyGet($kv) { - return Memcached_DataObject::pkeyGet('Fave', $kv); - } + static function addNew($user, $notice) { + $fave = new Fave(); + $fave->user_id = $user->id; + $fave->notice_id = $notice->id; + if (!$fave->insert()) { + common_log_db_error($fave, 'INSERT', __FILE__); + return false; + } + return $fave; + } + + function &pkeyGet($kv) + { + return Memcached_DataObject::pkeyGet('Fave', $kv); + } } diff --git a/classes/Foreign_link.php b/classes/Foreign_link.php index 7a625a209..afc0e2180 100644 --- a/classes/Foreign_link.php +++ b/classes/Foreign_link.php @@ -4,7 +4,7 @@ */ require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; -class Foreign_link extends Memcached_DataObject +class Foreign_link extends Memcached_DataObject { ###START_AUTOCODE /* the code below is auto generated do not remove the above tag */ @@ -13,7 +13,7 @@ class Foreign_link extends Memcached_DataObject public $user_id; // int(4) primary_key not_null public $foreign_id; // int(4) primary_key not_null public $service; // int(4) primary_key not_null - public $credentials; // varchar(255) + public $credentials; // varchar(255) public $noticesync; // tinyint(1) not_null default_1 public $friendsync; // tinyint(1) not_null default_2 public $profilesync; // tinyint(1) not_null default_1 @@ -21,56 +21,84 @@ class Foreign_link extends Memcached_DataObject public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Foreign_link',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Foreign_link',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - // XXX: This only returns a 1->1 single obj mapping. Change? Or make - // a getForeignUsers() that returns more than one? --Zach - static function getByUserID($user_id, $service) { - $flink = new Foreign_link(); - $flink->service = $service; - $flink->user_id = $user_id; - $flink->limit(1); - - if ($flink->find(TRUE)) { - return $flink; - } - - return NULL; - } - - static function getByForeignID($foreign_id, $service) { - $flink = new Foreign_link(); - $flink->service = $service; - $flink->foreign_id = $foreign_id; - $flink->limit(1); - - if ($flink->find(TRUE)) { - return $flink; - } - - return NULL; - } - - # Convenience methods - function getForeignUser() { - $fuser = new Foreign_user(); - $fuser->service = $this->service; - $fuser->id = $this->foreign_id; - - $fuser->limit(1); - - if ($fuser->find(TRUE)) { - return $fuser; - } - - return NULL; - } - - function getUser() { - return User::staticGet($this->user_id); - } - + // XXX: This only returns a 1->1 single obj mapping. Change? Or make + // a getForeignUsers() that returns more than one? --Zach + static function getByUserID($user_id, $service) + { + $flink = new Foreign_link(); + $flink->service = $service; + $flink->user_id = $user_id; + $flink->limit(1); + + if ($flink->find(true)) { + return $flink; + } + + return null; + } + + static function getByForeignID($foreign_id, $service) + { + $flink = new Foreign_link(); + $flink->service = $service; + $flink->foreign_id = $foreign_id; + $flink->limit(1); + + if ($flink->find(true)) { + return $flink; + } + + return null; + } + + function set_flags($noticesync, $replysync, $friendsync) + { + if ($noticesync) { + $this->noticesync |= FOREIGN_NOTICE_SEND; + } else { + $this->noticesync &= ~FOREIGN_NOTICE_SEND; + } + + if ($replysync) { + $this->noticesync |= FOREIGN_NOTICE_SEND_REPLY; + } else { + $this->noticesync &= ~FOREIGN_NOTICE_SEND_REPLY; + } + + if ($friendsync) { + $this->friendsync |= FOREIGN_FRIEND_RECV; + } else { + $this->friendsync &= ~FOREIGN_FRIEND_RECV; + } + + $this->profilesync = 0; + } + + # Convenience methods + function getForeignUser() + { + $fuser = new Foreign_user(); + $fuser->service = $this->service; + $fuser->id = $this->foreign_id; + + $fuser->limit(1); + + if ($fuser->find(true)) { + return $fuser; + } + + return null; + } + + function getUser() + { + return User::staticGet($this->user_id); + } + } diff --git a/classes/Foreign_service.php b/classes/Foreign_service.php index 18ef83d69..ef614dbd6 100644 --- a/classes/Foreign_service.php +++ b/classes/Foreign_service.php @@ -17,7 +17,8 @@ class Foreign_service extends Memcached_DataObject public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Foreign_service',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Foreign_service',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE diff --git a/classes/Foreign_subscription.php b/classes/Foreign_subscription.php index 315064067..d50860621 100644 --- a/classes/Foreign_subscription.php +++ b/classes/Foreign_subscription.php @@ -16,7 +16,8 @@ class Foreign_subscription extends Memcached_DataObject public $created; // datetime() not_null /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Foreign_subscription',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Foreign_subscription',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE diff --git a/classes/Foreign_user.php b/classes/Foreign_user.php index 027fae69d..61727abe5 100644 --- a/classes/Foreign_user.php +++ b/classes/Foreign_user.php @@ -18,53 +18,55 @@ class Foreign_user extends Memcached_DataObject public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Foreign_user',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Foreign_user',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - - // XXX: This only returns a 1->1 single obj mapping. Change? Or make - // a getForeignUsers() that returns more than one? --Zach - static function getForeignUser($id, $service) { - $fuser = new Foreign_user(); - $fuser->whereAdd("service = $service"); - $fuser->whereAdd("id = $id"); - $fuser->limit(1); - - if ($fuser->find()) { - $fuser->fetch(); - return $fuser; - } - - return NULL; - } - - function updateKeys(&$orig) { - $parts = array(); - foreach (array('id', 'service', 'uri', 'nickname') as $k) { - if (strcmp($this->$k, $orig->$k) != 0) { - $parts[] = $k . ' = ' . $this->_quote($this->$k); - } - } - if (count($parts) == 0) { - # No changes - return true; - } - $toupdate = implode(', ', $parts); + + // XXX: This only returns a 1->1 single obj mapping. Change? Or make + // a getForeignUsers() that returns more than one? --Zach + static function getForeignUser($id, $service) { + $fuser = new Foreign_user(); + $fuser->whereAdd("service = $service"); + $fuser->whereAdd("id = $id"); + $fuser->limit(1); + + if ($fuser->find()) { + $fuser->fetch(); + return $fuser; + } + + return null; + } + + function updateKeys(&$orig) + { + $parts = array(); + foreach (array('id', 'service', 'uri', 'nickname') as $k) { + if (strcmp($this->$k, $orig->$k) != 0) { + $parts[] = $k . ' = ' . $this->_quote($this->$k); + } + } + if (count($parts) == 0) { + # No changes + return true; + } + $toupdate = implode(', ', $parts); - $table = $this->tableName(); - if(common_config('db','quote_identifiers')) { - $table = '"' . $table . '"'; - } - $qry = 'UPDATE ' . $table . ' SET ' . $toupdate . - ' WHERE id = ' . $this->id; - $orig->decache(); - $result = $this->query($qry); - if ($result) { - $this->encache(); - } - return $result; - } + $table = $this->tableName(); + if(common_config('db','quote_identifiers')) { + $table = '"' . $table . '"'; + } + $qry = 'UPDATE ' . $table . ' SET ' . $toupdate . + ' WHERE id = ' . $this->id; + $orig->decache(); + $result = $this->query($qry); + if ($result) { + $this->encache(); + } + return $result; + } - + } diff --git a/classes/Invitation.php b/classes/Invitation.php index 1477391b0..8a36fd8df 100644 --- a/classes/Invitation.php +++ b/classes/Invitation.php @@ -17,7 +17,8 @@ class Invitation extends Memcached_DataObject public $created; // datetime() not_null /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Invitation',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Invitation',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php index 7a33e158d..b9f599dbc 100644 --- a/classes/Memcached_DataObject.php +++ b/classes/Memcached_DataObject.php @@ -23,154 +23,164 @@ require_once INSTALLDIR.'/classes/Memcached_DataObject.php'; class Memcached_DataObject extends DB_DataObject { - function &staticGet($cls, $k, $v=NULL) { - if (is_null($v)) { - $v = $k; - # XXX: HACK! - $i = new $cls; - $keys = $i->keys(); - $k = $keys[0]; - unset($i); - } - $i = Memcached_DataObject::getcached($cls, $k, $v); - if ($i) { - return $i; - } else { - $i = DB_DataObject::staticGet($cls, $k, $v); - if ($i) { - $i->encache(); - } - return $i; - } - } + function &staticGet($cls, $k, $v=null) + { + if (is_null($v)) { + $v = $k; + # XXX: HACK! + $i = new $cls; + $keys = $i->keys(); + $k = $keys[0]; + unset($i); + } + $i = Memcached_DataObject::getcached($cls, $k, $v); + if ($i) { + return $i; + } else { + $i = DB_DataObject::staticGet($cls, $k, $v); + if ($i) { + $i->encache(); + } + return $i; + } + } - function &pkeyGet($cls, $kv) { - $i = Memcached_DataObject::multicache($cls, $kv); - if ($i) { - return $i; - } else { - $i = new $cls(); - foreach ($kv as $k => $v) { - $i->$k = $v; - } - if ($i->find(true)) { - $i->encache(); - } else { - $i = NULL; - } + function &pkeyGet($cls, $kv) + { + $i = Memcached_DataObject::multicache($cls, $kv); + if ($i) { + return $i; + } else { + $i = new $cls(); + foreach ($kv as $k => $v) { + $i->$k = $v; + } + if ($i->find(true)) { + $i->encache(); + } else { + $i = null; + } return $i; - } - } + } + } - function insert() { - $result = parent::insert(); - return $result; - } - - function update($orig=NULL) { - if (is_object($orig) && $orig instanceof Memcached_DataObject) { - $orig->decache(); # might be different keys - } - $result = parent::update($orig); - if ($result) { - $this->encache(); - } - return $result; - } - - function delete() { - $this->decache(); # while we still have the values! - return parent::delete(); - } - - static function memcache() { - return common_memcache(); - } - - static function cacheKey($cls, $k, $v) { - return common_cache_key(strtolower($cls).':'.$k.':'.$v); - } - - static function getcached($cls, $k, $v) { - $c = Memcached_DataObject::memcache(); - if (!$c) { - return false; - } else { - return $c->get(Memcached_DataObject::cacheKey($cls, $k, $v)); - } - } + function insert() + { + $result = parent::insert(); + return $result; + } + + function update($orig=null) + { + if (is_object($orig) && $orig instanceof Memcached_DataObject) { + $orig->decache(); # might be different keys + } + $result = parent::update($orig); + if ($result) { + $this->encache(); + } + return $result; + } + + function delete() + { + $this->decache(); # while we still have the values! + return parent::delete(); + } + + static function memcache() { + return common_memcache(); + } + + static function cacheKey($cls, $k, $v) { + return common_cache_key(strtolower($cls).':'.$k.':'.$v); + } + + static function getcached($cls, $k, $v) { + $c = Memcached_DataObject::memcache(); + if (!$c) { + return false; + } else { + return $c->get(Memcached_DataObject::cacheKey($cls, $k, $v)); + } + } - function keyTypes() { - global $_DB_DATAOBJECT; + function keyTypes() + { + global $_DB_DATAOBJECT; if (!isset($_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"])) { - $this->databaseStructure(); + $this->databaseStructure(); } - return $_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"]; - } - - function encache() { - $c = $this->memcache(); - if (!$c) { - return false; - } else { - $pkey = array(); - $pval = array(); - $types = $this->keyTypes(); - ksort($types); - foreach ($types as $key => $type) { - if ($type == 'K') { - $pkey[] = $key; - $pval[] = $this->$key; - } else { - $c->set($this->cacheKey($this->tableName(), $key, $this->$key), $this); - } - } - # XXX: should work for both compound and scalar pkeys - $pvals = implode(',', $pval); - $pkeys = implode(',', $pkey); - $c->set($this->cacheKey($this->tableName(), $pkeys, $pvals), $this); - } - } - - function decache() { - $c = $this->memcache(); - if (!$c) { - return false; - } else { - $pkey = array(); - $pval = array(); - $types = $this->keyTypes(); - ksort($types); - foreach ($types as $key => $type) { - if ($type == 'K') { - $pkey[] = $key; - $pval[] = $this->$key; - } else { - $c->delete($this->cacheKey($this->tableName(), $key, $this->$key)); - } - } - # should work for both compound and scalar pkeys - # XXX: comma works for now but may not be safe separator for future keys - $pvals = implode(',', $pval); - $pkeys = implode(',', $pkey); - $c->delete($this->cacheKey($this->tableName(), $pkeys, $pvals)); - } - } + return $_DB_DATAOBJECT['INI'][$this->_database][$this->__table."__keys"]; + } + + function encache() + { + $c = $this->memcache(); + if (!$c) { + return false; + } else { + $pkey = array(); + $pval = array(); + $types = $this->keyTypes(); + ksort($types); + foreach ($types as $key => $type) { + if ($type == 'K') { + $pkey[] = $key; + $pval[] = $this->$key; + } else { + $c->set($this->cacheKey($this->tableName(), $key, $this->$key), $this); + } + } + # XXX: should work for both compound and scalar pkeys + $pvals = implode(',', $pval); + $pkeys = implode(',', $pkey); + $c->set($this->cacheKey($this->tableName(), $pkeys, $pvals), $this); + } + } + + function decache() + { + $c = $this->memcache(); + if (!$c) { + return false; + } else { + $pkey = array(); + $pval = array(); + $types = $this->keyTypes(); + ksort($types); + foreach ($types as $key => $type) { + if ($type == 'K') { + $pkey[] = $key; + $pval[] = $this->$key; + } else { + $c->delete($this->cacheKey($this->tableName(), $key, $this->$key)); + } + } + # should work for both compound and scalar pkeys + # XXX: comma works for now but may not be safe separator for future keys + $pvals = implode(',', $pval); + $pkeys = implode(',', $pkey); + $c->delete($this->cacheKey($this->tableName(), $pkeys, $pvals)); + } + } - function multicache($cls, $kv) { - ksort($kv); - $c = Memcached_DataObject::memcache(); - if (!$c) { - return false; - } else { - $pkeys = implode(',', array_keys($kv)); - $pvals = implode(',', array_values($kv)); - return $c->get(Memcached_DataObject::cacheKey($cls, $pkeys, $pvals)); - } - } + function multicache($cls, $kv) + { + ksort($kv); + $c = Memcached_DataObject::memcache(); + if (!$c) { + return false; + } else { + $pkeys = implode(',', array_keys($kv)); + $pvals = implode(',', array_values($kv)); + return $c->get(Memcached_DataObject::cacheKey($cls, $pkeys, $pvals)); + } + } - function getSearchEngine($table) { + function getSearchEngine($table) + { require_once INSTALLDIR.'/lib/search_engines.php'; static $search_engine; if (!isset($search_engine)) { diff --git a/classes/Message.php b/classes/Message.php index ef4bd0316..4806057b4 100644 --- a/classes/Message.php +++ b/classes/Message.php @@ -22,47 +22,50 @@ class Message extends Memcached_DataObject public $source; // varchar(32) /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Message',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Message',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - - function getFrom() { - return Profile::staticGet('id', $this->from_profile); - } - - function getTo() { - return Profile::staticGet('id', $this->to_profile); - } - - static function saveNew($from, $to, $content, $source) { - - $msg = new Message(); - - $msg->from_profile = $from; - $msg->to_profile = $to; - $msg->content = common_shorten_links($content); - $msg->rendered = common_render_text($content); - $msg->created = common_sql_now(); - $msg->source = $source; - - $result = $msg->insert(); - - if (!$result) { - common_log_db_error($msg, 'INSERT', __FILE__); - return _('Could not insert message.'); - } - - $orig = clone($msg); - $msg->uri = common_local_url('showmessage', array('message' => $msg->id)); - - $result = $msg->update($orig); - - if (!$result) { - common_log_db_error($msg, 'UPDATE', __FILE__); - return _('Could not update message with new URI.'); - } - - return $msg; - } + + function getFrom() + { + return Profile::staticGet('id', $this->from_profile); + } + + function getTo() + { + return Profile::staticGet('id', $this->to_profile); + } + + static function saveNew($from, $to, $content, $source) { + + $msg = new Message(); + + $msg->from_profile = $from; + $msg->to_profile = $to; + $msg->content = common_shorten_links($content); + $msg->rendered = common_render_text($content); + $msg->created = common_sql_now(); + $msg->source = $source; + + $result = $msg->insert(); + + if (!$result) { + common_log_db_error($msg, 'INSERT', __FILE__); + return _('Could not insert message.'); + } + + $orig = clone($msg); + $msg->uri = common_local_url('showmessage', array('message' => $msg->id)); + + $result = $msg->update($orig); + + if (!$result) { + common_log_db_error($msg, 'UPDATE', __FILE__); + return _('Could not update message with new URI.'); + } + + return $msg; + } } diff --git a/classes/Nonce.php b/classes/Nonce.php index 89d673c53..2c0edfa14 100644 --- a/classes/Nonce.php +++ b/classes/Nonce.php @@ -18,7 +18,8 @@ class Nonce extends Memcached_DataObject public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Nonce',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Nonce',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE diff --git a/classes/Notice.php b/classes/Notice.php index ca8283bce..3eb653066 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -10,11 +10,11 @@ * * 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 + * 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/>. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ if (!defined('LACONICA')) { exit(1); } @@ -31,69 +31,73 @@ define('NOTICE_CACHE_WINDOW', 61); class Notice extends Memcached_DataObject { - ###START_AUTOCODE - /* the code below is auto generated do not remove the above tag */ - - public $__table = 'notice'; // table name - public $id; // int(4) primary_key not_null - public $profile_id; // int(4) not_null - public $uri; // varchar(255) unique_key - public $content; // varchar(140) - public $rendered; // text() - public $url; // varchar(255) - public $created; // datetime() not_null - public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP - public $reply_to; // int(4) - public $is_local; // tinyint(1) - public $source; // varchar(32) - - /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Notice',$k,$v); } - - /* the code above is auto generated do not remove the tag below */ - ###END_AUTOCODE - - function getProfile() { - return Profile::staticGet('id', $this->profile_id); - } - - function delete() { - $this->blowCaches(true); - $this->blowFavesCache(true); - $this->blowInboxes(); - return parent::delete(); - } - - function saveTags() { - /* extract all #hastags */ - $count = preg_match_all('/(?:^|\s)#([A-Za-z0-9_\-\.]{1,64})/', strtolower($this->content), $match); - if (!$count) { - return true; - } - - /* elide characters we don't want in the tag */ - $match[1] = str_replace(array('-', '_', '.'), '', $match[1]); - - /* Add them to the database */ - foreach(array_unique($match[1]) as $hashtag) { - $tag = DB_DataObject::factory('Notice_tag'); - $tag->notice_id = $this->id; - $tag->tag = $hashtag; - $tag->created = $this->created; - $id = $tag->insert(); - if (!$id) { - $last_error = PEAR::getStaticProperty('DB_DataObject','lastError'); - common_log(LOG_ERR, 'DB error inserting hashtag: ' . $last_error->message); - common_server_error(sprintf(_('DB error inserting hashtag: %s'), $last_error->message)); - return; - } - } - return true; - } - - static function saveNew($profile_id, $content, $source=NULL, $is_local=1, $reply_to=NULL, $uri=NULL) { - - $profile = Profile::staticGet($profile_id); + ###START_AUTOCODE + /* the code below is auto generated do not remove the above tag */ + + public $__table = 'notice'; // table name + public $id; // int(4) primary_key not_null + public $profile_id; // int(4) not_null + public $uri; // varchar(255) unique_key + public $content; // varchar(140) + public $rendered; // text() + public $url; // varchar(255) + public $created; // datetime() not_null + public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP + public $reply_to; // int(4) + public $is_local; // tinyint(1) + public $source; // varchar(32) + + /* Static get */ + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Notice',$k,$v); } + + /* the code above is auto generated do not remove the tag below */ + ###END_AUTOCODE + + function getProfile() + { + return Profile::staticGet('id', $this->profile_id); + } + + function delete() + { + $this->blowCaches(true); + $this->blowFavesCache(true); + $this->blowInboxes(); + return parent::delete(); + } + + function saveTags() + { + /* extract all #hastags */ + $count = preg_match_all('/(?:^|\s)#([A-Za-z0-9_\-\.]{1,64})/', strtolower($this->content), $match); + if (!$count) { + return true; + } + + /* elide characters we don't want in the tag */ + $match[1] = str_replace(array('-', '_', '.'), '', $match[1]); + + /* Add them to the database */ + foreach(array_unique($match[1]) as $hashtag) { + $tag = DB_DataObject::factory('Notice_tag'); + $tag->notice_id = $this->id; + $tag->tag = $hashtag; + $tag->created = $this->created; + $id = $tag->insert(); + if (!$id) { + $last_error = PEAR::getStaticProperty('DB_DataObject','lastError'); + common_log(LOG_ERR, 'DB error inserting hashtag: ' . $last_error->message); + common_server_error(sprintf(_('DB error inserting hashtag: %s'), $last_error->message)); + return; + } + } + return true; + } + + static function saveNew($profile_id, $content, $source=null, $is_local=1, $reply_to=null, $uri=null) { + + $profile = Profile::staticGet($profile_id); if (!$profile) { common_log(LOG_ERR, 'Problem saving notice. Unknown user.'); @@ -102,69 +106,69 @@ class Notice extends Memcached_DataObject if (common_config('throttle', 'enabled') && !Notice::checkEditThrottle($profile_id)) { common_log(LOG_WARNING, 'Excessive posting by profile #' . $profile_id . '; throttled.'); - return _('Too many notices too fast; take a breather and post again in a few minutes.'); + return _('Too many notices too fast; take a breather and post again in a few minutes.'); } - $banned = common_config('profile', 'banned'); + $banned = common_config('profile', 'banned'); - if ( in_array($profile_id, $banned) || in_array($profile->nickname, $banned)) { - common_log(LOG_WARNING, "Attempted post from banned user: $profile->nickname (user id = $profile_id)."); + if ( in_array($profile_id, $banned) || in_array($profile->nickname, $banned)) { + common_log(LOG_WARNING, "Attempted post from banned user: $profile->nickname (user id = $profile_id)."); return _('You are banned from posting notices on this site.'); - } + } - $notice = new Notice(); - $notice->profile_id = $profile_id; + $notice = new Notice(); + $notice->profile_id = $profile_id; - $blacklist = common_config('public', 'blacklist'); + $blacklist = common_config('public', 'blacklist'); - # Blacklisted are non-false, but not 1, either + # Blacklisted are non-false, but not 1, either - if ($blacklist && in_array($profile_id, $blacklist)) { - $notice->is_local = -1; - } else { - $notice->is_local = $is_local; - } + if ($blacklist && in_array($profile_id, $blacklist)) { + $notice->is_local = -1; + } else { + $notice->is_local = $is_local; + } - $notice->reply_to = $reply_to; - $notice->created = common_sql_now(); - $notice->content = common_shorten_links($content); - $notice->rendered = common_render_content($notice->content, $notice); - $notice->source = $source; - $notice->uri = $uri; + $notice->reply_to = $reply_to; + $notice->created = common_sql_now(); + $notice->content = common_shorten_links($content); + $notice->rendered = common_render_content($notice->content, $notice); + $notice->source = $source; + $notice->uri = $uri; - $id = $notice->insert(); + $id = $notice->insert(); - if (!$id) { - common_log_db_error($notice, 'INSERT', __FILE__); - return _('Problem saving notice.'); - } + if (!$id) { + common_log_db_error($notice, 'INSERT', __FILE__); + return _('Problem saving notice.'); + } - # Update the URI after the notice is in the database - if (!$uri) { - $orig = clone($notice); - $notice->uri = common_notice_uri($notice); + # Update the URI after the notice is in the database + if (!$uri) { + $orig = clone($notice); + $notice->uri = common_notice_uri($notice); - if (!$notice->update($orig)) { - common_log_db_error($notice, 'UPDATE', __FILE__); - return _('Problem saving notice.'); - } - } + if (!$notice->update($orig)) { + common_log_db_error($notice, 'UPDATE', __FILE__); + return _('Problem saving notice.'); + } + } - # XXX: do we need to change this for remote users? + # XXX: do we need to change this for remote users? - common_save_replies($notice); - $notice->saveTags(); + common_save_replies($notice); + $notice->saveTags(); - # Clear the cache for subscribed users, so they'll update at next request - # XXX: someone clever could prepend instead of clearing the cache + # Clear the cache for subscribed users, so they'll update at next request + # XXX: someone clever could prepend instead of clearing the cache - if (common_config('memcached', 'enabled')) { - $notice->blowCaches(); - } + if (common_config('memcached', 'enabled')) { + $notice->blowCaches(); + } - $notice->addToInboxes(); - return $notice; - } + $notice->addToInboxes(); + return $notice; + } static function checkEditThrottle($profile_id) { $profile = Profile::staticGet($profile_id); @@ -184,356 +188,366 @@ class Notice extends Memcached_DataObject return true; } - function blowCaches($blowLast=false) { - $this->blowSubsCache($blowLast); - $this->blowNoticeCache($blowLast); - $this->blowRepliesCache($blowLast); - $this->blowPublicCache($blowLast); - $this->blowTagCache($blowLast); - } - - function blowTagCache($blowLast=false) { - $cache = common_memcache(); - if ($cache) { - $tag = new Notice_tag(); - $tag->notice_id = $this->id; - if ($tag->find()) { - while ($tag->fetch()) { - $cache->delete(common_cache_key('notice_tag:notice_stream:' . $tag->tag)); - if ($blowLast) { - $cache->delete(common_cache_key('notice_tag:notice_stream:' . $tag->tag . ';last')); - } - } - } - $tag->free(); - unset($tag); - } - } - - function blowSubsCache($blowLast=false) { - $cache = common_memcache(); - if ($cache) { - $user = new User(); - - $user->query('SELECT id ' . - 'FROM user JOIN subscription ON user.id = subscription.subscriber ' . - 'WHERE subscription.subscribed = ' . $this->profile_id); - - while ($user->fetch()) { - $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id)); - if ($blowLast) { - $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id . ';last')); - } - } - $user->free(); - unset($user); - } - } - - function blowNoticeCache($blowLast=false) { - if ($this->is_local) { - $cache = common_memcache(); - if ($cache) { - $cache->delete(common_cache_key('profile:notices:'.$this->profile_id)); - if ($blowLast) { - $cache->delete(common_cache_key('profile:notices:'.$this->profile_id.';last')); - } - } - } - } - - function blowRepliesCache($blowLast=false) { - $cache = common_memcache(); - if ($cache) { - $reply = new Reply(); - $reply->notice_id = $this->id; - if ($reply->find()) { - while ($reply->fetch()) { - $cache->delete(common_cache_key('user:replies:'.$reply->profile_id)); - if ($blowLast) { - $cache->delete(common_cache_key('user:replies:'.$reply->profile_id.';last')); - } - } - } - $reply->free(); - unset($reply); - } - } - - function blowPublicCache($blowLast=false) { - if ($this->is_local == 1) { - $cache = common_memcache(); - if ($cache) { - $cache->delete(common_cache_key('public')); - if ($blowLast) { - $cache->delete(common_cache_key('public').';last'); - } - } - } - } - - function blowFavesCache($blowLast=false) { - $cache = common_memcache(); - if ($cache) { - $fave = new Fave(); - $fave->notice_id = $this->id; - if ($fave->find()) { - while ($fave->fetch()) { - $cache->delete(common_cache_key('user:faves:'.$fave->user_id)); - if ($blowLast) { - $cache->delete(common_cache_key('user:faves:'.$fave->user_id.';last')); - } - } - } - $fave->free(); - unset($fave); - } - } - - # XXX: too many args; we need to move to named params or even a separate - # class for notice streams - - static function getStream($qry, $cachekey, $offset=0, $limit=20, $since_id=0, $before_id=0, $order=NULL, $since=NULL) { - - if (common_config('memcached', 'enabled')) { - - # Skip the cache if this is a since, since_id or before_id qry - if ($since_id > 0 || $before_id > 0 || $since) { - return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order, $since); - } else { - return Notice::getCachedStream($qry, $cachekey, $offset, $limit, $order); - } - } - - return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order, $since); - } - - static function getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order, $since) { - - $needAnd = FALSE; - $needWhere = TRUE; - - if (preg_match('/\bWHERE\b/i', $qry)) { - $needWhere = FALSE; - $needAnd = TRUE; - } - - if ($since_id > 0) { - - if ($needWhere) { - $qry .= ' WHERE '; - $needWhere = FALSE; - } else { - $qry .= ' AND '; - } - - $qry .= ' notice.id > ' . $since_id; - } - - if ($before_id > 0) { - - if ($needWhere) { - $qry .= ' WHERE '; - $needWhere = FALSE; - } else { - $qry .= ' AND '; - } - - $qry .= ' notice.id < ' . $before_id; - } + function blowCaches($blowLast=false) + { + $this->blowSubsCache($blowLast); + $this->blowNoticeCache($blowLast); + $this->blowRepliesCache($blowLast); + $this->blowPublicCache($blowLast); + $this->blowTagCache($blowLast); + } + + function blowTagCache($blowLast=false) + { + $cache = common_memcache(); + if ($cache) { + $tag = new Notice_tag(); + $tag->notice_id = $this->id; + if ($tag->find()) { + while ($tag->fetch()) { + $cache->delete(common_cache_key('notice_tag:notice_stream:' . $tag->tag)); + if ($blowLast) { + $cache->delete(common_cache_key('notice_tag:notice_stream:' . $tag->tag . ';last')); + } + } + } + $tag->free(); + unset($tag); + } + } + + function blowSubsCache($blowLast=false) + { + $cache = common_memcache(); + if ($cache) { + $user = new User(); + + $user->query('SELECT id ' . + 'FROM user JOIN subscription ON user.id = subscription.subscriber ' . + 'WHERE subscription.subscribed = ' . $this->profile_id); + + while ($user->fetch()) { + $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id)); + if ($blowLast) { + $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id . ';last')); + } + } + $user->free(); + unset($user); + } + } + + function blowNoticeCache($blowLast=false) + { + if ($this->is_local) { + $cache = common_memcache(); + if ($cache) { + $cache->delete(common_cache_key('profile:notices:'.$this->profile_id)); + if ($blowLast) { + $cache->delete(common_cache_key('profile:notices:'.$this->profile_id.';last')); + } + } + } + } + + function blowRepliesCache($blowLast=false) + { + $cache = common_memcache(); + if ($cache) { + $reply = new Reply(); + $reply->notice_id = $this->id; + if ($reply->find()) { + while ($reply->fetch()) { + $cache->delete(common_cache_key('user:replies:'.$reply->profile_id)); + if ($blowLast) { + $cache->delete(common_cache_key('user:replies:'.$reply->profile_id.';last')); + } + } + } + $reply->free(); + unset($reply); + } + } + + function blowPublicCache($blowLast=false) + { + if ($this->is_local == 1) { + $cache = common_memcache(); + if ($cache) { + $cache->delete(common_cache_key('public')); + if ($blowLast) { + $cache->delete(common_cache_key('public').';last'); + } + } + } + } + + function blowFavesCache($blowLast=false) + { + $cache = common_memcache(); + if ($cache) { + $fave = new Fave(); + $fave->notice_id = $this->id; + if ($fave->find()) { + while ($fave->fetch()) { + $cache->delete(common_cache_key('user:faves:'.$fave->user_id)); + if ($blowLast) { + $cache->delete(common_cache_key('user:faves:'.$fave->user_id.';last')); + } + } + } + $fave->free(); + unset($fave); + } + } + + # XXX: too many args; we need to move to named params or even a separate + # class for notice streams + + static function getStream($qry, $cachekey, $offset=0, $limit=20, $since_id=0, $before_id=0, $order=null, $since=null) { + + if (common_config('memcached', 'enabled')) { + + # Skip the cache if this is a since, since_id or before_id qry + if ($since_id > 0 || $before_id > 0 || $since) { + return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order, $since); + } else { + return Notice::getCachedStream($qry, $cachekey, $offset, $limit, $order); + } + } + + return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order, $since); + } + + static function getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order, $since) { + + $needAnd = false; + $needWhere = true; + + if (preg_match('/\bWHERE\b/i', $qry)) { + $needWhere = false; + $needAnd = true; + } + + if ($since_id > 0) { + + if ($needWhere) { + $qry .= ' WHERE '; + $needWhere = false; + } else { + $qry .= ' AND '; + } + + $qry .= ' notice.id > ' . $since_id; + } + + if ($before_id > 0) { + + if ($needWhere) { + $qry .= ' WHERE '; + $needWhere = false; + } else { + $qry .= ' AND '; + } + + $qry .= ' notice.id < ' . $before_id; + } - if ($since) { - - if ($needWhere) { - $qry .= ' WHERE '; - $needWhere = FALSE; - } else { - $qry .= ' AND '; - } + if ($since) { - $qry .= ' notice.created > \'' . date('Y-m-d H:i:s', $since) . '\''; - } + if ($needWhere) { + $qry .= ' WHERE '; + $needWhere = false; + } else { + $qry .= ' AND '; + } - # Allow ORDER override + $qry .= ' notice.created > \'' . date('Y-m-d H:i:s', $since) . '\''; + } - if ($order) { - $qry .= $order; - } else { - $qry .= ' ORDER BY notice.created DESC, notice.id DESC '; - } + # Allow ORDER override - if (common_config('db','type') == 'pgsql') { - $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; - } else { - $qry .= ' LIMIT ' . $offset . ', ' . $limit; - } + if ($order) { + $qry .= $order; + } else { + $qry .= ' ORDER BY notice.created DESC, notice.id DESC '; + } - $notice = new Notice(); + if (common_config('db','type') == 'pgsql') { + $qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $qry .= ' LIMIT ' . $offset . ', ' . $limit; + } - $notice->query($qry); + $notice = new Notice(); - return $notice; - } + $notice->query($qry); - # XXX: this is pretty long and should probably be broken up into - # some helper functions + return $notice; + } - static function getCachedStream($qry, $cachekey, $offset, $limit, $order) { + # XXX: this is pretty long and should probably be broken up into + # some helper functions - # If outside our cache window, just go to the DB + static function getCachedStream($qry, $cachekey, $offset, $limit, $order) { - if ($offset + $limit > NOTICE_CACHE_WINDOW) { - return Notice::getStreamDirect($qry, $offset, $limit, NULL, NULL, $order, NULL); - } + # If outside our cache window, just go to the DB - # Get the cache; if we can't, just go to the DB + if ($offset + $limit > NOTICE_CACHE_WINDOW) { + return Notice::getStreamDirect($qry, $offset, $limit, null, null, $order, null); + } - $cache = common_memcache(); + # Get the cache; if we can't, just go to the DB - if (!$cache) { - return Notice::getStreamDirect($qry, $offset, $limit, NULL, NULL, $order, NULL); - } + $cache = common_memcache(); - # Get the notices out of the cache + if (!$cache) { + return Notice::getStreamDirect($qry, $offset, $limit, null, null, $order, null); + } - $notices = $cache->get(common_cache_key($cachekey)); + # Get the notices out of the cache - # On a cache hit, return a DB-object-like wrapper + $notices = $cache->get(common_cache_key($cachekey)); - if ($notices !== FALSE) { - $wrapper = new NoticeWrapper(array_slice($notices, $offset, $limit)); - return $wrapper; - } + # On a cache hit, return a DB-object-like wrapper - # If the cache was invalidated because of new data being - # added, we can try and just get the new stuff. We keep an additional - # copy of the data at the key + ';last' + if ($notices !== false) { + $wrapper = new NoticeWrapper(array_slice($notices, $offset, $limit)); + return $wrapper; + } - # No cache hit. Try to get the *last* cached version + # If the cache was invalidated because of new data being + # added, we can try and just get the new stuff. We keep an additional + # copy of the data at the key + ';last' - $last_notices = $cache->get(common_cache_key($cachekey) . ';last'); + # No cache hit. Try to get the *last* cached version - if ($last_notices) { + $last_notices = $cache->get(common_cache_key($cachekey) . ';last'); - # Reverse-chron order, so last ID is last. + if ($last_notices) { - $last_id = $last_notices[0]->id; + # Reverse-chron order, so last ID is last. - # XXX: this assumes monotonically increasing IDs; a fair - # bet with our DB. + $last_id = $last_notices[0]->id; - $new_notice = Notice::getStreamDirect($qry, 0, NOTICE_CACHE_WINDOW, - $last_id, NULL, $order, NULL); + # XXX: this assumes monotonically increasing IDs; a fair + # bet with our DB. - if ($new_notice) { - $new_notices = array(); - while ($new_notice->fetch()) { - $new_notices[] = clone($new_notice); - } - $new_notice->free(); - $notices = array_slice(array_merge($new_notices, $last_notices), - 0, NOTICE_CACHE_WINDOW); + $new_notice = Notice::getStreamDirect($qry, 0, NOTICE_CACHE_WINDOW, + $last_id, null, $order, null); - # Store the array in the cache for next time + if ($new_notice) { + $new_notices = array(); + while ($new_notice->fetch()) { + $new_notices[] = clone($new_notice); + } + $new_notice->free(); + $notices = array_slice(array_merge($new_notices, $last_notices), + 0, NOTICE_CACHE_WINDOW); - $result = $cache->set(common_cache_key($cachekey), $notices); - $result = $cache->set(common_cache_key($cachekey) . ';last', $notices); + # Store the array in the cache for next time - # return a wrapper of the array for use now + $result = $cache->set(common_cache_key($cachekey), $notices); + $result = $cache->set(common_cache_key($cachekey) . ';last', $notices); - return new NoticeWrapper(array_slice($notices, $offset, $limit)); - } - } + # return a wrapper of the array for use now - # Otherwise, get the full cache window out of the DB + return new NoticeWrapper(array_slice($notices, $offset, $limit)); + } + } - $notice = Notice::getStreamDirect($qry, 0, NOTICE_CACHE_WINDOW, NULL, NULL, $order, NULL); + # Otherwise, get the full cache window out of the DB - # If there are no hits, just return the value + $notice = Notice::getStreamDirect($qry, 0, NOTICE_CACHE_WINDOW, null, null, $order, null); - if (!$notice) { - return $notice; - } + # If there are no hits, just return the value - # Pack results into an array + if (!$notice) { + return $notice; + } - $notices = array(); + # Pack results into an array - while ($notice->fetch()) { - $notices[] = clone($notice); - } + $notices = array(); - $notice->free(); + while ($notice->fetch()) { + $notices[] = clone($notice); + } - # Store the array in the cache for next time + $notice->free(); - $result = $cache->set(common_cache_key($cachekey), $notices); - $result = $cache->set(common_cache_key($cachekey) . ';last', $notices); + # Store the array in the cache for next time - # return a wrapper of the array for use now + $result = $cache->set(common_cache_key($cachekey), $notices); + $result = $cache->set(common_cache_key($cachekey) . ';last', $notices); - $wrapper = new NoticeWrapper(array_slice($notices, $offset, $limit)); + # return a wrapper of the array for use now - return $wrapper; - } + $wrapper = new NoticeWrapper(array_slice($notices, $offset, $limit)); - function publicStream($offset=0, $limit=20, $since_id=0, $before_id=0, $since=NULL) { + return $wrapper; + } - $parts = array(); + function publicStream($offset=0, $limit=20, $since_id=0, $before_id=0, $since=null) + { - $qry = 'SELECT * FROM notice '; + $parts = array(); - if (common_config('public', 'localonly')) { - $parts[] = 'is_local = 1'; - } else { - # -1 == blacklisted - $parts[] = 'is_local != -1'; - } + $qry = 'SELECT * FROM notice '; - if ($parts) { - $qry .= ' WHERE ' . implode(' AND ', $parts); - } + if (common_config('public', 'localonly')) { + $parts[] = 'is_local = 1'; + } else { + # -1 == blacklisted + $parts[] = 'is_local != -1'; + } - return Notice::getStream($qry, - 'public', - $offset, $limit, $since_id, $before_id, NULL, $since); - } + if ($parts) { + $qry .= ' WHERE ' . implode(' AND ', $parts); + } - function addToInboxes() { - $enabled = common_config('inboxes', 'enabled'); + return Notice::getStream($qry, + 'public', + $offset, $limit, $since_id, $before_id, null, $since); + } - if ($enabled === true || $enabled === 'transitional') { - $inbox = new Notice_inbox(); - $qry = 'INSERT INTO notice_inbox (user_id, notice_id, created) ' . - 'SELECT user.id, ' . $this->id . ', "' . $this->created . '" ' . - 'FROM user JOIN subscription ON user.id = subscription.subscriber ' . - 'WHERE subscription.subscribed = ' . $this->profile_id . ' ' . - 'AND NOT EXISTS (SELECT user_id, notice_id ' . - 'FROM notice_inbox ' . - 'WHERE user_id = user.id ' . - 'AND notice_id = ' . $this->id . ' )'; - if ($enabled === 'transitional') { - $qry .= ' AND user.inboxed = 1'; - } - $inbox->query($qry); - } - return; - } + function addToInboxes() + { + $enabled = common_config('inboxes', 'enabled'); + + if ($enabled === true || $enabled === 'transitional') { + $inbox = new Notice_inbox(); + $qry = 'INSERT INTO notice_inbox (user_id, notice_id, created) ' . + 'SELECT user.id, ' . $this->id . ', "' . $this->created . '" ' . + 'FROM user JOIN subscription ON user.id = subscription.subscriber ' . + 'WHERE subscription.subscribed = ' . $this->profile_id . ' ' . + 'AND NOT EXISTS (SELECT user_id, notice_id ' . + 'FROM notice_inbox ' . + 'WHERE user_id = user.id ' . + 'AND notice_id = ' . $this->id . ' )'; + if ($enabled === 'transitional') { + $qry .= ' AND user.inboxed = 1'; + } + $inbox->query($qry); + } + return; + } - # Delete from inboxes if we're deleted. + # Delete from inboxes if we're deleted. - function blowInboxes() { + function blowInboxes() + { - $enabled = common_config('inboxes', 'enabled'); + $enabled = common_config('inboxes', 'enabled'); - if ($enabled === true || $enabled === 'transitional') { - $inbox = new Notice_inbox(); - $inbox->notice_id = $this->id; - $inbox->delete(); - } + if ($enabled === true || $enabled === 'transitional') { + $inbox = new Notice_inbox(); + $inbox->notice_id = $this->id; + $inbox->delete(); + } - return; - } + return; + } } diff --git a/classes/NoticeWrapper.php b/classes/NoticeWrapper.php index f8c0aa381..233340ccd 100644 --- a/classes/NoticeWrapper.php +++ b/classes/NoticeWrapper.php @@ -21,7 +21,8 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/classes/Notice.php'); -class NoticeWrapper extends Notice { +class NoticeWrapper extends Notice +{ public $id; // int(4) primary_key not_null public $profile_id; // int(4) not_null @@ -35,25 +36,27 @@ class NoticeWrapper extends Notice { public $is_local; // tinyint(1) public $source; // varchar(32) - var $notices = NULL; - var $i = -1; - - function __construct($arr) { - $this->notices = $arr; - } - - function fetch() { - static $fields = array('id', 'profile_id', 'uri', 'content', 'rendered', - 'url', 'created', 'modified', 'reply_to', 'is_local', 'source'); - $this->i++; - if ($this->i >= count($this->notices)) { - return false; - } else { - $n = $this->notices[$this->i]; - foreach ($fields as $f) { - $this->$f = $n->$f; - } - return true; - } - } + var $notices = null; + var $i = -1; + + function __construct($arr) + { + $this->notices = $arr; + } + + function fetch() + { + static $fields = array('id', 'profile_id', 'uri', 'content', 'rendered', + 'url', 'created', 'modified', 'reply_to', 'is_local', 'source'); + $this->i++; + if ($this->i >= count($this->notices)) { + return false; + } else { + $n = $this->notices[$this->i]; + foreach ($fields as $f) { + $this->$f = $n->$f; + } + return true; + } + } }
\ No newline at end of file diff --git a/classes/Notice_inbox.php b/classes/Notice_inbox.php index cc482bd19..81ddb4538 100644 --- a/classes/Notice_inbox.php +++ b/classes/Notice_inbox.php @@ -33,7 +33,8 @@ class Notice_inbox extends Memcached_DataObject public $source; // tinyint(1) default_1 /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Notice_inbox',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Notice_inbox',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE diff --git a/classes/Notice_source.php b/classes/Notice_source.php index e0a41b927..e7568bbca 100644 --- a/classes/Notice_source.php +++ b/classes/Notice_source.php @@ -17,7 +17,8 @@ class Notice_source extends Memcached_DataObject public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Notice_source',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Notice_source',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE diff --git a/classes/Notice_tag.php b/classes/Notice_tag.php index 5b75ff13f..94f9296d6 100644 --- a/classes/Notice_tag.php +++ b/classes/Notice_tag.php @@ -30,26 +30,28 @@ class Notice_tag extends Memcached_DataObject public $created; // datetime() not_null /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Notice_tag',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Notice_tag',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - - static function getStream($tag, $offset=0, $limit=20) { - $qry = - 'SELECT notice.* ' . - 'FROM notice JOIN notice_tag ON notice.id = notice_tag.notice_id ' . - 'WHERE notice_tag.tag = "%s" '; + + static function getStream($tag, $offset=0, $limit=20) { + $qry = + 'SELECT notice.* ' . + 'FROM notice JOIN notice_tag ON notice.id = notice_tag.notice_id ' . + 'WHERE notice_tag.tag = "%s" '; - return Notice::getStream(sprintf($qry, $tag), - 'notice_tag:notice_stream:' . common_keyize($tag), - $offset, $limit); - } - - function blowCache() { - $cache = common_memcache(); - if ($cache) { - $cache->delete(common_cache_key('notice_tag:notice_stream:' . $this->tag)); - } - } + return Notice::getStream(sprintf($qry, $tag), + 'notice_tag:notice_stream:' . common_keyize($tag), + $offset, $limit); + } + + function blowCache() + { + $cache = common_memcache(); + if ($cache) { + $cache->delete(common_cache_key('notice_tag:notice_stream:' . $this->tag)); + } + } } diff --git a/classes/Profile.php b/classes/Profile.php index b57d7e38d..31bdf71d5 100644 --- a/classes/Profile.php +++ b/classes/Profile.php @@ -41,119 +41,150 @@ class Profile extends Memcached_DataObject public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Profile',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Profile',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - function getAvatar($width, $height=NULL) { - if (is_null($height)) { - $height = $width; - } - return Avatar::pkeyGet(array('profile_id' => $this->id, - 'width' => $width, - 'height' => $height)); - } - - function getOriginalAvatar() { - $avatar = DB_DataObject::factory('avatar'); - $avatar->profile_id = $this->id; - $avatar->original = true; - if ($avatar->find(true)) { - return $avatar; - } else { - return NULL; - } - } - - function setOriginal($source) { - - $info = @getimagesize($source); - - if (!$info) { - return NULL; - } - - $filename = common_avatar_filename($this->id, - image_type_to_extension($info[2]), - NULL, common_timestamp()); - $filepath = common_avatar_path($filename); - - copy($source, $filepath); - - $avatar = new Avatar(); - - $avatar->profile_id = $this->id; - $avatar->width = $info[0]; - $avatar->height = $info[1]; - $avatar->mediatype = image_type_to_mime_type($info[2]); - $avatar->filename = $filename; - $avatar->original = true; - $avatar->url = common_avatar_url($filename); - $avatar->created = DB_DataObject_Cast::dateTime(); # current time - - # XXX: start a transaction here - - if (!$this->delete_avatars()) { - @unlink($filepath); - return NULL; - } - - if (!$avatar->insert()) { - @unlink($filepath); - return NULL; - } - - foreach (array(AVATAR_PROFILE_SIZE, AVATAR_STREAM_SIZE, AVATAR_MINI_SIZE) as $size) { - # We don't do a scaled one if original is our scaled size - if (!($avatar->width == $size && $avatar->height == $size)) { - $s = $avatar->scale($size); - if (!$s) { - return NULL; - } - } - } - - return $avatar; - } - - function delete_avatars() { - $avatar = new Avatar(); - $avatar->profile_id = $this->id; - $avatar->find(); - while ($avatar->fetch()) { - $avatar->delete(); - } - return true; - } - - function getBestName() { - return ($this->fullname) ? $this->fullname : $this->nickname; - } + function getAvatar($width, $height=null) + { + if (is_null($height)) { + $height = $width; + } + return Avatar::pkeyGet(array('profile_id' => $this->id, + 'width' => $width, + 'height' => $height)); + } + + function getOriginalAvatar() + { + $avatar = DB_DataObject::factory('avatar'); + $avatar->profile_id = $this->id; + $avatar->original = true; + if ($avatar->find(true)) { + return $avatar; + } else { + return null; + } + } + + function setOriginal($source) + { + + $info = @getimagesize($source); + + if (!$info) { + return null; + } + + $filename = common_avatar_filename($this->id, + image_type_to_extension($info[2]), + null, common_timestamp()); + $filepath = common_avatar_path($filename); + + copy($source, $filepath); + + $avatar = new Avatar(); + + $avatar->profile_id = $this->id; + $avatar->width = $info[0]; + $avatar->height = $info[1]; + $avatar->mediatype = image_type_to_mime_type($info[2]); + $avatar->filename = $filename; + $avatar->original = true; + $avatar->url = common_avatar_url($filename); + $avatar->created = DB_DataObject_Cast::dateTime(); # current time + + # XXX: start a transaction here + + if (!$this->delete_avatars()) { + @unlink($filepath); + return null; + } + + if (!$avatar->insert()) { + @unlink($filepath); + return null; + } + + foreach (array(AVATAR_PROFILE_SIZE, AVATAR_STREAM_SIZE, AVATAR_MINI_SIZE) as $size) { + # We don't do a scaled one if original is our scaled size + if (!($avatar->width == $size && $avatar->height == $size)) { + $s = $avatar->scale($size); + if (!$s) { + return null; + } + } + } + + return $avatar; + } + + function crop_avatars($x, $y, $w, $h) + { + + $avatar = $this->getOriginalAvatar(); + $this->delete_avatars(false); # don't delete original + + foreach (array(AVATAR_PROFILE_SIZE, AVATAR_STREAM_SIZE, AVATAR_MINI_SIZE) as $size) { + # We don't do a scaled one if original is our scaled size + if (!($avatar->width == $size && $avatar->height == $size)) { + $s = $avatar->scale_and_crop($size, $x, $y, $w, $h); + if (!$s) { + return NULL; + } + } + } + return true; + } + + function delete_avatars($original=true) + { + $avatar = new Avatar(); + $avatar->profile_id = $this->id; + $avatar->find(); + while ($avatar->fetch()) { + if ($avatar->original) { + if ($original == false) { + continue; + } + } + $avatar->delete(); + } + return true; + } + + function getBestName() + { + return ($this->fullname) ? $this->fullname : $this->nickname; + } # Get latest notice on or before date; default now - function getCurrentNotice($dt=NULL) { - $notice = new Notice(); - $notice->profile_id = $this->id; - if ($dt) { - $notice->whereAdd('created < "' . $dt . '"'); - } - $notice->orderBy('created DESC, notice.id DESC'); - $notice->limit(1); - if ($notice->find(true)) { - return $notice; - } - return NULL; - } - - function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) { - $qry = - 'SELECT * ' . - 'FROM notice ' . - 'WHERE profile_id = %d '; - - return Notice::getStream(sprintf($qry, $this->id), - 'profile:notices:'.$this->id, - $offset, $limit, $since_id, $before_id); - } + function getCurrentNotice($dt=null) + { + $notice = new Notice(); + $notice->profile_id = $this->id; + if ($dt) { + $notice->whereAdd('created < "' . $dt . '"'); + } + $notice->orderBy('created DESC, notice.id DESC'); + $notice->limit(1); + if ($notice->find(true)) { + return $notice; + } + return null; + } + + function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0) + { + $qry = + 'SELECT * ' . + 'FROM notice ' . + 'WHERE profile_id = %d '; + + return Notice::getStream(sprintf($qry, $this->id), + 'profile:notices:'.$this->id, + $offset, $limit, $since_id, $before_id); + } } diff --git a/classes/Profile_block.php b/classes/Profile_block.php index 6ea26a3bc..551e690e2 100644 --- a/classes/Profile_block.php +++ b/classes/Profile_block.php @@ -36,12 +36,14 @@ class Profile_block extends Memcached_DataObject public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Profile_block',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Profile_block',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - function get($blocker, $blocked) { + function get($blocker, $blocked) + { return Memcached_DataObject::pkeyGet('Profile_block', array('blocker' => $blocker, 'blocked' => $blocked)); diff --git a/classes/Profile_tag.php b/classes/Profile_tag.php index dde19aea2..cb60cbaec 100644 --- a/classes/Profile_tag.php +++ b/classes/Profile_tag.php @@ -16,86 +16,87 @@ class Profile_tag extends Memcached_DataObject public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Profile_tag',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Profile_tag',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - static function getTags($tagger, $tagged) { - - $tags = array(); + static function getTags($tagger, $tagged) { + + $tags = array(); - # XXX: store this in memcached - - $profile_tag = new Profile_tag(); - $profile_tag->tagger = $tagger; - $profile_tag->tagged = $tagged; - - $profile_tag->find(); - - while ($profile_tag->fetch()) { - $tags[] = $profile_tag->tag; - } - - $profile_tag->free(); - - return $tags; - } - - static function setTags($tagger, $tagged, $newtags) { - - $oldtags = Profile_tag::getTags($tagger, $tagged); - - # Delete stuff that's old that not in new - - $to_delete = array_diff($oldtags, $newtags); - - # Insert stuff that's in new and not in old - - $to_insert = array_diff($newtags, $oldtags); - - $profile_tag = new Profile_tag(); - - $profile_tag->tagger = $tagger; - $profile_tag->tagged = $tagged; - - $profile_tag->query('BEGIN'); - - foreach ($to_delete as $deltag) { - $profile_tag->tag = $deltag; - $result = $profile_tag->delete(); - if (!$result) { - common_log_db_error($profile_tag, 'DELETE', __FILE__); - return false; - } - } - - foreach ($to_insert as $instag) { - $profile_tag->tag = $instag; - $result = $profile_tag->insert(); - if (!$result) { - common_log_db_error($profile_tag, 'INSERT', __FILE__); - return false; - } - } - - $profile_tag->query('COMMIT'); - - return true; - } - - # Return profiles with a given tag - static function getTagged($tagger, $tag) { - $profile = new Profile(); - $profile->query('SELECT profile.* ' . - 'FROM profile JOIN profile_tag ' . - 'ON profile.id = profile_tag.tagged ' . - 'WHERE profile_tag.tagger = ' . $tagger . ' ' . - 'AND profile_tag.tag = "' . $tag . '" '); - $tagged = array(); - while ($profile->fetch()) { - $tagged[] = clone($profile); - } - return $tagged; - } + # XXX: store this in memcached + + $profile_tag = new Profile_tag(); + $profile_tag->tagger = $tagger; + $profile_tag->tagged = $tagged; + + $profile_tag->find(); + + while ($profile_tag->fetch()) { + $tags[] = $profile_tag->tag; + } + + $profile_tag->free(); + + return $tags; + } + + static function setTags($tagger, $tagged, $newtags) { + + $oldtags = Profile_tag::getTags($tagger, $tagged); + + # Delete stuff that's old that not in new + + $to_delete = array_diff($oldtags, $newtags); + + # Insert stuff that's in new and not in old + + $to_insert = array_diff($newtags, $oldtags); + + $profile_tag = new Profile_tag(); + + $profile_tag->tagger = $tagger; + $profile_tag->tagged = $tagged; + + $profile_tag->query('BEGIN'); + + foreach ($to_delete as $deltag) { + $profile_tag->tag = $deltag; + $result = $profile_tag->delete(); + if (!$result) { + common_log_db_error($profile_tag, 'DELETE', __FILE__); + return false; + } + } + + foreach ($to_insert as $instag) { + $profile_tag->tag = $instag; + $result = $profile_tag->insert(); + if (!$result) { + common_log_db_error($profile_tag, 'INSERT', __FILE__); + return false; + } + } + + $profile_tag->query('COMMIT'); + + return true; + } + + # Return profiles with a given tag + static function getTagged($tagger, $tag) { + $profile = new Profile(); + $profile->query('SELECT profile.* ' . + 'FROM profile JOIN profile_tag ' . + 'ON profile.id = profile_tag.tagged ' . + 'WHERE profile_tag.tagger = ' . $tagger . ' ' . + 'AND profile_tag.tag = "' . $tag . '" '); + $tagged = array(); + while ($profile->fetch()) { + $tagged[] = clone($profile); + } + return $tagged; + } } diff --git a/classes/Queue_item.php b/classes/Queue_item.php index 8ba3281de..9b909ec22 100644 --- a/classes/Queue_item.php +++ b/classes/Queue_item.php @@ -16,40 +16,42 @@ class Queue_item extends Memcached_DataObject public $claimed; // datetime() /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Queue_item',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Queue_item',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - function sequenceKey() { return array(false, false); } - - static function top($transport) { - - $qi = new Queue_item(); - $qi->transport = $transport; - $qi->orderBy('created'); - $qi->whereAdd('claimed is NULL'); - - $qi->limit(1); - - $cnt = $qi->find(TRUE); - - if ($cnt) { - # XXX: potential race condition - # can we force it to only update if claimed is still NULL - # (or old)? - common_log(LOG_INFO, 'claiming queue item = ' . $qi->notice_id . ' for transport ' . $transport); - $orig = clone($qi); - $qi->claimed = common_sql_now(); - $result = $qi->update($orig); - if ($result) { - common_log(LOG_INFO, 'claim succeeded.'); - return $qi; - } else { - common_log(LOG_INFO, 'claim failed.'); - } - } - $qi = NULL; - return NULL; - } + function sequenceKey() + { return array(false, false); } + + static function top($transport) { + + $qi = new Queue_item(); + $qi->transport = $transport; + $qi->orderBy('created'); + $qi->whereAdd('claimed is null'); + + $qi->limit(1); + + $cnt = $qi->find(true); + + if ($cnt) { + # XXX: potential race condition + # can we force it to only update if claimed is still null + # (or old)? + common_log(LOG_INFO, 'claiming queue item = ' . $qi->notice_id . ' for transport ' . $transport); + $orig = clone($qi); + $qi->claimed = common_sql_now(); + $result = $qi->update($orig); + if ($result) { + common_log(LOG_INFO, 'claim succeeded.'); + return $qi; + } else { + common_log(LOG_INFO, 'claim failed.'); + } + } + $qi = null; + return null; + } } diff --git a/classes/Remember_me.php b/classes/Remember_me.php index 5bbd6cf17..8dc29bfa3 100644 --- a/classes/Remember_me.php +++ b/classes/Remember_me.php @@ -15,10 +15,12 @@ class Remember_me extends Memcached_DataObject public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Remember_me',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Remember_me',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - function sequenceKey() { return array(false, false); } + function sequenceKey() + { return array(false, false); } } diff --git a/classes/Remote_profile.php b/classes/Remote_profile.php index c961dcca2..5aa6d913e 100644 --- a/classes/Remote_profile.php +++ b/classes/Remote_profile.php @@ -38,7 +38,8 @@ class Remote_profile extends Memcached_DataObject public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Remote_profile',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Remote_profile',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE diff --git a/classes/Reply.php b/classes/Reply.php index d71ce3afb..af86aaf87 100644 --- a/classes/Reply.php +++ b/classes/Reply.php @@ -16,7 +16,8 @@ class Reply extends Memcached_DataObject public $replied_id; // int(4) /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Reply',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Reply',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE diff --git a/classes/Sms_carrier.php b/classes/Sms_carrier.php index 6ecb51346..ffa12de29 100644 --- a/classes/Sms_carrier.php +++ b/classes/Sms_carrier.php @@ -17,12 +17,14 @@ class Sms_carrier extends Memcached_DataObject public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Sms_carrier',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Sms_carrier',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - - function toEmailAddress($sms) { - return sprintf($this->email_pattern, $sms); - } + + function toEmailAddress($sms) + { + return sprintf($this->email_pattern, $sms); + } } diff --git a/classes/Subscription.php b/classes/Subscription.php index cc174fcce..3fe0d167f 100644 --- a/classes/Subscription.php +++ b/classes/Subscription.php @@ -40,12 +40,14 @@ class Subscription extends Memcached_DataObject public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Subscription',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Subscription',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - - function &pkeyGet($kv) { - return Memcached_DataObject::pkeyGet('Subscription', $kv); - } + + function &pkeyGet($kv) + { + return Memcached_DataObject::pkeyGet('Subscription', $kv); + } } diff --git a/classes/Token.php b/classes/Token.php index d180ecebe..1fabd72f1 100644 --- a/classes/Token.php +++ b/classes/Token.php @@ -19,7 +19,8 @@ class Token extends Memcached_DataObject public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('Token',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('Token',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE diff --git a/classes/User.php b/classes/User.php index 32d5bedde..92ff8776b 100644 --- a/classes/User.php +++ b/classes/User.php @@ -62,89 +62,98 @@ class User extends Memcached_DataObject public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('User',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('User',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - function getProfile() { - return Profile::staticGet('id', $this->id); - } - - function isSubscribed($other) { - assert(!is_null($other)); - # XXX: cache results of this query - $sub = Subscription::pkeyGet(array('subscriber' => $this->id, - 'subscribed' => $other->id)); - return (is_null($sub)) ? false : true; - } - - # 'update' won't write key columns, so we have to do it ourselves. - - function updateKeys(&$orig) { - $parts = array(); - foreach (array('nickname', 'email', 'jabber', 'incomingemail', 'sms', 'carrier', 'smsemail', 'language', 'timezone') as $k) { - if (strcmp($this->$k, $orig->$k) != 0) { - $parts[] = $k . ' = ' . $this->_quote($this->$k); - } - } - if (count($parts) == 0) { - # No changes - return true; - } - $toupdate = implode(', ', $parts); - - $table = $this->tableName(); - if(common_config('db','quote_identifiers')) { - $table = '"' . $table . '"'; - } - $qry = 'UPDATE ' . $table . ' SET ' . $toupdate . - ' WHERE id = ' . $this->id; - $orig->decache(); - $result = $this->query($qry); - if ($result) { - $this->encache(); - } - return $result; - } - - function allowed_nickname($nickname) { - # XXX: should already be validated for size, content, etc. - static $blacklist = array('rss', 'xrds', 'doc', 'main', - 'settings', 'notice', 'user', - 'search', 'avatar', 'tag', 'tags', - 'api', 'message'); - $merged = array_merge($blacklist, common_config('nickname', 'blacklist')); - return !in_array($nickname, $merged); - } - - function getCurrentNotice($dt=NULL) { - $profile = $this->getProfile(); - if (!$profile) { - return NULL; - } - return $profile->getCurrentNotice($dt); - } - - function getCarrier() { - return Sms_carrier::staticGet('id', $this->carrier); - } - - function subscribeTo($other) { - $sub = new Subscription(); - $sub->subscriber = $this->id; - $sub->subscribed = $other->id; - - $sub->created = common_sql_now(); # current time - - if (!$sub->insert()) { - return false; - } - - return true; - } - - function hasBlocked($other) { + function getProfile() + { + return Profile::staticGet('id', $this->id); + } + + function isSubscribed($other) + { + assert(!is_null($other)); + # XXX: cache results of this query + $sub = Subscription::pkeyGet(array('subscriber' => $this->id, + 'subscribed' => $other->id)); + return (is_null($sub)) ? false : true; + } + + # 'update' won't write key columns, so we have to do it ourselves. + + function updateKeys(&$orig) + { + $parts = array(); + foreach (array('nickname', 'email', 'jabber', 'incomingemail', 'sms', 'carrier', 'smsemail', 'language', 'timezone') as $k) { + if (strcmp($this->$k, $orig->$k) != 0) { + $parts[] = $k . ' = ' . $this->_quote($this->$k); + } + } + if (count($parts) == 0) { + # No changes + return true; + } + $toupdate = implode(', ', $parts); + + $table = $this->tableName(); + if(common_config('db','quote_identifiers')) { + $table = '"' . $table . '"'; + } + $qry = 'UPDATE ' . $table . ' SET ' . $toupdate . + ' WHERE id = ' . $this->id; + $orig->decache(); + $result = $this->query($qry); + if ($result) { + $this->encache(); + } + return $result; + } + + function allowed_nickname($nickname) + { + # XXX: should already be validated for size, content, etc. + static $blacklist = array('rss', 'xrds', 'doc', 'main', + 'settings', 'notice', 'user', + 'search', 'avatar', 'tag', 'tags', + 'api', 'message'); + $merged = array_merge($blacklist, common_config('nickname', 'blacklist')); + return !in_array($nickname, $merged); + } + + function getCurrentNotice($dt=null) + { + $profile = $this->getProfile(); + if (!$profile) { + return null; + } + return $profile->getCurrentNotice($dt); + } + + function getCarrier() + { + return Sms_carrier::staticGet('id', $this->carrier); + } + + function subscribeTo($other) + { + $sub = new Subscription(); + $sub->subscriber = $this->id; + $sub->subscribed = $other->id; + + $sub->created = common_sql_now(); # current time + + if (!$sub->insert()) { + return false; + } + + return true; + } + + function hasBlocked($other) + { $block = Profile_block::get($this->id, $other->id); @@ -158,261 +167,273 @@ class User extends Memcached_DataObject return $result; } - static function register($fields) { + static function register($fields) { + + # MAGICALLY put fields into current scope + + extract($fields); + + $profile = new Profile(); + + $profile->query('BEGIN'); + + $profile->nickname = $nickname; + $profile->profileurl = common_profile_url($nickname); + + if ($fullname) { + $profile->fullname = $fullname; + } + if ($homepage) { + $profile->homepage = $homepage; + } + if ($bio) { + $profile->bio = $bio; + } + if ($location) { + $profile->location = $location; + } + + $profile->created = common_sql_now(); - # MAGICALLY put fields into current scope + $id = $profile->insert(); - extract($fields); + if (!$id) { + common_log_db_error($profile, 'INSERT', __FILE__); + return false; + } - $profile = new Profile(); + $user = new User(); - $profile->query('BEGIN'); + $user->id = $id; + $user->nickname = $nickname; - $profile->nickname = $nickname; - $profile->profileurl = common_profile_url($nickname); + if ($password) { # may not have a password for OpenID users + $user->password = common_munge_password($password, $id); + } - if ($fullname) { - $profile->fullname = $fullname; - } - if ($homepage) { - $profile->homepage = $homepage; - } - if ($bio) { - $profile->bio = $bio; - } - if ($location) { - $profile->location = $location; - } + # Users who respond to invite email have proven their ownership of that address - $profile->created = common_sql_now(); + if ($code) { + $invite = Invitation::staticGet($code); + if ($invite && $invite->address && $invite->address_type == 'email' && $invite->address == $email) { + $user->email = $invite->address; + } + } - $id = $profile->insert(); + $inboxes = common_config('inboxes', 'enabled'); - if (!$id) { - common_log_db_error($profile, 'INSERT', __FILE__); - return FALSE; - } + if ($inboxes === true || $inboxes == 'transitional') { + $user->inboxed = 1; + } + + $user->created = common_sql_now(); + $user->uri = common_user_uri($user); + + $result = $user->insert(); - $user = new User(); + if (!$result) { + common_log_db_error($user, 'INSERT', __FILE__); + return false; + } - $user->id = $id; - $user->nickname = $nickname; + # Everyone is subscribed to themself - if ($password) { # may not have a password for OpenID users - $user->password = common_munge_password($password, $id); - } + $subscription = new Subscription(); + $subscription->subscriber = $user->id; + $subscription->subscribed = $user->id; + $subscription->created = $user->created; - # Users who respond to invite email have proven their ownership of that address + $result = $subscription->insert(); - if ($code) { - $invite = Invitation::staticGet($code); - if ($invite && $invite->address && $invite->address_type == 'email' && $invite->address == $email) { - $user->email = $invite->address; - } - } - - $inboxes = common_config('inboxes', 'enabled'); - - if ($inboxes === true || $inboxes == 'transitional') { - $user->inboxed = 1; - } - - $user->created = common_sql_now(); - $user->uri = common_user_uri($user); - - $result = $user->insert(); - - if (!$result) { - common_log_db_error($user, 'INSERT', __FILE__); - return FALSE; - } - - # Everyone is subscribed to themself - - $subscription = new Subscription(); - $subscription->subscriber = $user->id; - $subscription->subscribed = $user->id; - $subscription->created = $user->created; - - $result = $subscription->insert(); - - if (!$result) { - common_log_db_error($subscription, 'INSERT', __FILE__); - return FALSE; - } - - if ($email && !$user->email) { - - $confirm = new Confirm_address(); - $confirm->code = common_confirmation_code(128); - $confirm->user_id = $user->id; - $confirm->address = $email; - $confirm->address_type = 'email'; - - $result = $confirm->insert(); - if (!$result) { - common_log_db_error($confirm, 'INSERT', __FILE__); - return FALSE; - } - } - - if ($code && $user->email) { - $user->emailChanged(); - } - - $profile->query('COMMIT'); - - if ($email && !$user->email) { - mail_confirm_address($user, $confirm->code, $profile->nickname, $email); - } - - return $user; - } - - # Things we do when the email changes - - function emailChanged() { - - $invites = new Invitation(); - $invites->address = $this->email; - $invites->address_type = 'email'; - - if ($invites->find()) { - while ($invites->fetch()) { - $other = User::staticGet($invites->user_id); - subs_subscribe_to($other, $this); - } - } - } - - function hasFave($notice) { - $cache = common_memcache(); - - # XXX: Kind of a hack. - if ($cache) { - # This is the stream of favorite notices, in rev chron - # order. This forces it into cache. - $faves = $this->favoriteNotices(0, NOTICE_CACHE_WINDOW); - $cnt = 0; - while ($faves->fetch()) { - if ($faves->id < $notice->id) { - # If we passed it, it's not a fave - return false; - } else if ($faves->id == $notice->id) { - # If it matches a cached notice, then it's a fave - return true; - } - $cnt++; - } - # If we're not past the end of the cache window, - # then the cache has all available faves, so this one - # is not a fave. - if ($cnt < NOTICE_CACHE_WINDOW) { - return false; - } - # Otherwise, cache doesn't have all faves; - # fall through to the default - } - $fave = Fave::pkeyGet(array('user_id' => $this->id, - 'notice_id' => $notice->id)); - return ((is_null($fave)) ? false : true); - } - function mutuallySubscribed($other) { - return $this->isSubscribed($other) && - $other->isSubscribed($this); - } - - function mutuallySubscribedUsers() { - - # 3-way join; probably should get cached - $qry = 'SELECT user.* ' . - 'FROM subscription sub1 JOIN user ON sub1.subscribed = user.id ' . - 'JOIN subscription sub2 ON user.id = sub2.subscriber ' . - 'WHERE sub1.subscriber = %d and sub2.subscribed = %d ' . - 'ORDER BY user.nickname'; - $user = new User(); - $user->query(sprintf($qry, $this->id, $this->id)); - - return $user; - } - - function getReplies($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=NULL) { - $qry = - 'SELECT notice.* ' . - 'FROM notice JOIN reply ON notice.id = reply.notice_id ' . - 'WHERE reply.profile_id = %d '; - return Notice::getStream(sprintf($qry, $this->id), - 'user:replies:'.$this->id, - $offset, $limit, $since_id, $before_id, NULL, $since); - } - - function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=NULL) { + if (!$result) { + common_log_db_error($subscription, 'INSERT', __FILE__); + return false; + } + + if ($email && !$user->email) { + + $confirm = new Confirm_address(); + $confirm->code = common_confirmation_code(128); + $confirm->user_id = $user->id; + $confirm->address = $email; + $confirm->address_type = 'email'; + + $result = $confirm->insert(); + if (!$result) { + common_log_db_error($confirm, 'INSERT', __FILE__); + return false; + } + } + + if ($code && $user->email) { + $user->emailChanged(); + } + + $profile->query('COMMIT'); + + if ($email && !$user->email) { + mail_confirm_address($user, $confirm->code, $profile->nickname, $email); + } + + return $user; + } + + # Things we do when the email changes + + function emailChanged() + { + + $invites = new Invitation(); + $invites->address = $this->email; + $invites->address_type = 'email'; + + if ($invites->find()) { + while ($invites->fetch()) { + $other = User::staticGet($invites->user_id); + subs_subscribe_to($other, $this); + } + } + } + + function hasFave($notice) + { + $cache = common_memcache(); + + # XXX: Kind of a hack. + if ($cache) { + # This is the stream of favorite notices, in rev chron + # order. This forces it into cache. + $faves = $this->favoriteNotices(0, NOTICE_CACHE_WINDOW); + $cnt = 0; + while ($faves->fetch()) { + if ($faves->id < $notice->id) { + # If we passed it, it's not a fave + return false; + } else if ($faves->id == $notice->id) { + # If it matches a cached notice, then it's a fave + return true; + } + $cnt++; + } + # If we're not past the end of the cache window, + # then the cache has all available faves, so this one + # is not a fave. + if ($cnt < NOTICE_CACHE_WINDOW) { + return false; + } + # Otherwise, cache doesn't have all faves; + # fall through to the default + } + $fave = Fave::pkeyGet(array('user_id' => $this->id, + 'notice_id' => $notice->id)); + return ((is_null($fave)) ? false : true); + } + function mutuallySubscribed($other) + { + return $this->isSubscribed($other) && + $other->isSubscribed($this); + } + + function mutuallySubscribedUsers() + { + + # 3-way join; probably should get cached + $qry = 'SELECT user.* ' . + 'FROM subscription sub1 JOIN user ON sub1.subscribed = user.id ' . + 'JOIN subscription sub2 ON user.id = sub2.subscriber ' . + 'WHERE sub1.subscriber = %d and sub2.subscribed = %d ' . + 'ORDER BY user.nickname'; + $user = new User(); + $user->query(sprintf($qry, $this->id, $this->id)); + + return $user; + } + + function getReplies($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) + { + $qry = + 'SELECT notice.* ' . + 'FROM notice JOIN reply ON notice.id = reply.notice_id ' . + 'WHERE reply.profile_id = %d '; + return Notice::getStream(sprintf($qry, $this->id), + 'user:replies:'.$this->id, + $offset, $limit, $since_id, $before_id, null, $since); + } + + function getNotices($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) + { $profile = $this->getProfile(); if (!$profile) { - return NULL; + return null; } else { return $profile->getNotices($offset, $limit, $since_id, $before_id); } - } - - function favoriteNotices($offset=0, $limit=NOTICES_PER_PAGE) { - $qry = - 'SELECT notice.* ' . - 'FROM notice JOIN fave ON notice.id = fave.notice_id ' . - 'WHERE fave.user_id = %d '; - return Notice::getStream(sprintf($qry, $this->id), - 'user:faves:'.$this->id, - $offset, $limit); - } - - function noticesWithFriends($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=NULL) { - $enabled = common_config('inboxes', 'enabled'); - - # Complicated code, depending on whether we support inboxes yet - # XXX: make this go away when inboxes become mandatory - - if ($enabled === false || - ($enabled == 'transitional' && $this->inboxed == 0)) { - $qry = - 'SELECT notice.* ' . - 'FROM notice JOIN subscription ON notice.profile_id = subscription.subscribed ' . - 'WHERE subscription.subscriber = %d '; - $order = NULL; - } else if ($enabled === true || - ($enabled == 'transitional' && $this->inboxed == 1)) { - - $qry = - 'SELECT notice.* ' . - 'FROM notice JOIN notice_inbox ON notice.id = notice_inbox.notice_id ' . - 'WHERE notice_inbox.user_id = %d '; - # NOTE: we override ORDER - $order = 'ORDER BY notice_inbox.created DESC, notice_inbox.notice_id DESC '; - } - return Notice::getStream(sprintf($qry, $this->id), - 'user:notices_with_friends:' . $this->id, - $offset, $limit, $since_id, $before_id, - $order, $since); - } - - function blowFavesCache() { - $cache = common_memcache(); - if ($cache) { - # Faves don't happen chronologically, so we need to blow - # ;last cache, too - $cache->delete(common_cache_key('user:faves:'.$this->id)); - $cache->delete(common_cache_key('user:faves:'.$this->id).';last'); - } - } - - function getSelfTags() { - return Profile_tag::getTags($this->id, $this->id); - } - - function setSelfTags($newtags) { - return Profile_tag::setTags($this->id, $this->id, $newtags); - } - - function block($other) { + } + + function favoriteNotices($offset=0, $limit=NOTICES_PER_PAGE) + { + $qry = + 'SELECT notice.* ' . + 'FROM notice JOIN fave ON notice.id = fave.notice_id ' . + 'WHERE fave.user_id = %d '; + return Notice::getStream(sprintf($qry, $this->id), + 'user:faves:'.$this->id, + $offset, $limit); + } + + function noticesWithFriends($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $before_id=0, $since=null) + { + $enabled = common_config('inboxes', 'enabled'); + + # Complicated code, depending on whether we support inboxes yet + # XXX: make this go away when inboxes become mandatory + + if ($enabled === false || + ($enabled == 'transitional' && $this->inboxed == 0)) { + $qry = + 'SELECT notice.* ' . + 'FROM notice JOIN subscription ON notice.profile_id = subscription.subscribed ' . + 'WHERE subscription.subscriber = %d '; + $order = null; + } else if ($enabled === true || + ($enabled == 'transitional' && $this->inboxed == 1)) { + + $qry = + 'SELECT notice.* ' . + 'FROM notice JOIN notice_inbox ON notice.id = notice_inbox.notice_id ' . + 'WHERE notice_inbox.user_id = %d '; + # NOTE: we override ORDER + $order = 'ORDER BY notice_inbox.created DESC, notice_inbox.notice_id DESC '; + } + return Notice::getStream(sprintf($qry, $this->id), + 'user:notices_with_friends:' . $this->id, + $offset, $limit, $since_id, $before_id, + $order, $since); + } + + function blowFavesCache() + { + $cache = common_memcache(); + if ($cache) { + # Faves don't happen chronologically, so we need to blow + # ;last cache, too + $cache->delete(common_cache_key('user:faves:'.$this->id)); + $cache->delete(common_cache_key('user:faves:'.$this->id).';last'); + } + } + + function getSelfTags() + { + return Profile_tag::getTags($this->id, $this->id); + } + + function setSelfTags($newtags) + { + return Profile_tag::setTags($this->id, $this->id, $newtags); + } + + function block($other) + { # Add a new block record @@ -434,8 +455,8 @@ class User extends Memcached_DataObject # Cancel their subscription, if it exists - $sub = Subscription::pkeyGet(array('subscriber' => $other->id, - 'subscribed' => $this->id)); + $sub = Subscription::pkeyGet(array('subscriber' => $other->id, + 'subscribed' => $this->id)); if ($sub) { $result = $sub->delete(); @@ -450,7 +471,8 @@ class User extends Memcached_DataObject return true; } - function unblock($other) { + function unblock($other) + { # Get the block record diff --git a/classes/User_openid.php b/classes/User_openid.php index ad68f7402..f4fda1c72 100644 --- a/classes/User_openid.php +++ b/classes/User_openid.php @@ -17,7 +17,8 @@ class User_openid extends Memcached_DataObject public $modified; // timestamp() not_null default_CURRENT_TIMESTAMP /* Static get */ - function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('User_openid',$k,$v); } + function staticGet($k,$v=null) + { return Memcached_DataObject::staticGet('User_openid',$k,$v); } /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE diff --git a/file/.gitignore b/file/.gitignore new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/file/.gitignore diff --git a/htaccess.sample b/htaccess.sample index bd29d318f..e348635a8 100644 --- a/htaccess.sample +++ b/htaccess.sample @@ -143,6 +143,9 @@ RewriteRule ^api/notifications/leave/(.*)$ index.php?action=api&apiaction=notifi RewriteRule ^api/blocks/create/(.*)$ index.php?action=api&apiaction=blocks&method=create&argument=$1 [L,QSA] RewriteRule ^api/blocks/destroy/(.*)$ index.php?action=api&apiaction=blocks&method=destroy&argument=$1 [L,QSA] RewriteRule ^api/help/(.*)$ index.php?action=api&apiaction=help&method=$1 [L,QSA] +RewriteRule ^api/laconica/version(.*)$ index.php?action=api&apiaction=laconica&method=version$1 [L,QSA] +RewriteRule ^api/laconica/config(.*)$ index.php?action=api&apiaction=laconica&method=config$1 [L,QSA] +RewriteRule ^api/laconica/wadl\.xml$ index.php?action=api&apiaction=laconica&method=wadl.xml [L,QSA] <FilesMatch "\.(ini)"> Order allow,deny @@ -1,5 +1,5 @@ <?php -/* +/** * Laconica - a distributed open-source microblogging tool * Copyright (C) 2008, Controlez-Vous, Inc. * @@ -20,13 +20,13 @@ define('INSTALLDIR', dirname(__FILE__)); define('LACONICA', true); -require_once(INSTALLDIR . "/lib/common.php"); +require_once INSTALLDIR . '/lib/common.php'; -# get and cache current user +// get and cache current user $user = common_current_user(); -# initialize language env +// initialize language env common_init_language(); @@ -41,30 +41,34 @@ if (!$action || !preg_match('/^[a-zA-Z0-9_-]*$/', $action)) { if (!$user && common_config('site', 'private') && !in_array($action, array('login', 'openidlogin', 'finishopenidlogin', - 'recoverpassword', 'api', 'doc', 'register'))) -{ + 'recoverpassword', 'api', 'doc', 'register'))) { common_redirect(common_local_url('login')); } $actionfile = INSTALLDIR."/actions/$action.php"; if (file_exists($actionfile)) { - require_once($actionfile); - $action_class = ucfirst($action)."Action"; + + include_once $actionfile; + + $action_class = ucfirst($action).'Action'; + $action_obj = new $action_class(); - if ($config['db']['mirror'] && $action_obj->is_readonly()) { - if (is_array($config['db']['mirror'])) { - # "load balancing", ha ha - $k = array_rand($config['db']['mirror']); - $mirror = $config['db']['mirror'][$k]; - } else { - $mirror = $config['db']['mirror']; - } - $config['db']['database'] = $mirror; - } + + if ($config['db']['mirror'] && $action_obj->is_readonly()) { + if (is_array($config['db']['mirror'])) { + // "load balancing", ha ha + $k = array_rand($config['db']['mirror']); + + $mirror = $config['db']['mirror'][$k]; + } else { + $mirror = $config['db']['mirror']; + } + $config['db']['database'] = $mirror; + } if (call_user_func(array($action_obj, 'prepare'), $_REQUEST)) { - call_user_func(array($action_obj, 'handle'), $_REQUEST); - } + call_user_func(array($action_obj, 'handle'), $_REQUEST); + } } else { common_user_error(_('Unknown action')); }
\ No newline at end of file diff --git a/js/identica-badge.js b/js/identica-badge.js new file mode 100644 index 000000000..5c586b5d6 --- /dev/null +++ b/js/identica-badge.js @@ -0,0 +1,293 @@ +// identica badge -- updated to work with the native API, 12-4-2008 +// copyright Kent Brewster 2008 +// see http://kentbrewster.com/identica-badge for info +( function() { + var trueName = ''; + for (var i = 0; i < 16; i++) { + trueName += String.fromCharCode(Math.floor(Math.random() * 26) + 97); + } + window[trueName] = {}; + var $ = window[trueName]; + $.f = function() { + return { + runFunction : [], + init : function(target) { + var theScripts = document.getElementsByTagName('SCRIPT'); + for (var i = 0; i < theScripts.length; i++) { + if (theScripts[i].src.match(target)) { + $.a = {}; + if (theScripts[i].innerHTML) { + $.a = $.f.parseJson(theScripts[i].innerHTML); + } + if ($.a.err) { + alert('bad json!'); + } + $.f.loadDefaults(); + $.f.buildStructure(); + $.f.buildPresentation(); + theScripts[i].parentNode.insertBefore($.s, theScripts[i]); + theScripts[i].parentNode.removeChild(theScripts[i]); + break; + } + } + }, + parseJson : function(json) { + this.parseJson.data = json; + if ( typeof json !== 'string') { + return {"err":"trying to parse a non-string JSON object"}; + } + try { + var f = Function(['var document,top,self,window,parent,Number,Date,Object,Function,', + 'Array,String,Math,RegExp,Image,ActiveXObject;', + 'return (' , json.replace(/<\!--.+-->/gim,'').replace(/\bfunction\b/g,'function­') , ');'].join('')); + return f(); + } catch (e) { + return {"err":"trouble parsing JSON object"}; + } + }, + loadDefaults : function() { + $.d = { + "user":"7000", + "headerText" : "", + "height" : 350, + "width" : 300, + "background" : "#193441", + "border" : "1px solid black", + "userFontSize" : "inherit", + "userColor" : "inherit", + "headerBackground" : "transparent", + "headerColor" : "white", + "evenBackground" : "#fff", + "oddBackground" : "#eee", + "thumbnailBorder" : "1px solid black", + "thumbnailSize" : 24, + "padding" : 3, + "server" : "identi.ca" + }; + for (var k in $.d) { if ($.a[k] === undefined) { $.a[k] = $.d[k]; } } + }, + buildPresentation : function () { + var ns = document.createElement('style'); + document.getElementsByTagName('head')[0].appendChild(ns); + if (!window.createPopup) { + ns.appendChild(document.createTextNode('')); + ns.setAttribute("type", "text/css"); + } + var s = document.styleSheets[document.styleSheets.length - 1]; + var rules = { + "" : "{zoom:1;margin:0;padding:0;width:" + $.a.width + "px;background:" + $.a.background + ";border:" + $.a.border + ";font:13px/1.2em tahoma, veranda, arial, helvetica, clean, sans-serif;*font-size:small;*font:x-small;}", + "a" : "{cursor:pointer;text-decoration:none;}", + "a:hover" : "{text-decoration:underline;}", + "cite" : "{font-weight:bold;margin:0 0 0 4px;padding:0;display:block;font-style:normal;line-height:" + ($.a.thumbnailSize/2) + "px;}", + "cite a" : "{color:#C15D42;}", + "date":"{font-size:87%;margin:0 0 0 4px;padding:0;display:block;font-style:normal;line-height:" + ($.a.thumbnailSize/2) + "px;}", + "date:after" : "{clear:both; content:\".\"; display:block; height:0; visibility:hidden; }", + "date a" : "{color:#676;}", + "h3" : "{margin:0;padding:" + $.a.padding + "px;font-weight:bold;background:" + $.a.headerBackground + " url('http://" + $.a.server + "/favicon.ico') " + $.a.padding + "px 50% no-repeat;text-indent:" + ($.a.padding + 16) + "px;}", + "h3.loading" : "{background-image:url('http://l.yimg.com/us.yimg.com/i/us/my/mw/anim_loading_sm.gif');}", + "h3 a" : "{font-size:92%; color:" + $.a.headerColor + ";}", + "h4" : "{font-weight:normal; background:" + $.a.headerBackground + ";text-align:right;margin:0;padding:" + $.a.padding + "px;}", + "h4 a" : "{font-size:92%; color:" + $.a.headerColor + ";}", + "img":"{float:left; height:" + $.a.thumbnailSize + "px;width:" + $.a.thumbnailSize + "px;border:" + $.a.thumbnailBorder + ";margin-right:" + $.a.padding + "px;}", + "p" : "{margin:0; padding:0;width:" + ($.a.width - 22) + "px;overflow:hidden;font-size:87%;}", + "p a" : "{color:#C15D42;}", + "ul":"{margin:0; padding:0; height:" + $.a.height + "px;width:" + $.a.width + "px;overflow:auto;}", + "ul li":"{background:" + $.a.evenBackground + ";margin:0;padding:" + $.a.padding + "px;list-style:none;width:" + ($.a.width - 22) + "px;overflow:hidden;border-bottom:1px solid #D8E2D7;}", + "ul li:hover":"{background:#f3f8ea;}" + }; + var ieRules = ""; + // brute-force each and every style rule here to !important + // sometimes you have to take off and nuke the site from orbit; it's the only way to be sure + for (var z in rules) { + var selector = '.' + trueName + ' ' + z; + var rule = rules[z]; + if (typeof rule === 'string') { + var important = rule.replace(/;/gi, '!important;'); + if (!window.createPopup) { + var theRule = document.createTextNode(selector + important); + ns.appendChild(theRule); + } else { + ieRules += selector + important; + } + } + } + if (window.createPopup) { s.cssText = ieRules; } + }, + buildStructure : function() { + $.s = document.createElement('DIV'); + $.s.className = trueName; + $.s.h = document.createElement('H3'); + $.s.h.a = document.createElement('A'); + $.s.h.a.target = '_laconica'; + $.s.h.appendChild($.s.h.a); + $.s.appendChild($.s.h); + $.s.r = document.createElement('UL'); + $.s.appendChild($.s.r); + $.s.f = document.createElement('H4'); + var a = document.createElement('A'); + a.innerHTML = 'get this'; + a.target = '_blank'; + a.href = 'http://kentbrewster.com/identica-badge'; + $.s.f.appendChild(a); + $.s.appendChild($.s.f); + $.f.getUser(); + }, + getUser : function() { + if (!$.f.runFunction) { $.f.runFunction = []; } + var n = $.f.runFunction.length; + var id = trueName + '.f.runFunction[' + n + ']'; + $.f.runFunction[n] = function(r) { + delete($.f.runFunction[n]); + var a = document.createElement('A'); + a.rel = $.a.user; + a.rev = r.name; + a.id = r.screen_name; + $.f.removeScript(id); + $.f.changeUserTo(a); + }; + var url = 'http://' + $.a.server + '/api/users/show/' + $.a.user + '.json?callback=' + id; + $.f.runScript(url, id); + }, + changeUserTo : function(el) { + $.a.user = el.rel; + $.s.h.a.innerHTML = el.rev + $.a.headerText; + $.s.h.a.href = 'http://' + $.a.server + '/' + el.id; + $.f.runSearch(); + }, + runSearch : function() { + $.s.h.className = 'loading'; + $.s.r.innerHTML = ''; + if (!$.f.runFunction) { $.f.runFunction = []; } + var n = $.f.runFunction.length; + var id = trueName + '.f.runFunction[' + n + ']'; + $.f.runFunction[n] = function(r) { + delete($.f.runFunction[n]); + $.f.removeScript(id); + $.f.renderResult(r); + }; + var url = 'http://' + $.a.server + '/api/statuses/friends/' + $.a.user + '.json?callback=' + id; + $.f.runScript(url, id); + }, + renderResult: function(r) { + for (var i = 0; i < r.length; i++) { + if (!r[i].status) { + r.splice(i, 1); + } else { + r[i].status_id = parseInt(r[i].status.id); + } + } + r = $.f.sortArray(r, "status_id", true); + $.s.h.className = ''; + for (var i = 0; i < r.length; i++) { + var li = document.createElement('LI'); + var icon = document.createElement('A'); + if (r[i] && r[i].url) { + icon.href = r[i].url; + icon.target = '_laconica'; + icon.title = 'Visit ' + r[i].screen_name + ' at ' + r[i].url; + } else { + icon.href = 'http://' + $.a.server + '/' + r[i].screen_name; + icon.target = '_laconica'; + icon.title = 'Visit ' + r[i].screen_name + ' at http://' + $.a.server + '/' + r[i].screen_name; + } + + var img = document.createElement('IMG'); + img.src = r[i].profile_image_url; + icon.appendChild(img); + li.appendChild(icon); + + var user = document.createElement('CITE'); + var a = document.createElement('A'); + a.rel = r[i].id; + a.rev = r[i].name; + a.id = r[i].screen_name; + a.innerHTML = r[i].name; + a.href = 'http://' + $.a.server + '/' + r[i].screen_name; + a.onclick = function() { + $.f.changeUserTo(this); + return false; + }; + user.appendChild(a); + li.appendChild(user); + var updated = document.createElement('DATE'); + if (r[i].status && r[i].status.created_at) { + var date_link = document.createElement('A'); + date_link.innerHTML = r[i].status.created_at.split(/\+/)[0]; + date_link.href = 'http://' + $.a.server + '/notice/' + r[i].status.id; + date_link.target = '_laconica'; + updated.appendChild(date_link); + if (r[i].status.in_reply_to_status_id) { + updated.appendChild(document.createTextNode(' in reply to ')); + var in_reply_to = document.createElement('A'); + in_reply_to.innerHTML = r[i].status.in_reply_to_status_id; + in_reply_to.href = 'http://' + $.a.server + '/notice/' + r[i].status.in_reply_to_status_id; + in_reply_to.target = '_laconica'; + updated.appendChild(in_reply_to); + } + } else { + updated.innerHTML = 'has not updated yet'; + } + li.appendChild(updated); + var p = document.createElement('P'); + if (r[i].status && r[i].status.text) { + var raw = r[i].status.text; + var cooked = raw; + cooked = cooked.replace(/http:\/\/([^ ]+)/g, "<a href=\"http://$1\" target=\"_laconica\">http://$1</a>"); + cooked = cooked.replace(/@([\w*]+)/g, '@<a href="http://' + $.a.server + '/$1" target=\"_laconica\">$1</a>'); + cooked = cooked.replace(/#([\w*]+)/g, '#<a href="http://' + $.a.server + '/tag/$1" target="_laconica">$1</a>'); + p.innerHTML = cooked; + } + li.appendChild(p); + var a = p.getElementsByTagName('A'); + for (var j = 0; j < a.length; j++) { + if (a[j].className == 'changeUserTo') { + a[j].className = ''; + a[j].href = 'http://' + $.a.server + '/' + a[j].innerHTML; + a[j].rel = a[j].innerHTML; + a[j].onclick = function() { + $.f.changeUserTo(this); + return false; + } + } + } + $.s.r.appendChild(li); + } + }, + sortArray : function(r, k, x) { + if (window.createPopup) { + return r; + } + function s(a, b) { + if (x === true) { + return b[k] - a[k]; + } else { + return a[k] - b[k]; + } + } + r = r.sort(s); + return r; + }, + runScript : function(url, id) { + var s = document.createElement('script'); + s.id = id; + s.type ='text/javascript'; + s.src = url; + document.getElementsByTagName('body')[0].appendChild(s); + }, + removeScript : function(id) { + if (document.getElementById(id)) { + var s = document.getElementById(id); + s.parentNode.removeChild(s); + } + } + }; + }(); +// var thisScript = /^https?:\/\/[^\/]*r8ar.com\/identica-badge.js$/; + var thisScript = /identica-badge.js$/; + if(typeof window.addEventListener !== 'undefined') { + window.addEventListener('load', function() { $.f.init(thisScript); }, false); + } else if(typeof window.attachEvent !== 'undefined') { + window.attachEvent('onload', function() { $.f.init(thisScript); }); + } +} )(); + diff --git a/js/jcrop/Jcrop.gif b/js/jcrop/Jcrop.gif Binary files differnew file mode 100644 index 000000000..72ea7ccb5 --- /dev/null +++ b/js/jcrop/Jcrop.gif diff --git a/js/jcrop/jquery.Jcrop.css b/js/jcrop/jquery.Jcrop.css new file mode 100644 index 000000000..b95178a72 --- /dev/null +++ b/js/jcrop/jquery.Jcrop.css @@ -0,0 +1,45 @@ +/* Fixes issue here http://code.google.com/p/jcrop/issues/detail?id=1 */ +.jcrop-holder +{ + text-align: left; +} + +.jcrop-vline, .jcrop-hline +{ + font-size: 0; + position: absolute; + background: white url('Jcrop.gif') top left repeat; + /* + opacity: .5; + *filter:alpha(opacity=50); + */ +} +.jcrop-vline { height: 100%; width: 1px !important; } +.jcrop-hline { width: 100%; height: 1px !important; } +.jcrop-handle { + font-size: 1px; + width: 7px !important; + height: 7px !important; + border: 1px #eee solid; + background-color: #333; + *width: 9px; + *height: 9px; +} + +.jcrop-tracker { + *background-color: gray; + width: 100%; height: 100%; +} + +.custom .jcrop-vline, +.custom .jcrop-hline +{ + background: yellow; +} +.custom .jcrop-handle +{ + border-color: black; + background-color: #C7BB00; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; +} diff --git a/js/jcrop/jquery.Jcrop.go.js b/js/jcrop/jquery.Jcrop.go.js new file mode 100644 index 000000000..7c5b5e4e6 --- /dev/null +++ b/js/jcrop/jquery.Jcrop.go.js @@ -0,0 +1,41 @@ + $(function(){ + jQuery("#avatar_original img.avatar").Jcrop({ onChange: showPreview, + setSelect: [ 0, 0, $("#avatar_original img.avatar").attr("width"), $("#avatar_original img.avatar").attr("height") ], + onSelect: updateCoords, + aspectRatio: 1, + boxWidth: 480, + boxHeight: 480, + bgColor: '#000', + bgOpacity: .4 + }); + }); + + function showPreview(coords) { + var rx = 96 / coords.w; + var ry = 96 / coords.h; + + var img_width = $("#avatar_original img.avatar").attr("width"); + var img_height = $("#avatar_original img.avatar").attr("height"); + + + $('#avatar_preview img.avatar').css({ + width: Math.round(rx *img_width) + 'px', + height: Math.round(ry * img_height) + 'px', + marginLeft: '-' + Math.round(rx * coords.x) + 'px', + marginTop: '-' + Math.round(ry * coords.y) + 'px' + }); + }; + + function updateCoords(c) { + $('#avatar_crop_x').val(c.x); + $('#avatar_crop_y').val(c.y); + $('#avatar_crop_w').val(c.w); + $('#avatar_crop_h').val(c.h); + }; + + function checkCoords() { + if (parseInt($('#avatar_crop_w').val())) return true; + alert('Please select a crop region then press submit.'); + return false; + }; + diff --git a/js/jcrop/jquery.Jcrop.pack.js b/js/jcrop/jquery.Jcrop.pack.js new file mode 100644 index 000000000..aa82e8abe --- /dev/null +++ b/js/jcrop/jquery.Jcrop.pack.js @@ -0,0 +1,8 @@ +/** + * Jcrop v.0.9.5 (packed) + * (c) 2008 Kelly Hallman and DeepLiquid.com + * More information: http://deepliquid.com/content/Jcrop.html + * Released under MIT License - this header must remain with code + */ + +eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('$.1n=7(G,F){d G=G,F=F;g(1p(G)!==\'2d\')G=$(G)[0];g(1p(F)!==\'2d\')F={};g(!(\'2x\'1a F))F.2x=$.3d.3e?K:M;g(!(\'2c\'1a F))F.2c=$.3d.3e?K:M;d 4f={2x:K,3W:\'4C\',1f:4D,3T:\'4Y\',3x:.6,3O:.4,3P:.5,53:5,3N:9,3D:5,51:14,25:0,2c:M,3I:M,3B:M,30:M,3A:M,49:0,4p:0,4k:8,3V:20,3X:3,2f:K,3n:[0,0],2z:[0,0],2O:[0,0],2D:7(){},2G:7(){}};d j=4f;21(F);d $I=$(G).B({16:\'1b\'});47($I,j.49,j.4p);d S=$I.W(),L=$I.U(),$12=$(\'<12 />\').W(S).U(L).1f(1L(\'4F\')).B({16:\'4H\',4B:j.3T});g(j.1f)$12.1f(j.1f);$I.54($12);d $34=$(\'<I />\').3Y(\'2N\',$I.3Y(\'2N\')).B(\'16\',\'1b\').W(S).U(L);d $2C=$(\'<12 />\').W(1t(V)).U(1t(V)).B({1l:59,16:\'1b\',4o:\'4g\'}).1P($34);d $2g=$(\'<12 />\').W(1t(V)).U(1t(V)).B({1l:5b});d $28=$(\'<12 />\').B({16:\'1b\',1l:55}).3U($I).1P($2C,$2g);d 23=j.4k;d $1S=$(\'<12 />\').1f(1L(\'3v\')).W(S+(23*2)).U(L+(23*2)).B({16:\'1b\',R:D(-23),P:D(-23),1l:3R,1z:0}).3q(48);d 1I,1Q;d 2u=2Q(G),1q,1B,3i,58,3h,1O;g(\'36\'1a j){1I=j.36[0]/S;1Q=j.36[1]/L}d E=7(){d A=0,u=0,q=0,m=0,Z,Y;7 1A(z){d z=2T(z);q=A=z[0];m=u=z[1]};7 1y(z){d z=2T(z);Z=z[0]-q;Y=z[1]-m;q=z[0];m=z[1]};7 3f(){k[Z,Y]};7 2b(2y){d Z=2y[0],Y=2y[1];g(0>A+Z)Z-=Z+A;g(0>u+Y)Y-=Y+u;g(L<m+Y)Y+=L-(m+Y);g(S<q+Z)Z+=S-(q+Z);A+=Z;q+=Z;u+=Y;m+=Y};7 2K(T){d c=Q();1E(T){C\'1s\':k[c.q,c.y];C\'11\':k[c.x,c.y];C\'2e\':k[c.q,c.m];C\'1M\':k[c.x,c.m]}};7 Q(){g(!j.25&&!1B)k 3F();d 1k=j.25?j.25:1B,5c=j.2O,4u=j.2z,1V=q-A,1Z=m-u,3c=N.17(1V),3j=N.17(1Z),3M=3c/3j,15,13;g(3M<1k){13=m;w=3j*1k;15=1V<0?A-w:w+A;g(15<0){15=0;h=N.17((15-A)/1k);13=1Z<0?u-h:h+u}1g g(15>S){15=S;h=N.17((15-A)/1k);13=1Z<0?u-h:h+u}}1g{15=q;h=3c/1k;13=1Z<0?u-h:u+h;g(13<0){13=0;w=N.17((13-u)*1k);15=1V<0?A-w:w+A}1g g(13>L){13=L;w=N.17(13-u)*1k;15=1V<0?A-w:w+A}}k 4E=3g(1F(A,u,15,13))};7 2T(p){g(p[0]<0)p[0]=0;g(p[1]<0)p[1]=0;g(p[0]>S)p[0]=S;g(p[1]>L)p[1]=L;k[p[0],p[1]]};7 1F(A,u,q,m){d 2R=A,3r=q,3o=u,3l=m;g(q<A){2R=q;3r=A}g(m<u){3o=m;3l=u}k[N.1K(2R),N.1K(3o),N.1K(3r),N.1K(3l)]};7 3F(){d 1U=q-A;d 22=m-u;g(2q&&(N.17(1U)>2q))q=(1U>0)?(A+2q):(A-2q);g(2n&&(N.17(22)>2n))m=(22>0)?(u+2n):(u-2n);g(2i&&(N.17(22)<2i))m=(22>0)?(u+2i):(u-2i);g(2m&&(N.17(1U)<2m))q=(1U>0)?(A+2m):(A-2m);g(A<0){q-=A;A-=A}g(u<0){m-=u;u-=u}g(q<0){A-=q;q-=q}g(m<0){u-=m;m-=m}g(q>S){d X=q-S;A-=X;q-=X}g(m>L){d X=m-L;u-=X;m-=X}g(A>S){d X=A-L;m-=X;u-=X}g(u>L){d X=u-L;m-=X;u-=X}k 3g(1F(A,u,q,m))};7 3g(a){k{x:a[0],y:a[1],q:a[2],m:a[3],w:a[2]-a[0],h:a[3]-a[1]}};k{1F:1F,1A:1A,1y:1y,3f:3f,2b:2b,2K:2K,Q:Q}}();d J=7(){d 4v,4z,4y,1R,2U=4x;d 2F={};d H={};d 2E=K;d 1i=j.3D;g(j.30){2F={R:1Y(\'3C\').B(\'R\',$.3d.3e?D(-1):D(0)),3Q:1Y(\'3C\'),P:1Y(\'3z\'),3L:1Y(\'3z\')}}g(j.3A){H.t=1W(\'n\');H.b=1W(\'s\');H.r=1W(\'e\');H.l=1W(\'w\')}j.3B&&2Y([\'n\',\'s\',\'e\',\'w\']);j.3I&&2Y([\'1M\',\'11\',\'1s\',\'2e\']);7 1Y(1u){d 1J=$(\'<12 />\').B({16:\'1b\',1z:j.3O}).1f(1L(1u));$2C.1P(1J);k 1J};7 2W(T,3y){d 1J=$(\'<12 />\').3q(3b(T)).B({3p:T+\'-2A\',16:\'1b\',1l:3y});$2g.1P(1J);k 1J};7 3J(T){k 2W(T,2U++).B({R:D(-1i+1),P:D(-1i+1),1z:j.3P}).1f(1L(\'H\'))};7 1W(T){d s=j.3N,o=1i,h=s,w=s,t=o,l=o;1E(T){C\'n\':C\'s\':w=1t(V);O;C\'e\':C\'w\':h=1t(V);O}k 2W(T,2U++).W(w).U(h).B({R:D(-t+1),P:D(-l+1)})};7 2Y(2J){4U(i 1a 2J)H[2J[i]]=3J(2J[i])};7 31(c){d 3a=N.1K((c.h/2)-1i),35=N.1K((c.w/2)-1i),4V=4W=-1i+1,2a=c.w-1i,1X=c.h-1i,x,y;\'e\'1a H&&H.e.B({R:D(3a),P:D(2a)})&&H.w.B({R:D(3a)})&&H.s.B({R:D(1X),P:D(35)})&&H.n.B({P:D(35)});\'1s\'1a H&&H.1s.B({P:D(2a)})&&H.2e.B({R:D(1X),P:D(2a)})&&H.1M.B({R:D(1X)});\'b\'1a H&&H.b.B({R:D(1X)})&&H.r.B({P:D(2a)})};7 3K(x,y){$34.B({R:D(-y),P:D(-x)});$28.B({R:D(y),P:D(x)})};7 2A(w,h){$28.W(w).U(h)};7 3s(){d p=E.Q();E.1A([p.x,p.y]);E.1y([p.q,p.m])};7 2I(){g(1R)k 1e()};7 1e(){d c=E.Q();2A(c.w,c.h);3K(c.x,c.y);j.30&&2F[\'3L\'].B({P:D(c.w-1)})&&2F[\'3Q\'].B({R:D(c.h-1)});2E&&31(c);1R||1w();j.2D(2H(c))};7 1w(){$28.1w();$I.B(\'1z\',j.3x);1R=M};7 1r(){1o();$28.1v();$I.B(\'1z\',1);1R=K};7 1v(){1r();$I.B(\'1z\',1);1R=K};7 2t(){2E=M;31(E.Q());$2g.1w()};7 1o(){2E=K;$2g.1v()};7 2o(v){(3h=v)?1o():2t()};7 1h(){d c=E.Q();2o(K);3s()};1o();$2C.1P($(\'<12 />\').1f(1L(\'3v\')).3q(3b(\'1N\')).B({3p:\'1N\',16:\'1b\',1l:4M,1z:0}));k{2I:2I,1e:1e,1r:1r,1w:1w,1v:1v,2t:2t,1o:1o,2o:2o,1h:1h}}();d 1j=7(){d 2w=7(){},2v=7(){},2L=j.2x;g(!2L){$1S.3k(2B).2S(26).4N(26)}7 4j(){g(2L){$(3t).3k(2B).2S(26)}$1S.B({1l:4G})}7 4i(){g(2L){$(3t).3H(\'3k\',2B).3H(\'2S\',26)}$1S.B({1l:3R})}7 2B(e){2w(2r(e))};7 26(e){e.2j();e.2k();g(1q){1q=K;2v(2r(e));j.2G(2H(E.Q()));4i();2w=7(){};2v=7(){}}k K};7 1G(1N,1h){1q=M;2w=1N;2v=1h;4j();k K};7 1x(t){$1S.B(\'3p\',t)};$I.4s($1S);k{1G:1G,1x:1x}}();d 33=7(){d $24=$(\'<4w 1u="4L" />\').B({16:\'1b\',P:\'-4O\'}).57(43).56(2f).5a(41),$3S=$(\'<12 />\').B({16:\'1b\',4o:\'4g\'}).1P($24);7 2l(){g(j.2c){$24.1w();$24.4Z()}};7 41(e){$24.1v()};7 2f(e){g(!j.2f)k;d 42=1O,1C;1O=e.4Q?M:K;g(42!=1O){g(1O&&1q){1C=E.Q();1B=1C.w/1C.h}1g 1B=0;J.1e()}e.2k();e.2j();k K};7 29(e,x,y){E.2b([x,y]);J.2I();e.2j();e.2k()};7 43(e){g(e.4T)k M;2f(e);d 2h=1O?10:1;1E(e.5d){C 37:29(e,-2h,0);O;C 39:29(e,2h,0);O;C 38:29(e,0,-2h);O;C 40:29(e,0,2h);O;C 27:J.1r();O;C 9:k M}k K};g(j.2c)$3S.3U($I);k{2l:2l}}();7 D(n){k\'\'+1m(n)+\'D\'};7 1t(n){k\'\'+1m(n)+\'%\'};7 1L(44){k j.3W+\'-\'+44};7 2Q(G){d z=$(G).2y();k[z.P,z.R]};7 2r(e){k[(e.4q-2u[0]),(e.4r-2u[1])]};7 46(1u){g(1u!=3i){1j.1x(1u);3i=1u}};7 4a(19,z){2u=2Q(G);1j.1x(19==\'1N\'?19:19+\'-2A\');g(19==\'1N\')k 1j.1G(4e(z),2P);d 1C=E.Q();E.1A(E.2K(4b(19)));1j.1G(45(19,1C),2P)};7 45(19,f){k 7(z){g(!j.25&&!1B)1E(19){C\'e\':z[1]=f.m;O;C\'w\':z[1]=f.m;O;C\'n\':z[0]=f.q;O;C\'s\':z[0]=f.q;O}1g 1E(19){C\'e\':z[1]=f.y+1;O;C\'w\':z[1]=f.y+1;O;C\'n\':z[0]=f.x+1;O;C\'s\':z[0]=f.x+1;O}E.1y(z);J.1e()}};7 4e(z){d 2M=z;33.2l();k 7(z){E.2b([z[0]-2M[0],z[1]-2M[1]]);2M=z;J.1e()}};7 4b(T){1E(T){C\'n\':k\'1M\';C\'s\':k\'11\';C\'e\':k\'11\';C\'w\':k\'1s\';C\'1s\':k\'1M\';C\'11\':k\'2e\';C\'2e\':k\'11\';C\'1M\':k\'1s\'}};7 3b(T){k 7(e){1q=M;4a(T,2r(e));e.2k();e.2j();k K}};7 47($G,w,h){d 11=$G.W(),1H=$G.U();g((11>w)&&w>0){11=w;1H=(w/$G.W())*$G.U()}g((1H>h)&&h>0){1H=h;11=(h/$G.U())*$G.W()}1I=$G.W()/11;1Q=$G.U()/1H;$G.W(11).U(1H)};7 2H(c){k{x:1m(c.x*1I),y:1m(c.y*1Q),q:1m(c.q*1I),m:1m(c.m*1Q),w:1m(c.w*1I),h:1m(c.h*1Q)}};7 2P(z){d c=E.Q();g(c.w>j.3n[0]&&c.h>j.3n[1]){J.2t();J.1h()}1g{J.1r()}1j.1x(\'2X\')};7 48(e){1q=M;2u=2Q(G);J.1r();J.1o();46(\'2X\');E.1A(2r(e));1j.1G(4c,2P);33.2l();e.2k();e.2j();k K};7 4c(z){E.1y(z);J.1e()};7 2Z(a){d A=a[0],u=a[1],q=a[2],m=a[3];g(3h)k;d 2s=E.1F(A,u,q,m);d c=E.Q();d 18=2p=[c.x,c.y,c.q,c.m];d 3w=j.3V;d x=18[0];d y=18[1];d q=18[2];d m=18[3];d 3Z=2s[0]-2p[0];d 4m=2s[1]-2p[1];d 4n=2s[2]-2p[2];d 4l=2s[3]-2p[3];d 1c=0;d 4h=j.3X;J.2o(M);d 3u=7(){k 7(){1c+=(V-1c)/4h;18[0]=x+((1c/V)*3Z);18[1]=y+((1c/V)*4m);18[2]=q+((1c/V)*4n);18[3]=m+((1c/V)*4l);g(1c<V)32();1g J.1h();g(1c>=4K.8)1c=V;1d(18)}}();7 32(){4I.4t(3u,3w)};32()};7 1d(l){E.1A([l[0],l[1]]);E.1y([l[2],l[3]]);J.1e()};7 21(F){g(1p(F)!=\'2d\')F={};j=$.4X(j,F);g(1p(j.2D)!==\'7\')j.2D=7(){};g(1p(j.2G)!==\'7\')j.2G=7(){}};7 3m(){k 2H(E.Q())};7 2V(){k E.Q()};7 3E(F){21(F);g(\'1d\'1a F){1d(F.1d);J.1h()}};g(1p(F)!=\'2d\')F={};g(\'1d\'1a F){1d(F.1d);J.1h()}d 2q=j.2z[0]||0;d 2n=j.2z[1]||0;d 2m=j.2O[0]||0;d 2i=j.2O[1]||0;1j.1x(\'2X\');k{2Z:2Z,1d:1d,21:3E,3m:3m,2V:2V}};$.5e.1n=7(j){7 3G(1D){d 4d=j.4R||1D.2N;d I=4P 4S();d 1D=1D;I.50=7(){$(1D).1v().4A(I);1D.1n=$.1n(I,j)};I.2N=4d};g(1p(j)!==\'2d\')j={};1T.4J(7(){g(\'1n\'1a 1T){g(j==\'52\')k 1T.1n;1g 1T.1n.21(j)}1g 3G(1T)});k 1T};',62,325,'|||||||function||||||var|||if|||options|return||y2||||x2||||y1|||||pos|x1|css|case|px|Coords|opt|obj|handle|img|Selection|false|boundy|true|Math|break|left|getFixed|top|boundx|ord|height|100|width|delta|oy|ox||nw|div|yy||xx|position|abs|animat|mode|in|absolute|pcent|setSelect|update|addClass|else|done|hhs|Tracker|aspect|zIndex|parseInt|Jcrop|disableHandles|typeof|btndown|release|ne|pct|type|hide|show|setCursor|setCurrent|opacity|setPressed|aspectLock|fc|from|switch|flipCoords|activateHandlers|nh|xscale|jq|round|cssClass|sw|move|shift_down|append|yscale|awake|trk|this|xsize|rw|insertDragbar|south|insertBorder|rh||setOptions|ysize|bound|keymgr|aspectRatio|trackUp||sel|doNudge|east|moveOffset|keySupport|object|se|watchShift|hdl_holder|nudge|ymin|preventDefault|stopPropagation|watchKeys|xmin|ylimit|animMode|initcr|xlimit|mouseAbs|animto|enableHandles|docOffset|onDone|onMove|trackDocument|offset|maxSize|resize|trackMove|img_holder|onChange|seehandles|borders|onSelect|unscale|updateVisible|li|getCorner|trackDoc|lloc|src|minSize|doneSelect|getPos|xa|mouseup|rebound|hdep|tellScaled|dragDiv|crosshair|createHandles|animateTo|drawBorders|moveHandles|animateStart|KeyManager|img2|midhoriz|trueSize||||midvert|createDragger|rwa|browser|msie|getOffset|makeObj|animating|lastcurs|rha|mousemove|yb|tellSelect|minSelect|ya|cursor|mousedown|xb|refresh|document|animator|tracker|interv|bgOpacity|zi|vline|dragEdges|sideHandles|hline|handleOffset|setOptionsNew|getRect|attachWhenDone|unbind|cornerHandles|insertHandle|moveto|right|real_ratio|handleSize|borderOpacity|handleOpacity|bottom|290|keywrap|bgColor|insertBefore|animationDelay|baseClass|swingSpeed|attr|ix1||onBlur|init_shift|parseKey|cl|dragmodeHandler|myCursor|presize|newSelection|boxWidth|startDragMode|oppLockCorner|selectDrag|loadsrc|createMover|defaults|hidden|velocity|toBack|toFront|boundary|iy2|iy1|ix2|overflow|boxHeight|pageX|pageY|before|setTimeout|max|start|input|370|dragmode|end|after|backgroundColor|jcrop|null|last|holder|450|relative|window|each|99|radio|360|mouseout|30px|new|shiftKey|useImg|Image|ctrlKey|for|north|west|extend|black|focus|onload|edgeMargin|api|handlePad|wrap|300|keyup|keydown|dimmed|310|blur|320|min|keyCode|fn'.split('|'),0,{})) diff --git a/lib/Shorturl_api.php b/lib/Shorturl_api.php index 7beae0ec6..fe106cb83 100644 --- a/lib/Shorturl_api.php +++ b/lib/Shorturl_api.php @@ -19,14 +19,17 @@ if (!defined('LACONICA')) { exit(1); } -class ShortUrlApi { +class ShortUrlApi +{ protected $service_url; - function __construct($service_url) { + function __construct($service_url) + { $this->service_url = $service_url; } - function shorten($url) { + function shorten($url) + { if ($this->is_long($url)) return $this->shorten_imp($url); return $url; } @@ -67,8 +70,10 @@ class ShortUrlApi { } } -class LilUrl extends ShortUrlApi { - function __construct() { +class LilUrl extends ShortUrlApi +{ + function __construct() + { parent::__construct('http://ur1.ca/'); } @@ -85,8 +90,10 @@ class LilUrl extends ShortUrlApi { } -class PtitUrl extends ShortUrlApi { - function __construct() { +class PtitUrl extends ShortUrlApi +{ + function __construct() + { parent::__construct('http://ptiturl.com/?creer=oui&action=Reduire&url='); } @@ -102,8 +109,10 @@ class PtitUrl extends ShortUrlApi { } } -class TightUrl extends ShortUrlApi { - function __construct() { +class TightUrl extends ShortUrlApi +{ + function __construct() + { parent::__construct('http://2tu.us/?save=y&url='); } diff --git a/lib/action.php b/lib/action.php index 7a2461bb5..486b40387 100644 --- a/lib/action.php +++ b/lib/action.php @@ -1,5 +1,5 @@ <?php -/* +/** * Laconica - a distributed open-source microblogging tool * Copyright (C) 2008, Controlez-Vous, Inc. * @@ -17,126 +17,145 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -if (!defined('LACONICA')) { exit(1); } - -class Action { // lawsuit - - var $args; - - function Action() { - } - - # For initializing members of the class - - function prepare($argarray) { - $this->args =& common_copy_args($argarray); - return true; - } - - # For comparison with If-Last-Modified - # If not applicable, return NULL - - function last_modified() { - return NULL; - } - - function etag() { - return NULL; - } - - function is_readonly() { - return false; - } - - function arg($key, $def=NULL) { - if (array_key_exists($key, $this->args)) { - return $this->args[$key]; - } else { - return $def; - } - } - - function trimmed($key, $def=NULL) { - $arg = $this->arg($key, $def); - return (is_string($arg)) ? trim($arg) : $arg; - } - - # Note: argarray ignored, since it's now passed in in prepare() - - function handle($argarray=NULL) { - - $lm = $this->last_modified(); - $etag = $this->etag(); - - if ($etag) { - header('ETag: ' . $etag); - } - - if ($lm) { - header('Last-Modified: ' . date(DATE_RFC1123, $lm)); - $if_modified_since = $_SERVER['HTTP_IF_MODIFIED_SINCE']; - if ($if_modified_since) { - $ims = strtotime($if_modified_since); - if ($lm <= $ims) { - if (!$etag || $this->_has_etag($etag, $_SERVER['HTTP_IF_NONE_MATCH'])) { - header('HTTP/1.1 304 Not Modified'); - # Better way to do this? - exit(0); - } - } - } - } - } - - function _has_etag($etag, $if_none_match) { - return ($if_none_match) && in_array($etag, explode(',', $if_none_match)); - } - - function boolean($key, $def=false) { - $arg = strtolower($this->trimmed($key)); - - if (is_null($arg)) { - return $def; - } else if (in_array($arg, array('true', 'yes', '1'))) { - return true; - } else if (in_array($arg, array('false', 'no', '0'))) { - return false; - } else { - return $def; - } - } - - function server_error($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) { - $action = $this->trimmed('action'); - common_debug("User error '$code' on '$action': $msg", __FILE__); - common_user_error($msg, $code); - } - - function self_url() { - $action = $this->trimmed('action'); - $args = $this->args; - unset($args['action']); - foreach (array_keys($_COOKIE) as $cookie) { - unset($args[$cookie]); - } - return common_local_url($action, $args); - } - - function nav_menu($menu) { +if (!defined('LACONICA')) { + exit(1); +} + +class Action // lawsuit +{ + + var $args; + + function Action() + { + } + + // For initializing members of the class + + function prepare($argarray) + { + $this->args =& common_copy_args($argarray); + return true; + } + + // For comparison with If-Last-Modified + // If not applicable, return null + + function last_modified() + { + return null; + } + + function etag() + { + return null; + } + + function is_readonly() + { + return false; + } + + function arg($key, $def=null) + { + if (array_key_exists($key, $this->args)) { + return $this->args[$key]; + } else { + return $def; + } + } + + function trimmed($key, $def=null) + { + $arg = $this->arg($key, $def); + return (is_string($arg)) ? trim($arg) : $arg; + } + + // Note: argarray ignored, since it's now passed in in prepare() + + function handle($argarray=null) + { + + $lm = $this->last_modified(); + $etag = $this->etag(); + + if ($etag) { + header('ETag: ' . $etag); + } + + if ($lm) { + header('Last-Modified: ' . date(DATE_RFC1123, $lm)); + $if_modified_since = $_SERVER['HTTP_IF_MODIFIED_SINCE']; + if ($if_modified_since) { + $ims = strtotime($if_modified_since); + if ($lm <= $ims) { + if (!$etag || + $this->_has_etag($etag, $_SERVER['HTTP_IF_NONE_MATCH'])) { + header('HTTP/1.1 304 Not Modified'); + // Better way to do this? + exit(0); + } + } + } + } + } + + function _has_etag($etag, $if_none_match) + { + return ($if_none_match) && in_array($etag, explode(',', $if_none_match)); + } + + function boolean($key, $def=false) + { + $arg = strtolower($this->trimmed($key)); + + if (is_null($arg)) { + return $def; + } else if (in_array($arg, array('true', 'yes', '1'))) { + return true; + } else if (in_array($arg, array('false', 'no', '0'))) { + return false; + } else { + return $def; + } + } + + function server_error($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) + { + $action = $this->trimmed('action'); + common_debug("User error '$code' on '$action': $msg", __FILE__); + common_user_error($msg, $code); + } + + function self_url() + { + $action = $this->trimmed('action'); + $args = $this->args; + unset($args['action']); + foreach (array_keys($_COOKIE) as $cookie) { + unset($args[$cookie]); + } + return common_local_url($action, $args); + } + + function nav_menu($menu) + { $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_menu_item(common_local_url($menuaction, + isset($menudesc[2]) ? $menudesc[2] : null), + $menudesc[0], + $menudesc[1], + $action == $menuaction); } common_element_end('ul'); - } + } } diff --git a/lib/common.php b/lib/common.php index 5a28c3091..74c992f1c 100644 --- a/lib/common.php +++ b/lib/common.php @@ -54,100 +54,100 @@ require_once(INSTALLDIR.'/lib/language.php'); $config = array('site' => - array('name' => 'Just another Laconica microblog', - 'server' => 'localhost', - 'theme' => 'default', - 'path' => '/', - 'logfile' => NULL, - 'fancy' => false, - 'locale_path' => INSTALLDIR.'/locale', - 'language' => 'en_US', - 'languages' => get_all_languages(), - 'email' => - array_key_exists('SERVER_ADMIN', $_SERVER) ? $_SERVER['SERVER_ADMIN'] : NULL, - 'broughtby' => NULL, - 'timezone' => 'UTC', - 'broughtbyurl' => NULL, - 'closed' => false, - 'inviteonly' => false, + array('name' => 'Just another Laconica microblog', + 'server' => 'localhost', + 'theme' => 'default', + 'path' => '/', + 'logfile' => null, + 'fancy' => false, + 'locale_path' => INSTALLDIR.'/locale', + 'language' => 'en_US', + 'languages' => get_all_languages(), + 'email' => + array_key_exists('SERVER_ADMIN', $_SERVER) ? $_SERVER['SERVER_ADMIN'] : null, + 'broughtby' => null, + 'timezone' => 'UTC', + 'broughtbyurl' => null, + 'closed' => false, + 'inviteonly' => false, 'private' => false), - 'syslog' => - array('appname' => 'laconica', # for syslog - 'priority' => 'debug'), # XXX: currently ignored - 'queue' => - array('enabled' => false), - 'license' => - array('url' => 'http://creativecommons.org/licenses/by/3.0/', - 'title' => 'Creative Commons Attribution 3.0', - 'image' => 'http://i.creativecommons.org/l/by/3.0/88x31.png'), - 'mail' => - array('backend' => 'mail', - 'params' => NULL), - 'nickname' => - array('blacklist' => array(), - 'featured' => array()), - 'profile' => - array('banned' => array()), - 'avatar' => - array('server' => NULL), - 'public' => - array('localonly' => true, - 'blacklist' => array()), - 'theme' => - array('server' => NULL), - 'throttle' => + 'syslog' => + array('appname' => 'laconica', # for syslog + 'priority' => 'debug'), # XXX: currently ignored + 'queue' => + array('enabled' => false), + 'license' => + array('url' => 'http://creativecommons.org/licenses/by/3.0/', + 'title' => 'Creative Commons Attribution 3.0', + 'image' => 'http://i.creativecommons.org/l/by/3.0/88x31.png'), + 'mail' => + array('backend' => 'mail', + 'params' => null), + 'nickname' => + array('blacklist' => array(), + 'featured' => array()), + 'profile' => + array('banned' => array()), + 'avatar' => + array('server' => null), + 'public' => + array('localonly' => true, + 'blacklist' => array()), + 'theme' => + array('server' => null), + 'throttle' => array('enabled' => false, // whether to throttle edits; false by default 'count' => 20, // number of allowed messages in timespan 'timespan' => 600), // timespan for throttling - 'xmpp' => - array('enabled' => false, - 'server' => 'INVALID SERVER', - 'port' => 5222, - 'user' => 'update', - 'encryption' => true, - 'resource' => 'uniquename', - 'password' => 'blahblahblah', - 'host' => NULL, # only set if != server - 'debug' => false, # print extra debug info - 'public' => array()), # JIDs of users who want to receive the public stream + 'xmpp' => + array('enabled' => false, + 'server' => 'INVALID SERVER', + 'port' => 5222, + 'user' => 'update', + 'encryption' => true, + 'resource' => 'uniquename', + 'password' => 'blahblahblah', + 'host' => null, # only set if != server + 'debug' => false, # print extra debug info + 'public' => array()), # JIDs of users who want to receive the public stream 'sphinx' => array('enabled' => false, 'server' => 'localhost', 'port' => 3312), - 'tag' => - array('dropoff' => 864000.0), - 'popular' => - array('dropoff' => 864000.0), - 'daemon' => - array('piddir' => '/var/run', - 'user' => false, - 'group' => false), - 'integration' => - array('source' => 'Laconica'), # source attribute for Twitter - 'memcached' => - array('enabled' => false, - 'server' => 'localhost', - 'port' => 11211), - 'inboxes' => - array('enabled' => true), # on by default for new sites - ); + 'tag' => + array('dropoff' => 864000.0), + 'popular' => + array('dropoff' => 864000.0), + 'daemon' => + array('piddir' => '/var/run', + 'user' => false, + 'group' => false), + 'integration' => + array('source' => 'Laconica'), # source attribute for Twitter + 'memcached' => + array('enabled' => false, + 'server' => 'localhost', + 'port' => 11211), + 'inboxes' => + array('enabled' => true), # on by default for new sites + ); $config['db'] = &PEAR::getStaticProperty('DB_DataObject','options'); $config['db'] = array('database' => 'YOU HAVE TO SET THIS IN config.php', - 'schema_location' => INSTALLDIR . '/classes', - 'class_location' => INSTALLDIR . '/classes', - 'require_prefix' => 'classes/', - 'class_prefix' => '', - 'mirror' => NULL, + 'schema_location' => INSTALLDIR . '/classes', + 'class_location' => INSTALLDIR . '/classes', + 'require_prefix' => 'classes/', + 'class_prefix' => '', + 'mirror' => null, 'db_driver' => 'DB', # XXX: JanRain libs only work with DB - 'quote_identifiers' => false, - 'type' => 'mysql' ); + 'quote_identifiers' => false, + 'type' => 'mysql' ); if (function_exists('date_default_timezone_set')) { - /* Work internally in UTC */ - date_default_timezone_set('UTC'); + /* Work internally in UTC */ + date_default_timezone_set('UTC'); } require_once(INSTALLDIR.'/config.php'); @@ -163,10 +163,11 @@ require_once(INSTALLDIR.'/lib/subs.php'); require_once(INSTALLDIR.'/lib/Shorturl_api.php'); require_once(INSTALLDIR.'/lib/twitter.php'); -function __autoload($class) { - if ($class == 'OAuthRequest') { - require_once('OAuth.php'); - } else if (file_exists(INSTALLDIR.'/classes/' . $class . '.php')) { +function __autoload($class) +{ + if ($class == 'OAuthRequest') { + require_once('OAuth.php'); + } else if (file_exists(INSTALLDIR.'/classes/' . $class . '.php')) { require_once(INSTALLDIR.'/classes/' . $class . '.php'); } } diff --git a/lib/daemon.php b/lib/daemon.php index 359a4343b..9c1ae50a0 100644 --- a/lib/daemon.php +++ b/lib/daemon.php @@ -1,5 +1,5 @@ <?php -/* +/** * Laconica - a distributed open-source microblogging tool * Copyright (C) 2008, Controlez-Vous, Inc. * @@ -17,117 +17,128 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -if (!defined('LACONICA')) { exit(1); } - -class Daemon { - - function name() { - return NULL; - } - - function background() { - $pid = pcntl_fork(); - if ($pid < 0) { # error - common_log(LOG_ERR, "Could not fork."); - return false; - } else if ($pid > 0) { # parent - common_log(LOG_INFO, "Successfully forked."); - exit(0); - } else { # child - return true; - } - } - - function alreadyRunning() { - - $pidfilename = $this->pidFilename(); - - if (!$pidfilename) { - return false; - } - - if (!file_exists($pidfilename)) { - return false; - } - $contents = file_get_contents($pidfilename); - if (posix_kill(trim($contents),0)) { - return true; - } else { - return false; - } - } - - function writePidFile() { - $pidfilename = $this->pidFilename(); - - if (!$pidfilename) { - return false; - } - - return file_put_contents($pidfilename, posix_getpid() . "\n"); - } - - function clearPidFile() { - $pidfilename = $this->pidFilename(); - if (!$pidfilename) { - return false; - } - return unlink($pidfilename); - } - - function pidFilename() { - $piddir = common_config('daemon', 'piddir'); - if (!$piddir) { - return NULL; - } - $name = $this->name(); - if (!$name) { - return NULL; - } - return $piddir . '/' . $name . '.pid'; - } - - function changeUser() { - - $username = common_config('daemon', 'user'); - - if ($username) { - $user_info = posix_getpwnam($username); - if (!$user_info) { - common_log(LOG_WARNING, 'Ignoring unknown user for daemon: ' . $username); - } else { - common_log(LOG_INFO, "Setting user to " . $username); - posix_setuid($user_info['uid']); - } - } - - $groupname = common_config('daemon', 'group'); - - if ($groupname) { - $group_info = posix_getgrnam($groupname); - if (!$group_info) { - common_log(LOG_WARNING, 'Ignoring unknown group for daemon: ' . $groupname); - } else { - common_log(LOG_INFO, "Setting group to " . $groupname); - posix_setgid($group_info['gid']); - } - } - } - - function runOnce() { - if ($this->alreadyRunning()) { - common_log(LOG_INFO, $this->name() . ' already running. Exiting.'); - exit(0); - } - if ($this->background()) { - $this->writePidFile(); - $this->changeUser(); - $this->run(); - $this->clearPidFile(); - } - } - - function run() { - return true; - } +if (!defined('LACONICA')) { + exit(1); +} + +class Daemon +{ + function name() + { + return null; + } + + function background() + { + $pid = pcntl_fork(); + if ($pid < 0) { // error + common_log(LOG_ERR, "Could not fork."); + return false; + } else if ($pid > 0) { // parent + common_log(LOG_INFO, "Successfully forked."); + exit(0); + } else { // child + return true; + } + } + + function alreadyRunning() + { + $pidfilename = $this->pidFilename(); + + if (!$pidfilename) { + return false; + } + + if (!file_exists($pidfilename)) { + return false; + } + $contents = file_get_contents($pidfilename); + if (posix_kill(trim($contents), 0)) { + return true; + } else { + return false; + } + } + + function writePidFile() + { + $pidfilename = $this->pidFilename(); + + if (!$pidfilename) { + return false; + } + + return file_put_contents($pidfilename, posix_getpid() . "\n"); + } + + function clearPidFile() + { + $pidfilename = $this->pidFilename(); + if (!$pidfilename) { + return false; + } + return unlink($pidfilename); + } + + function pidFilename() + { + $piddir = common_config('daemon', 'piddir'); + if (!$piddir) { + return null; + } + $name = $this->name(); + if (!$name) { + return null; + } + return $piddir . '/' . $name . '.pid'; + } + + function changeUser() + { + $username = common_config('daemon', 'user'); + + if ($username) { + $user_info = posix_getpwnam($username); + if (!$user_info) { + common_log(LOG_WARNING, + 'Ignoring unknown user for daemon: ' . $username); + } else { + common_log(LOG_INFO, "Setting user to " . $username); + posix_setuid($user_info['uid']); + } + } + + $groupname = common_config('daemon', 'group'); + + if ($groupname) { + $group_info = posix_getgrnam($groupname); + if (!$group_info) { + common_log(LOG_WARNING, + 'Ignoring unknown group for daemon: ' . $groupname); + } else { + common_log(LOG_INFO, "Setting group to " . $groupname); + posix_setgid($group_info['gid']); + } + } + } + + function runOnce() + { + if ($this->alreadyRunning()) { + common_log(LOG_INFO, $this->name() . ' already running. Exiting.'); + exit(0); + } + if ($this->background()) { + $this->writePidFile(); + $this->changeUser(); + $this->run(); + $this->clearPidFile(); + } + } + + function run() + { + return true; + } } diff --git a/lib/deleteaction.php b/lib/deleteaction.php index 5ba0e7e44..9e89c0a54 100644 --- a/lib/deleteaction.php +++ b/lib/deleteaction.php @@ -19,43 +19,48 @@ if (!defined('LACONICA')) { exit(1); } -class DeleteAction extends Action { - - function handle($args) { - parent::handle($args); - $user = common_current_user(); - $notice_id = $this->trimmed('notice'); - $notice = Notice::staticGet($notice_id); - if (!$notice) { - common_user_error(_('No such notice.')); - exit; - } - - $profile = $notice->getProfile(); - $user_profile = $user->getProfile(); - - if (!common_logged_in()) { - common_user_error(_('Not logged in.')); - exit; - } else if ($notice->profile_id != $user_profile->id) { - common_user_error(_('Can\'t delete this notice.')); - exit; - } - } - - function show_top($arr=NULL) { - $instr = $this->get_instructions(); - $output = common_markup_to_html($instr); - common_element_start('div', 'instructions'); - common_raw($output); - common_element_end('div'); - } - - function get_title() { - return NULL; - } - - function show_header() { - return; - } +class DeleteAction extends Action +{ + + function handle($args) + { + parent::handle($args); + $user = common_current_user(); + $notice_id = $this->trimmed('notice'); + $notice = Notice::staticGet($notice_id); + if (!$notice) { + common_user_error(_('No such notice.')); + exit; + } + + $profile = $notice->getProfile(); + $user_profile = $user->getProfile(); + + if (!common_logged_in()) { + common_user_error(_('Not logged in.')); + exit; + } else if ($notice->profile_id != $user_profile->id) { + common_user_error(_('Can\'t delete this notice.')); + exit; + } + } + + function show_top($arr=null) + { + $instr = $this->get_instructions(); + $output = common_markup_to_html($instr); + common_element_start('div', 'instructions'); + common_raw($output); + common_element_end('div'); + } + + function get_title() + { + return null; + } + + function show_header() + { + return; + } } diff --git a/lib/facebookaction.php b/lib/facebookaction.php index 87a82ba01..9230bad59 100644 --- a/lib/facebookaction.php +++ b/lib/facebookaction.php @@ -10,274 +10,280 @@ * * 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 + * 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/>. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ if (!defined('LACONICA')) { exit(1); } -require_once(INSTALLDIR.'/extlib/facebook/facebook.php'); - -class FacebookAction extends Action { - - function handle($args) { - parent::handle($args); - } - - function get_facebook() { - $apikey = common_config('facebook', 'apikey'); - $secret = common_config('facebook', 'secret'); - return new Facebook($apikey, $secret); - } - - function update_profile_box($facebook, $fbuid, $user) { - - $notice = $user->getCurrentNotice(); - - # Need to include inline CSS for styling the Profile box - - $style = '<style> - #notices { - clear: both; - margin: 0 auto; - padding: 0; - list-style-type: none; - width: 600px; - border-top: 1px solid #dec5b5; - } - #notices a:hover { - text-decoration: underline; - } - .notice_single { - clear: both; - display: block; - margin: 0; - padding: 5px 5px 5px 0; - min-height: 48px; - font-family: Georgia, "Times New Roman", Times, serif; - font-size: 13px; - line-height: 16px; - border-bottom: 1px solid #dec5b5; - background-color:#FCFFF5; - opacity:1; - } - .notice_single:hover { - background-color: #f7ebcc; - } - .notice_single p { - display: inline; - margin: 0; - padding: 0; - } - </style>'; - - $html = $this->render_notice($notice); - - $fbml = "<fb:wide>$content $html</fb:wide>"; - $fbml .= "<fb:narrow>$content $html</fb:narrow>"; - - $fbml_main = "<fb:narrow>$content $html</fb:narrow>"; - - $facebook->api_client->profile_setFBML(NULL, $fbuid, $fbml, NULL, NULL, $fbml_main); - } - - # Display methods - - function show_header($selected ='Home') { - - # Add a timestamp to the CSS file so Facebook cache wont ignore our changes - $ts = filemtime(theme_file('facebookapp.css')); - $cssurl = theme_path('facebookapp.css') . "?ts=$ts"; - - $header = '<link rel="stylesheet" type="text/css" href="'. $cssurl . '" />'; - # $header .='<script src="" ></script>'; - $header .= '<fb:dashboard/>'; - - $header .= - '<fb:tabs>' - .'<fb:tab-item title="Home" href="index.php" selected="' . ($selected == 'Home') .'" />' - .'<fb:tab-item title="Invite Friends" href="invite.php" selected="' . ($selected == 'Invite') . '" />' - .'<fb:tab-item title="Settings" href="settings.php" selected="' . ($selected == 'Settings') . '" />' - .'</fb:tabs>'; - $header .= '<div id="main_body">'; - - echo $header; - - } - - function show_footer() { - $footer = '</div>'; - echo $footer; - } - - function show_login_form() { - - $loginform = - ' <h2>To add the Identi.ca application, you need to log into your Identi.ca account.</h2>' - .'<a href="http://identi.ca/">' - .' <img src="http://theme.identi.ca/identica/logo.png" alt="Identi.ca" id="logo"/>' - .'</a>' - .'<h1 class="pagetitle">Login</h1>' - .'<div class="instructions">' - .' <p>Login with your username and password. Don\'t have a username yet?' - .' <a href="http://identi.ca/main/register">Register</a> a new account.' - .' </p>' - .'</div>' - .'<div id="content">' - .' <form method="post" id="login">' - .' <p>' - .' <label for="nickname">Nickname</label>' - .' <input name="nickname" type="text" class="input_text" id="nickname"/>' - .' </p>' - .' <p>' - .' <label for="password">Password</label>' - .' <input name="password" type="password" class="password" id="password"/>' - .' </p>' - .' <p>' - .' <input type="submit" id="submit" name="submit" class="submit" value="Login"/>' - .' </p>' - .' </form>' - .' <p>' - .' <a href="http://identi.ca/main/recoverpassword">Lost or forgotten password?</a>' - .' </p>' - .'</div'; - - echo $loginform; - } - - function render_notice($notice) { - - global $config; - - $profile = $notice->getProfile(); - $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE); - - $noticeurl = common_local_url('shownotice', array('notice' => $notice->id)); - - # XXX: we need to figure this out better. Is this right? - if (strcmp($notice->uri, $noticeurl) != 0 && preg_match('/^http/', $notice->uri)) { - $noticeurl = $notice->uri; - } - - $html = - '<li class="notice_single" id="' . $notice->id . '">' - .'<a href="' . $profile->profileurl . '">' - .'<img src="'; - - if ($avatar) { - $html .= common_avatar_display_url($avatar); - } else { - $html .= common_default_avatar(AVATAR_STREAM_SIZE); - } - - $html .= - '" class="avatar stream" width="' - . AVATAR_STREAM_SIZE . '" height="' . AVATAR_STREAM_SIZE .'"' - .' alt="'; - - if ($profile->fullname) { - $html .= $profile->fullname; - } else { - $html .= $profile->nickname; - } - - $html .= - '"></a>' - .'<a href="' . $profile->profileurl . '" class="nickname">' . $profile->nickname . '</a>' - .'<p class="content">' . $notice->rendered . '</p>' - .'<p class="time">' - .'<a class="permalink" href="' . $noticeurl . '" title="' . common_exact_date($notice->created) . '">' . common_date_string($notice->created) . '</a>'; - - if ($notice->source) { - $html .= _(' from '); - $html .= $this->source_link($notice->source); - } - - if ($notice->reply_to) { - $replyurl = common_local_url('shownotice', array('notice' => $notice->reply_to)); - $html .= - ' (<a class="inreplyto" href="' . $replyurl . '">' . _('in reply to...') . ')'; - } - - $html .= '</p></li>'; - - return $html; - } - - function source_link($source) { - $source_name = _($source); - - $html = '<span class="noticesource">'; - - switch ($source) { - case 'web': - case 'xmpp': - case 'mail': - case 'omb': - case 'api': - $html .= $source_name; - break; - default: - $ns = Notice_source::staticGet($source); - if ($ns) { - $html .= '<a href="' . $ns->url . '">' . $ns->name . '</a>'; - } else { - $html .= $source_name; - } - break; - } - - $html .= '</span>'; - - return $html; - } - - function pagination($have_before, $have_after, $page, $fbaction, $args=NULL) { - - $html = ''; - - if ($have_before || $have_after) { - $html = '<div id="pagination">'; - $html .'<ul id="nav_pagination">'; - } - - if ($have_before) { - $pargs = array('page' => $page-1); - $newargs = ($args) ? array_merge($args,$pargs) : $pargs; - $html .= '<li class="before">'; - $html .'<a href="' . $this->pagination_url($fbaction, $newargs) . '">' . _('« After') . '</a>'; - $html .'</li>'; - } - - if ($have_after) { - $pargs = array('page' => $page+1); - $newargs = ($args) ? array_merge($args,$pargs) : $pargs; - $html .= '<li class="after">'; - $html .'<a href="' . $this->pagination_url($fbaction, $newargs) . '">' . _('Before »') . '</a>'; - $html .'</li>'; - } - - if ($have_before || $have_after) { - $html .= '<ul>'; - $html .'<div>'; - } - } - - function pagination_url($fbaction, $args=NULL) { - global $config; - - $extra = ''; - - if ($args) { - foreach ($args as $key => $value) { - $extra .= "&${key}=${value}"; - } - } - - return "$fbaction?${extra}"; - } +require_once(INSTALLDIR.'/lib/facebookutil.php'); + + +class FacebookAction extends Action +{ + + function handle($args) + { + parent::handle($args); + } + + function update_profile_box($facebook, $fbuid, $user) + { + + $notice = $user->getCurrentNotice(); + + # Need to include inline CSS for styling the Profile box + + $style = '<style> + #notices { + clear: both; + margin: 0 auto; + padding: 0; + list-style-type: none; + width: 600px; + border-top: 1px solid #dec5b5; + } + #notices a:hover { + text-decoration: underline; + } + .notice_single { + clear: both; + display: block; + margin: 0; + padding: 5px 5px 5px 0; + min-height: 48px; + font-family: Georgia, "Times New Roman", Times, serif; + font-size: 13px; + line-height: 16px; + border-bottom: 1px solid #dec5b5; + background-color:#FCFFF5; + opacity:1; + } + .notice_single:hover { + background-color: #f7ebcc; + } + .notice_single p { + display: inline; + margin: 0; + padding: 0; + } + </style>'; + + $html = Facebookaction::render_notice($notice); + + + $fbml = "<fb:wide>$style $html</fb:wide>"; + $fbml .= "<fb:narrow>$style $html</fb:narrow>"; + + $fbml_main = "<fb:narrow>$style $html</fb:narrow>"; + + $facebook->api_client->profile_setFBML(null, $fbuid, $fbml, null, null, $fbml_main); + } + + # Display methods + + function show_header($selected ='Home') + { + + # Add a timestamp to the CSS file so Facebook cache wont ignore our changes + $ts = filemtime(theme_file('facebookapp.css')); + $cssurl = theme_path('facebookapp.css') . "?ts=$ts"; + + $header = '<link rel="stylesheet" type="text/css" href="'. $cssurl . '" />'; + # $header .='<script src="" ></script>'; + $header .= '<fb:dashboard/>'; + + $header .= + '<fb:tabs>' + .'<fb:tab-item title="Home" href="index.php" selected="' . ($selected == 'Home') .'" />' + .'<fb:tab-item title="Invite Friends" href="invite.php" selected="' . ($selected == 'Invite') . '" />' + .'<fb:tab-item title="Settings" href="settings.php" selected="' . ($selected == 'Settings') . '" />' + .'</fb:tabs>'; + $header .= '<div id="main_body">'; + + echo $header; + + } + + function show_footer() + { + $footer = '</div>'; + echo $footer; + } + + function show_login_form() + { + + $loginform = + ' <h2>To add the Identi.ca application, you need to log into your Identi.ca account.</h2>' + .'<a href="http://identi.ca/">' + .' <img src="http://theme.identi.ca/identica/logo.png" alt="Identi.ca" id="logo"/>' + .'</a>' + .'<h1 class="pagetitle">Login</h1>' + .'<div class="instructions">' + .' <p>Login with your username and password. Don\'t have a username yet?' + .' <a href="http://identi.ca/main/register">Register</a> a new account.' + .' </p>' + .'</div>' + .'<div id="content">' + .' <form method="post" id="login">' + .' <p>' + .' <label for="nickname">Nickname</label>' + .' <input name="nickname" type="text" class="input_text" id="nickname"/>' + .' </p>' + .' <p>' + .' <label for="password">Password</label>' + .' <input name="password" type="password" class="password" id="password"/>' + .' </p>' + .' <p>' + .' <input type="submit" id="submit" name="submit" class="submit" value="Login"/>' + .' </p>' + .' </form>' + .' <p>' + .' <a href="http://identi.ca/main/recoverpassword">Lost or forgotten password?</a>' + .' </p>' + .'</div'; + + echo $loginform; + } + + function render_notice($notice) + { + + global $config; + + $profile = $notice->getProfile(); + $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE); + + $noticeurl = common_local_url('shownotice', array('notice' => $notice->id)); + + # XXX: we need to figure this out better. Is this right? + if (strcmp($notice->uri, $noticeurl) != 0 && preg_match('/^http/', $notice->uri)) { + $noticeurl = $notice->uri; + } + + $html = + '<li class="notice_single" id="' . $notice->id . '">' + .'<a href="' . $profile->profileurl . '">' + .'<img src="'; + + if ($avatar) { + $html .= common_avatar_display_url($avatar); + } else { + $html .= common_default_avatar(AVATAR_STREAM_SIZE); + } + + $html .= + '" class="avatar stream" width="' + . AVATAR_STREAM_SIZE . '" height="' . AVATAR_STREAM_SIZE .'"' + .' alt="'; + + if ($profile->fullname) { + $html .= $profile->fullname; + } else { + $html .= $profile->nickname; + } + + $html .= + '"></a>' + .'<a href="' . $profile->profileurl . '" class="nickname">' . $profile->nickname . '</a>' + .'<p class="content">' . $notice->rendered . '</p>' + .'<p class="time">' + .'<a class="permalink" href="' . $noticeurl . '" title="' . common_exact_date($notice->created) . '">' . common_date_string($notice->created) . '</a>'; + + if ($notice->source) { + $html .= _(' from '); + $html .= $this->source_link($notice->source); + } + + if ($notice->reply_to) { + $replyurl = common_local_url('shownotice', array('notice' => $notice->reply_to)); + $html .= + ' (<a class="inreplyto" href="' . $replyurl . '">' . _('in reply to...') . ')'; + } + + $html .= '</p></li>'; + + return $html; + } + + function source_link($source) + { + $source_name = _($source); + + $html = '<span class="noticesource">'; + + switch ($source) { + case 'web': + case 'xmpp': + case 'mail': + case 'omb': + case 'api': + $html .= $source_name; + break; + default: + $ns = Notice_source::staticGet($source); + if ($ns) { + $html .= '<a href="' . $ns->url . '">' . $ns->name . '</a>'; + } else { + $html .= $source_name; + } + break; + } + + $html .= '</span>'; + + return $html; + } + + function pagination($have_before, $have_after, $page, $fbaction, $args=null) + { + + $html = ''; + + if ($have_before || $have_after) { + $html = '<div id="pagination">'; + $html .'<ul id="nav_pagination">'; + } + + if ($have_before) { + $pargs = array('page' => $page-1); + $newargs = ($args) ? array_merge($args,$pargs) : $pargs; + $html .= '<li class="before">'; + $html .'<a href="' . $this->pagination_url($fbaction, $newargs) . '">' . _('« After') . '</a>'; + $html .'</li>'; + } + + if ($have_after) { + $pargs = array('page' => $page+1); + $newargs = ($args) ? array_merge($args,$pargs) : $pargs; + $html .= '<li class="after">'; + $html .'<a href="' . $this->pagination_url($fbaction, $newargs) . '">' . _('Before »') . '</a>'; + $html .'</li>'; + } + + if ($have_before || $have_after) { + $html .= '<ul>'; + $html .'<div>'; + } + } + + function pagination_url($fbaction, $args=null) + { + global $config; + + $extra = ''; + + if ($args) { + foreach ($args as $key => $value) { + $extra .= "&${key}=${value}"; + } + } + + return "$fbaction?${extra}"; + } } diff --git a/lib/facebookutil.php b/lib/facebookutil.php new file mode 100644 index 000000000..fc0e41e5b --- /dev/null +++ b/lib/facebookutil.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/>. + */ + +require_once(INSTALLDIR.'/extlib/facebook/facebook.php'); + +// Gets all the notices from users with a Facebook link since a given ID +function get_facebook_notices($since) +{ + $qry = 'SELECT notice.* ' . + 'FROM notice ' . + 'JOIN foreign_link ' . + 'WHERE notice.profile_id = foreign_link.user_id ' . + 'AND foreign_link.service = 2'; + + // XXX: What should the limit be? + return Notice::getStreamDirect($qry, 0, 100, 0, 0, null, $since); +} + +function get_facebook() +{ + $apikey = common_config('facebook', 'apikey'); + $secret = common_config('facebook', 'secret'); + return new Facebook($apikey, $secret); +} diff --git a/lib/gallery.php b/lib/gallery.php index 0dd351bab..34b58518c 100644 --- a/lib/gallery.php +++ b/lib/gallery.php @@ -1,6 +1,5 @@ <?php - -/* +/** * Laconica - a distributed open-source microblogging tool * Copyright (C) 2008, Controlez-Vous, Inc. * @@ -18,303 +17,330 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -if (!defined('LACONICA')) { exit(1); } +if (!defined('LACONICA')) { + exit(1); +} -require_once(INSTALLDIR.'/lib/profilelist.php'); +require_once INSTALLDIR.'/lib/profilelist.php'; -# 10x8 +// 10x8 define('AVATARS_PER_PAGE', 80); -class GalleryAction extends Action { - - function is_readonly() { - return true; - } - - function handle($args) { - parent::handle($args); - - # Post from the tag dropdown; redirect to a GET - - if ($_SERVER['REQUEST_METHOD'] == 'POST') { - common_redirect($this->self_url(), 307); - } - - $nickname = common_canonical_nickname($this->arg('nickname')); - $user = User::staticGet('nickname', $nickname); - - if (!$user) { - $this->no_such_user(); - return; - } - - $profile = $user->getProfile(); - - if (!$profile) { - $this->server_error(_('User without matching profile in system.')); - return; - } - - $page = $this->arg('page'); - - if (!$page) { - $page = 1; - } - - $display = $this->arg('display'); - - if (!$display) { - $display = 'list'; - } - - $tag = $this->arg('tag'); - - common_show_header($profile->nickname . ": " . $this->gallery_type(), - NULL, $profile, - array($this, 'show_top')); - - $this->display_links($profile, $page, $display); - $this->show_tags_dropdown($profile); - - $this->show_gallery($profile, $page, $display, $tag); - common_show_footer(); - } - - function no_such_user() { - $this->client_error(_('No such user.')); - } - - function show_tags_dropdown($profile) { - $tag = $this->trimmed('tag'); - list($lst, $usr) = $this->fields(); - $tags = $this->get_all_tags($profile, $lst, $usr); - $content = array(); - foreach ($tags as $t) { - $content[$t] = $t; - } - if ($tags) { - common_element_start('dl', array('id'=>'filter_tags')); - common_element('dt', null, _('Filter tags')); - common_element_start('dd'); - common_element_start('ul'); - common_element_start('li', array('id'=>'filter_tags_all', 'class'=>'child_1')); - common_element('a', array('href' => common_local_url($this->trimmed('action'), - array('nickname' => $profile->nickname))), - _('All')); - common_element_end('li'); - common_element_start('li', array('id'=>'filter_tags_item')); - common_element_start('form', array('name' => 'bytag', 'id' => 'bytag', 'method' => 'post')); - common_dropdown('tag', _('Tag'), $content, - _('Choose a tag to narrow list'), FALSE, $tag); - common_submit('go', _('Go')); - common_element_end('form'); - common_element_end('li'); - common_element_end('ul'); - common_element_end('dd'); - common_element_end('dl'); - } - } - - function show_top($profile) { - common_element('div', 'instructions', - $this->get_instructions($profile)); +class GalleryAction extends Action +{ + function is_readonly() + { + return true; + } + + function handle($args) + { + parent::handle($args); + + // Post from the tag dropdown; redirect to a GET + + if ($_SERVER['REQUEST_METHOD'] == 'POST') { + common_redirect($this->self_url(), 307); + } + + $nickname = common_canonical_nickname($this->arg('nickname')); + + $user = User::staticGet('nickname', $nickname); + + if (!$user) { + $this->no_such_user(); + return; + } + + $profile = $user->getProfile(); + + if (!$profile) { + $this->server_error(_('User without matching profile in system.')); + return; + } + + $page = $this->arg('page'); + + if (!$page) { + $page = 1; + } + + $display = $this->arg('display'); + + if (!$display) { + $display = 'list'; + } + + $tag = $this->arg('tag'); + + common_show_header($profile->nickname . ": " . $this->gallery_type(), + null, $profile, + array($this, 'show_top')); + + $this->display_links($profile, $page, $display); + $this->show_tags_dropdown($profile); + + $this->show_gallery($profile, $page, $display, $tag); + common_show_footer(); + } + + function no_such_user() + { + $this->client_error(_('No such user.')); + } + + function show_tags_dropdown($profile) + { + $tag = $this->trimmed('tag'); + + list($lst, $usr) = $this->fields(); + + $tags = $this->get_all_tags($profile, $lst, $usr); + + $content = array(); + + foreach ($tags as $t) { + $content[$t] = $t; + } + if ($tags) { + common_element_start('dl', array('id'=>'filter_tags')); + common_element('dt', null, _('Filter tags')); + common_element_start('dd'); + common_element_start('ul'); + common_element_start('li', array('id' => 'filter_tags_all', + 'class' => 'child_1')); + common_element('a', + array('href' => + common_local_url($this->trimmed('action'), + array('nickname' => + $profile->nickname))), + _('All')); + common_element_end('li'); + common_element_start('li', array('id'=>'filter_tags_item')); + common_element_start('form', array('name' => 'bytag', + 'id' => 'bytag', + 'method' => 'post')); + common_dropdown('tag', _('Tag'), $content, + _('Choose a tag to narrow list'), false, $tag); + common_submit('go', _('Go')); + common_element_end('form'); + common_element_end('li'); + common_element_end('ul'); + common_element_end('dd'); + common_element_end('dl'); + } + } + + function show_top($profile) + { + common_element('div', 'instructions', + $this->get_instructions($profile)); $this->show_menu(); - } - - function show_menu() { - # action => array('prompt', 'title', $args) - $action = $this->trimmed('action'); - $nickname = $this->trimmed('nickname'); - $menu = - array('subscriptions' => - array( _('Subscriptions'), - _('Subscriptions'), - array('nickname' => $nickname)), - 'subscribers' => - array( - _('Subscribers'), - _('Subscribers'), - array('nickname' => $nickname)), - ); - $this->nav_menu($menu); - } + } - function show_gallery($profile, $page, $display='list', $tag=NULL) { + function show_menu() + { + // action => array('prompt', 'title', $args) + $action = $this->trimmed('action'); + $nickname = $this->trimmed('nickname'); + + $menu = + array('subscriptions' => + array( _('Subscriptions'), + _('Subscriptions'), + array('nickname' => $nickname)), + 'subscribers' => + array( + _('Subscribers'), + _('Subscribers'), + array('nickname' => $nickname)), + ); + $this->nav_menu($menu); + } - $other = new Profile(); + function show_gallery($profile, $page, $display='list', $tag=null) + { + $other = new Profile(); - list($lst, $usr) = $this->fields(); + list($lst, $usr) = $this->fields(); - $per_page = ($display == 'list') ? PROFILES_PER_PAGE : AVATARS_PER_PAGE; + $per_page = ($display == 'list') ? PROFILES_PER_PAGE : AVATARS_PER_PAGE; - $offset = ($page-1)*$per_page; - $limit = $per_page + 1; + $offset = ($page-1)*$per_page; + $limit = $per_page + 1; - if (common_config('db','type') == 'pgsql') { - $lim = ' LIMIT ' . $limit . ' OFFSET ' . $offset; - } else { - $lim = ' LIMIT ' . $offset . ', ' . $limit; - } + if (common_config('db', 'type') == 'pgsql') { + $lim = ' LIMIT ' . $limit . ' OFFSET ' . $offset; + } else { + $lim = ' LIMIT ' . $offset . ', ' . $limit; + } - # XXX: memcached results - # FIXME: SQL injection on $tag + // XXX: memcached results + // FIXME: SQL injection on $tag - $other->query('SELECT profile.* ' . - 'FROM profile JOIN subscription ' . - 'ON profile.id = subscription.' . $lst . ' ' . - (($tag) ? 'JOIN profile_tag ON (profile.id = profile_tag.tagged AND subscription.'.$usr.'= profile_tag.tagger) ' : '') . - 'WHERE ' . $usr . ' = ' . $profile->id . ' ' . - 'AND subscriber != subscribed ' . - (($tag) ? 'AND profile_tag.tag= "' . $tag . '" ': '') . - 'ORDER BY subscription.created DESC, profile.id DESC ' . - $lim); + $other->query('SELECT profile.* ' . + 'FROM profile JOIN subscription ' . + 'ON profile.id = subscription.' . $lst . ' ' . + (($tag) ? 'JOIN profile_tag ON (profile.id = profile_tag.tagged AND subscription.'.$usr.'= profile_tag.tagger) ' : '') . + 'WHERE ' . $usr . ' = ' . $profile->id . ' ' . + 'AND subscriber != subscribed ' . + (($tag) ? 'AND profile_tag.tag= "' . $tag . '" ': '') . + 'ORDER BY subscription.created DESC, profile.id DESC ' . + $lim); - if ($display == 'list') { + if ($display == 'list') { $cls = $this->profile_list_class(); - $profile_list = new $cls($other, $profile, $this->trimmed('action')); - $cnt = $profile_list->show_list(); - } else { - $cnt = $this->icon_list($other); - } + $profile_list = new $cls($other, $profile, $this->trimmed('action')); + $cnt = $profile_list->show_list(); + } else { + $cnt = $this->icon_list($other); + } - # For building the pagination URLs + // For building the pagination URLs - $args = array('nickname' => $profile->nickname); + $args = array('nickname' => $profile->nickname); - if ($display != 'list') { - $args['display'] = $display; - } + if ($display != 'list') { + $args['display'] = $display; + } - common_pagination($page > 1, - $cnt > $per_page, - $page, - $this->trimmed('action'), - $args); - } + common_pagination($page > 1, + $cnt > $per_page, + $page, + $this->trimmed('action'), + $args); + } - function profile_list_class() { + function profile_list_class() + { return 'ProfileList'; } - function icon_list($other) { - - common_element_start('ul', $this->div_class()); - - $cnt = 0; - - while ($other->fetch()) { - - $cnt++; - - if ($cnt > AVATARS_PER_PAGE) { - break; - } - - common_element_start('li'); - - common_element_start('a', array('title' => ($other->fullname) ? - $other->fullname : - $other->nickname, - 'href' => $other->profileurl, - 'class' => 'subscription')); - $avatar = $other->getAvatar(AVATAR_STREAM_SIZE); - common_element('img', - array('src' => - (($avatar) ? common_avatar_display_url($avatar) : - common_default_avatar(AVATAR_STREAM_SIZE)), - 'width' => AVATAR_STREAM_SIZE, - 'height' => AVATAR_STREAM_SIZE, - 'class' => 'avatar stream', - 'alt' => ($other->fullname) ? - $other->fullname : - $other->nickname)); - common_element_end('a'); - - # XXX: subscribe form here - - common_element_end('li'); - } - - common_element_end('ul'); - - return $cnt; - } - - function gallery_type() { - return NULL; - } - - function get_instructions(&$profile) { - return NULL; - } - - function fields() { - return NULL; - } - - function div_class() { - return ''; - } - - function display_links($profile, $page, $display) { - $tag = $this->trimmed('tag'); - - common_element_start('dl', array('id'=>'subscriptions_nav')); - common_element('dt', null, _('Subscriptions navigation')); - common_element_start('dd'); - common_element_start('ul', array('class'=>'nav')); - - switch ($display) { - case 'list': - common_element('li', array('class'=>'child_1'), _('List')); - common_element_start('li'); - $url_args = array('display' => 'icons', - 'nickname' => $profile->nickname, - 'page' => 1 + floor((($page - 1) * PROFILES_PER_PAGE) / AVATARS_PER_PAGE)); - if ($tag) { - $url_args['tag'] = $tag; - } - $url = common_local_url($this->trimmed('action'), $url_args); - common_element('a', array('href' => $url), - _('Icons')); - common_element_end('li'); - break; - default: - common_element_start('li', array('class'=>'child_1')); - $url_args = array('nickname' => $profile->nickname, - 'page' => 1 + floor((($page - 1) * AVATARS_PER_PAGE) / PROFILES_PER_PAGE)); - if ($tag) { - $url_args['tag'] = $tag; - } - $url = common_local_url($this->trimmed('action'), $url_args); - common_element('a', array('href' => $url), - _('List')); - common_element_end('li'); - common_element('li', NULL, _('Icons')); - break; - } - - common_element_end('ul'); - common_element_end('dd'); - common_element_end('dl'); - } - - # Get list of tags we tagged other users with - - function get_all_tags($profile, $lst, $usr) { - $profile_tag = new Notice_tag(); - $profile_tag->query('SELECT DISTINCT(tag) ' . - 'FROM profile_tag, subscription ' . - 'WHERE tagger = ' . $profile->id . ' ' . - 'AND ' . $usr . ' = ' . $profile->id . ' ' . - 'AND ' . $lst . ' = tagged ' . - 'AND tagger != tagged'); - $tags = array(); - while ($profile_tag->fetch()) { - $tags[] = $profile_tag->tag; - } - $profile_tag->free(); - return $tags; - } + function icon_list($other) + { + common_element_start('ul', $this->div_class()); + + $cnt = 0; + + while ($other->fetch()) { + + $cnt++; + + if ($cnt > AVATARS_PER_PAGE) { + break; + } + + common_element_start('li'); + + common_element_start('a', array('title' => ($other->fullname) ? + $other->fullname : + $other->nickname, + 'href' => $other->profileurl, + 'class' => 'subscription')); + $avatar = $other->getAvatar(AVATAR_STREAM_SIZE); + common_element('img', + array('src' => + (($avatar) ? common_avatar_display_url($avatar) : + common_default_avatar(AVATAR_STREAM_SIZE)), + 'width' => AVATAR_STREAM_SIZE, + 'height' => AVATAR_STREAM_SIZE, + 'class' => 'avatar stream', + 'alt' => ($other->fullname) ? + $other->fullname : + $other->nickname)); + common_element_end('a'); + + // XXX: subscribe form here + + common_element_end('li'); + } + + common_element_end('ul'); + + return $cnt; + } + + function gallery_type() + { + return null; + } + + function get_instructions(&$profile) + { + return null; + } + + function fields() + { + return null; + } + + function div_class() + { + return ''; + } + + function display_links($profile, $page, $display) + { + $tag = $this->trimmed('tag'); + + common_element_start('dl', array('id'=>'subscriptions_nav')); + common_element('dt', null, _('Subscriptions navigation')); + common_element_start('dd'); + common_element_start('ul', array('class'=>'nav')); + + switch ($display) { + case 'list': + common_element('li', array('class'=>'child_1'), _('List')); + common_element_start('li'); + $url_args = array('display' => 'icons', + 'nickname' => $profile->nickname, + 'page' => 1 + floor((($page - 1) * PROFILES_PER_PAGE) / AVATARS_PER_PAGE)); + if ($tag) { + $url_args['tag'] = $tag; + } + $url = common_local_url($this->trimmed('action'), $url_args); + common_element('a', array('href' => $url), + _('Icons')); + common_element_end('li'); + break; + default: + common_element_start('li', array('class'=>'child_1')); + $url_args = array('nickname' => $profile->nickname, + 'page' => 1 + floor((($page - 1) * AVATARS_PER_PAGE) / PROFILES_PER_PAGE)); + if ($tag) { + $url_args['tag'] = $tag; + } + $url = common_local_url($this->trimmed('action'), $url_args); + common_element('a', array('href' => $url), + _('List')); + common_element_end('li'); + common_element('li', null, _('Icons')); + break; + } + + common_element_end('ul'); + common_element_end('dd'); + common_element_end('dl'); + } + + // Get list of tags we tagged other users with + + function get_all_tags($profile, $lst, $usr) + { + $profile_tag = new Notice_tag(); + $profile_tag->query('SELECT DISTINCT(tag) ' . + 'FROM profile_tag, subscription ' . + 'WHERE tagger = ' . $profile->id . ' ' . + 'AND ' . $usr . ' = ' . $profile->id . ' ' . + 'AND ' . $lst . ' = tagged ' . + 'AND tagger != tagged'); + $tags = array(); + while ($profile_tag->fetch()) { + $tags[] = $profile_tag->tag; + } + $profile_tag->free(); + return $tags; + } }
\ No newline at end of file diff --git a/lib/jabber.php b/lib/jabber.php index ab0fd6af8..099ded9eb 100644 --- a/lib/jabber.php +++ b/lib/jabber.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 + * utility functions for Jabber/GTalk/XMPP messages + * + * 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,286 +18,448 @@ * * 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 Network + * @package Laconica + * @author Evan Prodromou <evan@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); } +if (!defined('LACONICA')) { + exit(1); +} -require_once('XMPPHP/XMPP.php'); +require_once 'XMPPHP/XMPP.php'; -function jabber_valid_base_jid($jid) { - # Cheap but effective - return Validate::email($jid); -} +/** + * checks whether a string is a syntactically valid Jabber ID (JID) + * + * @param string $jid string to check + * + * @return boolean whether the string is a valid JID + */ -function jabber_normalize_jid($jid) { - if (preg_match("/(?:([^\@]+)\@)?([^\/]+)(?:\/(.*))?$/", $jid, $matches)) { - $node = $matches[1]; - $server = $matches[2]; - return strtolower($node.'@'.$server); - } else { - return NULL; - } +function jabber_valid_base_jid($jid) +{ + // Cheap but effective + return Validate::email($jid); } -function jabber_daemon_address() { - return common_config('xmpp', 'user') . '@' . common_config('xmpp', 'server'); +/** + * normalizes a Jabber ID for comparison + * + * @param string $jid JID to check + * + * @return string an equivalent JID in normalized (lowercase) form + */ + +function jabber_normalize_jid($jid) +{ + if (preg_match("/(?:([^\@]+)\@)?([^\/]+)(?:\/(.*))?$/", $jid, $matches)) { + $node = $matches[1]; + $server = $matches[2]; + return strtolower($node.'@'.$server); + } else { + return null; + } } -function jabber_connect($resource=NULL) { - static $conn = NULL; - if (!$conn) { - $conn = new XMPPHP_XMPP(common_config('xmpp', 'host') ? - common_config('xmpp', 'host') : - common_config('xmpp', 'server'), - common_config('xmpp', 'port'), - common_config('xmpp', 'user'), - common_config('xmpp', 'password'), - ($resource) ? $resource : - common_config('xmpp', 'resource'), - common_config('xmpp', 'server'), - common_config('xmpp', 'debug') ? - true : false, - common_config('xmpp', 'debug') ? - XMPPHP_Log::LEVEL_VERBOSE : NULL - ); - - if (!$conn) { - return false; - } - - $conn->autoSubscribe(); - $conn->useEncryption(common_config('xmpp', 'encryption')); - - try { - $conn->connect(true); # true = persistent connection - } catch (XMPPHP_Exception $e) { - common_log(LOG_ERROR, $e->getMessage()); - return false; - } - - $conn->processUntil('session_start'); - } - return $conn; +/** + * the JID of the Jabber daemon for this Laconica instance + * + * @return string JID of the Jabber daemon + */ + +function jabber_daemon_address() +{ + return common_config('xmpp', 'user') . '@' . common_config('xmpp', 'server'); } -function jabber_send_notice($to, $notice) { - $conn = jabber_connect(); - if (!$conn) { - return false; - } - $profile = Profile::staticGet($notice->profile_id); - if (!$profile) { - common_log(LOG_WARNING, 'Refusing to send notice with ' . - 'unknown profile ' . common_log_objstring($notice), - __FILE__); - return false; - } - $msg = jabber_format_notice($profile, $notice); - $entry = jabber_format_entry($profile, $notice); - $conn->message($to, $msg, 'chat', NULL, $entry); - $profile->free(); - return true; +/** + * connect the configured Jabber account to the configured server + * + * @param string $resource Resource to connect (defaults to configured resource) + * + * @return XMPPHP connection to the configured server + */ + +function jabber_connect($resource=null) +{ + static $conn = null; + if (!$conn) { + $conn = new XMPPHP_XMPP(common_config('xmpp', 'host') ? + common_config('xmpp', 'host') : + common_config('xmpp', 'server'), + common_config('xmpp', 'port'), + common_config('xmpp', 'user'), + common_config('xmpp', 'password'), + ($resource) ? $resource : + common_config('xmpp', 'resource'), + common_config('xmpp', 'server'), + common_config('xmpp', 'debug') ? + true : false, + common_config('xmpp', 'debug') ? + XMPPHP_Log::LEVEL_VERBOSE : null + ); + + if (!$conn) { + return false; + } + + $conn->autoSubscribe(); + $conn->useEncryption(common_config('xmpp', 'encryption')); + + try { + $conn->connect(true); // true = persistent connection + } catch (XMPPHP_Exception $e) { + common_log(LOG_ERROR, $e->getMessage()); + return false; + } + + $conn->processUntil('session_start'); + } + return $conn; } -# Extra stuff defined by Twitter, needed by twitter clients - -function jabber_format_entry($profile, $notice) { - - # FIXME: notice url might be remote - - $noticeurl = common_local_url('shownotice', - array('notice' => $notice->id)); - $msg = jabber_format_notice($profile, $notice); - $entry = "\n<entry xmlns='http://www.w3.org/2005/Atom'>\n"; - $entry .= "<source>\n"; - $entry .= "<title>" . $profile->nickname . " - " . common_config('site', 'name') . "</title>\n"; - $entry .= "<link href='" . htmlspecialchars($profile->profileurl) . "'/>\n"; - $entry .= "<link rel='self' type='application/rss+xml' href='" . common_local_url('userrss', array('nickname' => $profile->nickname)) . "'/>\n"; - $entry .= "<author><name>" . $profile->nickname . "</name></author>\n"; - $entry .= "<icon>" . common_profile_avatar_url($profile, AVATAR_PROFILE_SIZE) . "</icon>\n"; - $entry .= "</source>\n"; - $entry .= "<title>" . htmlspecialchars($msg) . "</title>\n"; - $entry .= "<summary>" . htmlspecialchars($msg) . "</summary>\n"; - $entry .= "<link rel='alternate' href='" . $noticeurl . "' />\n"; - $entry .= "<id>". $notice->uri . "</id>\n"; - $entry .= "<published>".common_date_w3dtf($notice->created)."</published>\n"; - $entry .= "<updated>".common_date_w3dtf($notice->modified)."</updated>\n"; - $entry .= "</entry>\n"; - - $html = "\n<html xmlns='http://jabber.org/protocol/xhtml-im'>\n"; - $html .= "<body xmlns='http://www.w3.org/1999/xhtml'>\n"; - $html .= "<a href='".htmlspecialchars($profile->profileurl)."'>".$profile->nickname."</a>: "; - $html .= ($notice->rendered) ? $notice->rendered : common_render_content($notice->content, $notice); - $html .= "\n</body>\n"; - $html .= "\n</html>\n"; - - $address = "<addresses xmlns='http://jabber.org/protocol/address'>\n"; - $address .= "<address type='replyto' jid='" . jabber_daemon_address() . "' />\n"; - $address .= "</addresses>\n"; - - # FIXME: include a pubsub event, too. - - return $html . $entry . $address; +/** + * send a single notice to a given Jabber address + * + * @param string $to JID to send the notice to + * @param Notice $notice notice to send + * + * @return boolean success value + */ + +function jabber_send_notice($to, $notice) +{ + $conn = jabber_connect(); + if (!$conn) { + return false; + } + $profile = Profile::staticGet($notice->profile_id); + if (!$profile) { + common_log(LOG_WARNING, 'Refusing to send notice with ' . + 'unknown profile ' . common_log_objstring($notice), + __FILE__); + return false; + } + $msg = jabber_format_notice($profile, $notice); + $entry = jabber_format_entry($profile, $notice); + $conn->message($to, $msg, 'chat', null, $entry); + $profile->free(); + return true; } -function jabber_send_message($to, $body, $type='chat', $subject=NULL) { - $conn = jabber_connect(); - if (!$conn) { - return false; - } - $conn->message($to, $body, $type, $subject); - return true; +/** + * extra information for XMPP messages, as defined by Twitter + * + * @param Profile $profile Profile of the sending user + * @param Notice $notice Notice being sent + * + * @return string Extra information (Atom, HTML, addresses) in string format + */ + +function jabber_format_entry($profile, $notice) +{ + // FIXME: notice url might be remote + + $noticeurl = common_local_url('shownotice', + array('notice' => $notice->id)); + + $msg = jabber_format_notice($profile, $notice); + + $self_url = common_local_url('userrss', array('nickname' => $profile->nickname)); + + $entry = "\n<entry xmlns='http://www.w3.org/2005/Atom'>\n"; + $entry .= "<source>\n"; + $entry .= "<title>" . $profile->nickname . " - " . common_config('site', 'name') . "</title>\n"; + $entry .= "<link href='" . htmlspecialchars($profile->profileurl) . "'/>\n"; + $entry .= "<link rel='self' type='application/rss+xml' href='" . $self_url . "'/>\n"; + $entry .= "<author><name>" . $profile->nickname . "</name></author>\n"; + $entry .= "<icon>" . common_profile_avatar_url($profile, AVATAR_PROFILE_SIZE) . "</icon>\n"; + $entry .= "</source>\n"; + $entry .= "<title>" . htmlspecialchars($msg) . "</title>\n"; + $entry .= "<summary>" . htmlspecialchars($msg) . "</summary>\n"; + $entry .= "<link rel='alternate' href='" . $noticeurl . "' />\n"; + $entry .= "<id>". $notice->uri . "</id>\n"; + $entry .= "<published>".common_date_w3dtf($notice->created)."</published>\n"; + $entry .= "<updated>".common_date_w3dtf($notice->modified)."</updated>\n"; + $entry .= "</entry>\n"; + + $html = "\n<html xmlns='http://jabber.org/protocol/xhtml-im'>\n"; + $html .= "<body xmlns='http://www.w3.org/1999/xhtml'>\n"; + $html .= "<a href='".htmlspecialchars($profile->profileurl)."'>".$profile->nickname."</a>: "; + $html .= ($notice->rendered) ? $notice->rendered : common_render_content($notice->content, $notice); + $html .= "\n</body>\n"; + $html .= "\n</html>\n"; + + $address = "<addresses xmlns='http://jabber.org/protocol/address'>\n"; + $address .= "<address type='replyto' jid='" . jabber_daemon_address() . "' />\n"; + $address .= "</addresses>\n"; + + // FIXME: include a pubsub event, too. + + return $html . $entry . $address; } -function jabber_send_presence($status, $show='available', $to=NULL, - $type = 'available', $priority=NULL) +/** + * sends a single text message to a given JID + * + * @param string $to JID to send the message to + * @param string $body body of the message + * @param string $type type of the message + * @param string $subject subject of the message + * + * @return boolean success flag + */ + +function jabber_send_message($to, $body, $type='chat', $subject=null) { - $conn = jabber_connect(); - if (!$conn) { - return false; - } - $conn->presence($status, $show, $to, $type, $priority); - return true; + $conn = jabber_connect(); + if (!$conn) { + return false; + } + $conn->message($to, $body, $type, $subject); + return true; } -function jabber_confirm_address($code, $nickname, $address) { - $body = 'User "' . $nickname . '" on ' . common_config('site', 'name') . ' ' . - 'has said that your Jabber ID belongs to them. ' . - 'If that\'s true, you can confirm by clicking on this URL: ' . - common_local_url('confirmaddress', array('code' => $code)) . - ' . (If you cannot click it, copy-and-paste it into the ' . - 'address bar of your browser). If that user isn\'t you, ' . - 'or if you didn\'t request this confirmation, just ignore this message.'; +/** + * sends a presence stanza on the Jabber network + * + * @param string $status current status, free-form string + * @param string $show structured status value + * @param string $to recipient of presence, null for general + * @param string $type type of status message, related to $show + * @param int $priority priority of the presence + * + * @return boolean success value + */ - return jabber_send_message($address, $body); +function jabber_send_presence($status, $show='available', $to=null, + $type = 'available', $priority=null) +{ + $conn = jabber_connect(); + if (!$conn) { + return false; + } + $conn->presence($status, $show, $to, $type, $priority); + return true; } -function jabber_special_presence($type, $to=NULL, $show=NULL, $status=NULL) { - $conn = jabber_connect(); - - $to = htmlspecialchars($to); - $status = htmlspecialchars($status); - $out = "<presence"; - if($to) $out .= " to='$to'"; - if($type) $out .= " type='$type'"; - if($show == 'available' and !$status) { - $out .= "/>"; - } else { - $out .= ">"; - if($show && ($show != 'available')) $out .= "<show>$show</show>"; - if($status) $out .= "<status>$status</status>"; - $out .= "</presence>"; - } - $conn->send($out); +/** + * sends a confirmation request to a JID + * + * @param string $code confirmation code for confirmation URL + * @param string $nickname nickname of confirming user + * @param string $address JID to send confirmation to + * + * @return boolean success flag + */ + +function jabber_confirm_address($code, $nickname, $address) +{ + $body = 'User "' . $nickname . '" on ' . common_config('site', 'name') . ' ' . + 'has said that your Jabber ID belongs to them. ' . + 'If that\'s true, you can confirm by clicking on this URL: ' . + common_local_url('confirmaddress', array('code' => $code)) . + ' . (If you cannot click it, copy-and-paste it into the ' . + 'address bar of your browser). If that user isn\'t you, ' . + 'or if you didn\'t request this confirmation, just ignore this message.'; + + return jabber_send_message($address, $body); } -function jabber_broadcast_notice($notice) { - - if (!common_config('xmpp', 'enabled')) { - return true; - } - $profile = Profile::staticGet($notice->profile_id); - - if (!$profile) { - common_log(LOG_WARNING, 'Refusing to broadcast notice with ' . - 'unknown profile ' . common_log_objstring($notice), - __FILE__); - return false; - } - - $msg = jabber_format_notice($profile, $notice); - $entry = jabber_format_entry($profile, $notice); - - $profile->free(); - unset($profile); - - $sent_to = array(); - $conn = jabber_connect(); - - # First, get users to whom this is a direct reply - $user = new User(); - $user->query('SELECT user.id, user.jabber ' . - 'FROM user JOIN reply ON user.id = reply.profile_id ' . - 'WHERE reply.notice_id = ' . $notice->id . ' ' . - 'AND user.jabber is not null ' . - 'AND user.jabbernotify = 1 ' . - 'AND user.jabberreplies = 1 '); - - while ($user->fetch()) { - common_log(LOG_INFO, - 'Sending reply notice ' . $notice->id . ' to ' . $user->jabber, - __FILE__); - $conn->message($user->jabber, $msg, 'chat', NULL, $entry); - $conn->processTime(0); - $sent_to[$user->id] = 1; - } - - $user->free(); - - # Now, get users subscribed to this profile - - $user = new User(); - $user->query('SELECT user.id, user.jabber ' . - 'FROM user JOIN subscription ON user.id = subscription.subscriber ' . - 'WHERE subscription.subscribed = ' . $notice->profile_id . ' ' . - 'AND user.jabber is not null ' . - 'AND user.jabbernotify = 1 ' . - 'AND subscription.jabber = 1 '); +/** + * sends a "special" presence stanza on the Jabber network + * + * @param string $type Type of presence + * @param string $to JID to send presence to + * @param string $show show value for presence + * @param string $status status value for presence + * + * @return boolean success flag + * + * @see jabber_send_presence() + */ - while ($user->fetch()) { - if (!array_key_exists($user->id, $sent_to)) { - common_log(LOG_INFO, - 'Sending notice ' . $notice->id . ' to ' . $user->jabber, - __FILE__); - $conn->message($user->jabber, $msg, 'chat', NULL, $entry); - # To keep the incoming queue from filling up, we service it after each send. - $conn->processTime(0); - } - } +function jabber_special_presence($type, $to=null, $show=null, $status=null) +{ + // FIXME: why use this instead of jabber_send_presence()? + $conn = jabber_connect(); + + $to = htmlspecialchars($to); + $status = htmlspecialchars($status); + + $out = "<presence"; + if ($to) { + $out .= " to='$to'"; + } + if ($type) { + $out .= " type='$type'"; + } + if ($show == 'available' and !$status) { + $out .= "/>"; + } else { + $out .= ">"; + if ($show && ($show != 'available')) { + $out .= "<show>$show</show>"; + } + if ($status) { + $out .= "<status>$status</status>"; + } + $out .= "</presence>"; + } + $conn->send($out); +} - $user->free(); +/** + * broadcast a notice to all subscribers and reply recipients + * + * This function will send a notice to all subscribers on the local server + * who have Jabber addresses, and have Jabber notification enabled, and + * have this subscription enabled for Jabber. It also sends the notice to + * all recipients of @-replies who have Jabber addresses and Jabber notification + * enabled. This is really the heart of Jabber distribution in Laconica. + * + * @param Notice $notice The notice to broadcast + * + * @return boolean success flag + */ - return true; +function jabber_broadcast_notice($notice) +{ + if (!common_config('xmpp', 'enabled')) { + return true; + } + $profile = Profile::staticGet($notice->profile_id); + + if (!$profile) { + common_log(LOG_WARNING, 'Refusing to broadcast notice with ' . + 'unknown profile ' . common_log_objstring($notice), + __FILE__); + return false; + } + + $msg = jabber_format_notice($profile, $notice); + $entry = jabber_format_entry($profile, $notice); + + $profile->free(); + unset($profile); + + $sent_to = array(); + + $conn = jabber_connect(); + + // First, get users to whom this is a direct reply + $user = new User(); + $user->query('SELECT user.id, user.jabber ' . + 'FROM user JOIN reply ON user.id = reply.profile_id ' . + 'WHERE reply.notice_id = ' . $notice->id . ' ' . + 'AND user.jabber is not null ' . + 'AND user.jabbernotify = 1 ' . + 'AND user.jabberreplies = 1 '); + + while ($user->fetch()) { + common_log(LOG_INFO, + 'Sending reply notice ' . $notice->id . ' to ' . $user->jabber, + __FILE__); + $conn->message($user->jabber, $msg, 'chat', null, $entry); + $conn->processTime(0); + $sent_to[$user->id] = 1; + } + + $user->free(); + + // Now, get users subscribed to this profile + + $user = new User(); + $user->query('SELECT user.id, user.jabber ' . + 'FROM user JOIN subscription ' . + 'ON user.id = subscription.subscriber ' . + 'WHERE subscription.subscribed = ' . $notice->profile_id . ' ' . + 'AND user.jabber is not null ' . + 'AND user.jabbernotify = 1 ' . + 'AND subscription.jabber = 1 '); + + while ($user->fetch()) { + if (!array_key_exists($user->id, $sent_to)) { + common_log(LOG_INFO, + 'Sending notice ' . $notice->id . ' to ' . $user->jabber, + __FILE__); + $conn->message($user->jabber, $msg, 'chat', null, $entry); + // To keep the incoming queue from filling up, + // we service it after each send. + $conn->processTime(0); + } + } + + $user->free(); + + return true; } -function jabber_public_notice($notice) { +/** + * send a notice to all public listeners + * + * For notices that are generated on the local system (by users), we can optionally + * forward them to remote listeners by XMPP. + * + * @param Notice $notice notice to broadcast + * + * @return boolean success flag + */ - # Now, users who want everything +function jabber_public_notice($notice) +{ + // Now, users who want everything - $public = common_config('xmpp', 'public'); + $public = common_config('xmpp', 'public'); - # FIXME PRIV don't send out private messages here - # XXX: should we send out non-local messages if public,localonly - # = false? I think not + // FIXME PRIV don't send out private messages here + // XXX: should we send out non-local messages if public,localonly + // = false? I think not - if ($public && $notice->is_local) { - $profile = Profile::staticGet($notice->profile_id); + if ($public && $notice->is_local) { + $profile = Profile::staticGet($notice->profile_id); - if (!$profile) { - common_log(LOG_WARNING, 'Refusing to broadcast notice with ' . - 'unknown profile ' . common_log_objstring($notice), - __FILE__); - return false; - } + if (!$profile) { + common_log(LOG_WARNING, 'Refusing to broadcast notice with ' . + 'unknown profile ' . common_log_objstring($notice), + __FILE__); + return false; + } - $msg = jabber_format_notice($profile, $notice); - $entry = jabber_format_entry($profile, $notice); + $msg = jabber_format_notice($profile, $notice); + $entry = jabber_format_entry($profile, $notice); - $conn = jabber_connect(); + $conn = jabber_connect(); - foreach ($public as $address) { - common_log(LOG_INFO, - 'Sending notice ' . $notice->id . ' to public listener ' . $address, - __FILE__); - $conn->message($address, $msg, 'chat', NULL, $entry); - $conn->processTime(0); - } - $profile->free(); - } + foreach ($public as $address) { + common_log(LOG_INFO, + 'Sending notice ' . $notice->id . + ' to public listener ' . $address, + __FILE__); + $conn->message($address, $msg, 'chat', null, $entry); + $conn->processTime(0); + } + $profile->free(); + } - return true; + return true; } -function jabber_format_notice(&$profile, &$notice) { - return $profile->nickname . ': ' . $notice->content; +/** + * makes a plain-text formatted version of a notice, suitable for Jabber distribution + * + * @param Profile &$profile profile of the sending user + * @param Notice &$notice notice being sent + * + * @return string plain-text version of the notice, with user nickname prefixed + */ + +function jabber_format_notice(&$profile, &$notice) +{ + return $profile->nickname . ': ' . $notice->content; } diff --git a/lib/language.php b/lib/language.php index 796e28870..1d00cc31e 100644 --- a/lib/language.php +++ b/lib/language.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 + * utility functions for i18n + * + * 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,79 +18,135 @@ * * 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 I18n + * @package Laconica + * @author Matthew Gregg <matthew.gregg@gmail.com> + * @author Ciaran Gultnieks <ciaran@ciarang.com> + * @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/ */ -if (!defined('LACONICA')) { exit(1); } +if (!defined('LACONICA')) { + exit(1); +} +/** + * Content negotiation for language codes + * + * @param string $httplang HTTP Accept-Language header + * + * @return string language code for best language match + */ +function client_prefered_language($httplang) +{ + $client_langs = array(); -function client_prefered_language($httplang) { - $client_langs = array(); - $all_languages = common_config('site','languages'); + $all_languages = common_config('site', 'languages'); - preg_match_all('"(((\S\S)-?(\S\S)?)(;q=([0-9.]+))?)\s*(,\s*|$)"',strtolower($httplang),$httplang); - for ($i = 0; $i < count($httplang); $i++) { - if(!empty($httplang[2][$i])) { - #if no q default to 1.0 - $client_langs[$httplang[2][$i]] = ($httplang[6][$i]? (float) $httplang[6][$i] : 1.0); - } - if(!empty($httplang[3][$i]) && empty($client_langs[$httplang[3][$i]])) { - #if a catchall default 0.01 lower - $client_langs[$httplang[3][$i]] = ($httplang[6][$i]? (float) $httplang[6][$i]-0.01 : 0.99); - } - } - #sort in decending q - arsort($client_langs); + preg_match_all('"(((\S\S)-?(\S\S)?)(;q=([0-9.]+))?)\s*(,\s*|$)"', + strtolower($httplang), $httplang); - foreach ($client_langs as $lang => $q) { - if (isset($all_languages[$lang])) { - return($all_languages[$lang]['lang']); - } - } - return FALSE; -} + for ($i = 0; $i < count($httplang); $i++) { + if (!empty($httplang[2][$i])) { + // if no q default to 1.0 + $client_langs[$httplang[2][$i]] = + ($httplang[6][$i]? (float) $httplang[6][$i] : 1.0); + } + if (!empty($httplang[3][$i]) && empty($client_langs[$httplang[3][$i]])) { + // if a catchall default 0.01 lower + $client_langs[$httplang[3][$i]] = + ($httplang[6][$i]? (float) $httplang[6][$i]-0.01 : 0.99); + } + } + // sort in decending q + arsort($client_langs); -function get_nice_language_list() { - $nice_lang = array(); - $all_languages = common_config('site','languages'); - foreach ($all_languages as $lang) { - $nice_lang = $nice_lang + array($lang['lang'] => $lang['name']); + foreach ($client_langs as $lang => $q) { + if (isset($all_languages[$lang])) { + return($all_languages[$lang]['lang']); } - return $nice_lang; + } + return false; } -// Get a list of all languages that are enabled in the default config. This -// should ONLY be called when setting up the default config in common.php. -// Any other attempt to get a list of lanugages should instead call -// common_config('site','languages') -function get_all_languages() { - return array( - 'en-us' => array('q' => 1, 'lang' => 'en_US', 'name' => 'English (US)', 'direction' => 'ltr'), - 'en-nz' => array('q' => 1, 'lang' => 'en_NZ', 'name' => 'English (NZ)', 'direction' => 'ltr'), - 'en-gb' => array('q' => 1, 'lang' => 'en_GB', 'name' => 'English (British)', 'direction' => 'ltr'), - 'en' => array('q' => 1, 'lang' => 'en', 'name' => 'English', 'direction' => 'ltr'), - 'da' => array('q' => 0.1, 'lang' => 'da_DK', 'name' => 'Danish', 'direction' => 'ltr'), - 'nl' => array('q' => 1, 'lang' => 'nl_NL', 'name' => 'Dutch', 'direction' => 'ltr'), - 'eo' => array('q' => 0.1, 'lang' => 'eo', 'name' => 'Esperanto', 'direction' => 'ltr'), - 'fr-fr' => array('q' => 0.9, 'lang' => 'fr_FR', 'name' => 'French', 'direction' => 'ltr'), - 'de' => array('q' => 1, 'lang' => 'de_DE', 'name' => 'German', 'direction' => 'ltr'), - 'it' => array('q' => 1, 'lang' => 'it_IT', 'name' => 'Italian', 'direction' => 'ltr'), - 'ko' => array('q' => 0.1, 'lang' => 'ko', 'name' => 'Korean', 'direction' => 'ltr'), - 'nb' => array('q' => 1, 'lang' => 'nb_NO', 'name' => 'Norwegian (bokmal)', 'direction' => 'ltr'), - 'pt' => array('q' => 0.2, 'lang' => 'pt', 'name' => 'Portuguese', 'direction' => 'ltr'), - 'pt-br' => array('q' => 1, 'lang' => 'pt_BR', 'name' => 'Portuguese Brazil', 'direction' => 'ltr'), -# 'ru' => array('q' => 0.1, 'lang' => 'ru_RU', 'name' => 'Russian', 'direction' => 'ltr'), - 'es' => array('q' => 1, 'lang' => 'es', 'name' => 'Spanish', 'direction' => 'ltr'), - 'tr' => array('q' => 1, 'lang' => 'tr_TR', 'name' => 'Turkish', 'direction' => 'ltr'), - 'uk' => array('q' => 1, 'lang' => 'uk_UA', 'name' => 'Ukrainian', 'direction' => 'ltr'), -# 'lt' => array('q' => 0.1, 'lang' => 'lt_LT', 'name' => 'Lithuanian', 'direction' => 'ltr'), -# 'sv' => array('q' => 1, 'lang' => 'sv_SE', 'name' => 'Swedish', 'direction' => 'ltr'), - 'pl' => array('q' => 1, 'lang' => 'pl_PL', 'name' => 'Polish', 'direction' => 'ltr'), - 'mk' => array('q' => 1, 'lang' => 'mk_MK', 'name' => 'Macedonian', 'direction' => 'ltr'), - 'jp' => array('q' => 0.1, 'lang' => 'ja_JP', 'name' => 'Japanese', 'direction' => 'ltr'), - 'cs' => array('q' => 1, 'lang' => 'cs_CZ', 'name' => 'Czech', 'direction' => 'ltr'), - 'ca' => array('q' => 1, 'lang' => 'ca_ES', 'name' => 'Catalan', 'direction' => 'ltr'), -# 'hr' => array('q' => 0.1, 'lang' => 'he_IL', 'name' => 'Hebrew', 'direction' => 'ltr') - ); +/** + * returns a simple code -> name mapping for languages + * + * @return array map of available languages by code to language name. + */ + +function get_nice_language_list() +{ + $nice_lang = array(); + + $all_languages = common_config('site', 'languages'); + + foreach ($all_languages as $lang) { + $nice_lang = $nice_lang + array($lang['lang'] => $lang['name']); + } + return $nice_lang; +} + +/** + * Get a list of all languages that are enabled in the default config + * + * This should ONLY be called when setting up the default config in common.php. + * Any other attempt to get a list of lanugages should instead call + * common_config('site','languages') + * + * @return array mapping of language codes to language info + */ + +function get_all_languages() +{ + return + array('en-us' => array('q' => 1, 'lang' => 'en_US', + 'name' => 'English (US)', 'direction' => 'ltr'), + 'en-nz' => array('q' => 1, 'lang' => 'en_NZ', + 'name' => 'English (NZ)', 'direction' => 'ltr'), + 'en-gb' => array('q' => 1, 'lang' => 'en_GB', + 'name' => 'English (British)', 'direction' => 'ltr'), + 'en' => array('q' => 1, 'lang' => 'en', + 'name' => 'English', 'direction' => 'ltr'), + 'da' => array('q' => 0.1, 'lang' => 'da_DK', + 'name' => 'Danish', 'direction' => 'ltr'), + 'nl' => array('q' => 1, 'lang' => 'nl_NL', + 'name' => 'Dutch', 'direction' => 'ltr'), + 'eo' => array('q' => 0.1, 'lang' => 'eo', + 'name' => 'Esperanto', 'direction' => 'ltr'), + 'fr-fr' => array('q' => 0.9, 'lang' => 'fr_FR', + 'name' => 'French', 'direction' => 'ltr'), + 'de' => array('q' => 1, 'lang' => 'de_DE', + 'name' => 'German', 'direction' => 'ltr'), + 'it' => array('q' => 1, 'lang' => 'it_IT', + 'name' => 'Italian', 'direction' => 'ltr'), + 'ko' => array('q' => 0.1, 'lang' => 'ko', + 'name' => 'Korean', 'direction' => 'ltr'), + 'nb' => array('q' => 1, 'lang' => 'nb_NO', + 'name' => 'Norwegian (bokmal)', 'direction' => 'ltr'), + 'pt' => array('q' => 0.2, 'lang' => 'pt', + 'name' => 'Portuguese', 'direction' => 'ltr'), + 'pt-br' => array('q' => 1, 'lang' => 'pt_BR', + 'name' => 'Portuguese Brazil', 'direction' => 'ltr'), + 'es' => array('q' => 1, 'lang' => 'es', + 'name' => 'Spanish', 'direction' => 'ltr'), + 'tr' => array('q' => 1, 'lang' => 'tr_TR', + 'name' => 'Turkish', 'direction' => 'ltr'), + 'uk' => array('q' => 1, 'lang' => 'uk_UA', + 'name' => 'Ukrainian', 'direction' => 'ltr'), + 'pl' => array('q' => 1, 'lang' => 'pl_PL', + 'name' => 'Polish', 'direction' => 'ltr'), + 'mk' => array('q' => 1, 'lang' => 'mk_MK', + 'name' => 'Macedonian', 'direction' => 'ltr'), + 'jp' => array('q' => 0.1, 'lang' => 'ja_JP', + 'name' => 'Japanese', 'direction' => 'ltr'), + 'cs' => array('q' => 1, 'lang' => 'cs_CZ', + 'name' => 'Czech', 'direction' => 'ltr'), + 'ca' => array('q' => 1, 'lang' => 'ca_ES', + 'name' => 'Catalan', 'direction' => 'ltr'), + ); } diff --git a/lib/mail.php b/lib/mail.php index a7cbab858..5638ae9bf 100644 --- a/lib/mail.php +++ b/lib/mail.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 + * utilities for sending email + * + * 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,295 +18,557 @@ * * 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 Mail + * @package Laconica + * @author Evan Prodromou <evan@controlyourself.ca> + * @author Zach Copley <zach@controlyourself.ca> + * @author Robin Millette <millette@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); } +if (!defined('LACONICA')) { + exit(1); +} -require_once('Mail.php'); +require_once 'Mail.php'; -function mail_backend() { - static $backend = NULL; +/** + * return the configured mail backend + * + * Uses the $config array to make a mail backend. Cached so it is safe to call + * more than once. + * + * @return Mail backend + */ - if (!$backend) { - global $config; - $backend = Mail::factory($config['mail']['backend'], - ($config['mail']['params']) ? $config['mail']['params'] : array()); - if (PEAR::isError($backend)) { - common_server_error($backend->getMessage(), 500); - } - } - return $backend; +function mail_backend() +{ + static $backend = null; + + if (!$backend) { + global $config; + $backend = Mail::factory($config['mail']['backend'], + ($config['mail']['params']) ? + $config['mail']['params'] : + array()); + if (PEAR::isError($backend)) { + common_server_error($backend->getMessage(), 500); + } + } + return $backend; } -# XXX: use Mail_Queue... maybe +/** + * send an email to one or more recipients + * + * @param array $recipients array of strings with email addresses of recipients + * @param array $headers array mapping strings to strings for email headers + * @param string $body body of the email + * + * @return boolean success flag + */ -function mail_send($recipients, $headers, $body) { - $backend = mail_backend(); +function mail_send($recipients, $headers, $body) +{ + // XXX: use Mail_Queue... maybe + $backend = mail_backend(); if (!isset($headers['Content-Type'])) { $headers['Content-Type'] = 'text/plain; charset=UTF-8'; } - assert($backend); # throws an error if it's bad - $sent = $backend->send($recipients, $headers, $body); - if (PEAR::isError($sent)) { - common_log(LOG_ERR, 'Email error: ' . $sent->getMessage()); - return false; - } - return true; + assert($backend); // throws an error if it's bad + $sent = $backend->send($recipients, $headers, $body); + if (PEAR::isError($sent)) { + common_log(LOG_ERR, 'Email error: ' . $sent->getMessage()); + return false; + } + return true; } -function mail_domain() { - $maildomain = common_config('mail', 'domain'); - if (!$maildomain) { - $maildomain = common_config('site', 'server'); - } - return $maildomain; -} +/** + * returns the configured mail domain + * + * Defaults to the server name. + * + * @return string mail domain, suitable for making email addresses. + */ -function mail_notify_from() { - $notifyfrom = common_config('mail', 'notifyfrom'); - if (!$notifyfrom) { - $domain = mail_domain(); - $notifyfrom = common_config('site', 'name') .' <noreply@'.$domain.'>'; - } - return $notifyfrom; +function mail_domain() +{ + $maildomain = common_config('mail', 'domain'); + if (!$maildomain) { + $maildomain = common_config('site', 'server'); + } + return $maildomain; } -function mail_to_user(&$user, $subject, $body, $address=NULL) { - if (!$address) { - $address = $user->email; - } +/** + * returns a good address for sending email from this server + * + * Uses either the configured value or a faked-up value made + * from the mail domain. + * + * @return string notify from address + */ - $recipients = $address; - $profile = $user->getProfile(); +function mail_notify_from() +{ + $notifyfrom = common_config('mail', 'notifyfrom'); - $headers['From'] = mail_notify_from(); - $headers['To'] = $profile->getBestName() . ' <' . $address . '>'; - $headers['Subject'] = $subject; + if (!$notifyfrom) { - return mail_send($recipients, $headers, $body); + $domain = mail_domain(); + + $notifyfrom = common_config('site', 'name') .' <noreply@'.$domain.'>'; + } + + return $notifyfrom; } -# For confirming a Jabber address +/** + * sends email to a user + * + * @param User &$user user to send email to + * @param string $subject subject of the email + * @param string $body body of the email + * @param string $address optional specification of email address + * + * @return boolean success flag + */ -function mail_confirm_address($user, $code, $nickname, $address) { +function mail_to_user(&$user, $subject, $body, $address=null) +{ + if (!$address) { + $address = $user->email; + } + + $recipients = $address; + $profile = $user->getProfile(); - $subject = _('Email address confirmation'); + $headers['From'] = mail_notify_from(); + $headers['To'] = $profile->getBestName() . ' <' . $address . '>'; + $headers['Subject'] = $subject; - $body = sprintf(_("Hey, %s.\n\nSomeone just entered this email address on %s.\n\n" . - "If it was you, and you want to confirm your entry, use the URL below:\n\n\t%s\n\n" . - "If not, just ignore this message.\n\nThanks for your time, \n%s\n") - , $nickname, common_config('site', 'name') - , common_local_url('confirmaddress', array('code' => $code)), common_config('site', 'name')); - return mail_to_user($user, $subject, $body, $address); + return mail_send($recipients, $headers, $body); } -function mail_subscribe_notify($listenee, $listener) { - $other = $listener->getProfile(); - mail_subscribe_notify_profile($listenee, $other); +/** + * Send an email to confirm a user's control of an email address + * + * @param User $user User claiming the email address + * @param string $code Confirmation code + * @param string $nickname Nickname of user + * @param string $address email address to confirm + * + * @see common_confirmation_code() + * + * @return success flag + */ + +function mail_confirm_address($user, $code, $nickname, $address) +{ + $subject = _('Email address confirmation'); + + $body = sprintf(_("Hey, %s.\n\n". + "Someone just entered this email address on %s.\n\n" . + "If it was you, and you want to confirm your entry, ". + "use the URL below:\n\n\t%s\n\n" . + "If not, just ignore this message.\n\n". + "Thanks for your time, \n%s\n"), + $nickname, common_config('site', 'name'), + common_local_url('confirmaddress', array('code' => $code)), + common_config('site', 'name')); + return mail_to_user($user, $subject, $body, $address); } -function mail_subscribe_notify_profile($listenee, $other) { - if ($listenee->email && $listenee->emailnotifysub) { - // use the recipients localization +/** + * notify a user of subscription by another user + * + * This is just a wrapper around the profile-based version. + * + * @param User $listenee user who is being subscribed to + * @param User $listener user who is subscribing + * + * @see mail_subscribe_notify_profile() + * + * @return void + */ + +function mail_subscribe_notify($listenee, $listener) +{ + $other = $listener->getProfile(); + mail_subscribe_notify_profile($listenee, $other); +} + +/** + * notify a user of subscription by a profile (remote or local) + * + * This function checks to see if the listenee has an email + * address and wants subscription notices. + * + * @param User $listenee user who's being subscribed to + * @param Profile $other profile of person who's listening + * + * @return void + */ + +function mail_subscribe_notify_profile($listenee, $other) +{ + if ($listenee->email && $listenee->emailnotifysub) { + + // use the recipient's localization common_init_locale($listenee->language); - $profile = $listenee->getProfile(); - $name = $profile->getBestName(); - $long_name = ($other->fullname) ? ($other->fullname . ' (' . $other->nickname . ')') : $other->nickname; - $recipients = $listenee->email; - $headers['From'] = mail_notify_from(); - $headers['To'] = $name . ' <' . $listenee->email . '>'; - $headers['Subject'] = sprintf(_('%1$s is now listening to your notices on %2$s.'), $other->getBestName(), - common_config('site', 'name')); - $body = sprintf(_('%1$s is now listening to your notices on %2$s.'."\n\n". - "\t".'%3$s'."\n\n". - 'Faithfully yours,'."\n".'%4$s.'."\n"), - $long_name, - common_config('site', 'name'), - $other->profileurl, - common_config('site', 'name')); + + $profile = $listenee->getProfile(); + + $name = $profile->getBestName(); + + $long_name = ($other->fullname) ? + ($other->fullname . ' (' . $other->nickname . ')') : $other->nickname; + + $recipients = $listenee->email; + + $headers['From'] = mail_notify_from(); + $headers['To'] = $name . ' <' . $listenee->email . '>'; + $headers['Subject'] = sprintf(_('%1$s is now listening to '. + 'your notices on %2$s.'), + $other->getBestName(), + common_config('site', 'name')); + + $body = sprintf(_('%1$s is now listening to your notices on %2$s.'."\n\n". + "\t".'%3$s'."\n\n". + '%4$s'. + '%5$s'. + '%6$s'. + "\n".'Faithfully yours,'."\n".'%7$s.'."\n\n". + "----\n". + "Change your email address or ". + "notification options at %8$s\n"), + $long_name, + common_config('site', 'name'), + $other->profileurl, + ($other->location) ? + sprintf(_("Location: %s\n"), $other->location) : '', + ($other->homepage) ? + sprintf(_("Homepage: %s\n"), $other->homepage) : '', + ($other->bio) ? + sprintf(_("Bio: %s\n\n"), $other->bio) : '', + common_config('site', 'name'), + common_local_url('emailsettings')); // reset localization common_init_locale(); - mail_send($recipients, $headers, $body); - } + mail_send($recipients, $headers, $body); + } } -function mail_new_incoming_notify($user) { +/** + * notify a user of their new incoming email address + * + * User's email and incoming fields should already be updated. + * + * @param User $user user with the new address + * + * @return void + */ - $profile = $user->getProfile(); - $name = $profile->getBestName(); +function mail_new_incoming_notify($user) +{ + $profile = $user->getProfile(); - $headers['From'] = $user->incomingemail; - $headers['To'] = $name . ' <' . $user->email . '>'; - $headers['Subject'] = sprintf(_('New email address for posting to %s'), - common_config('site', 'name')); + $name = $profile->getBestName(); - $body = sprintf(_("You have a new posting address on %1\$s.\n\n". - "Send email to %2\$s to post new messages.\n\n". - "More email instructions at %3\$s.\n\n". - "Faithfully yours,\n%4\$s"), - common_config('site', 'name'), - $user->incomingemail, - common_local_url('doc', array('title' => 'email')), - common_config('site', 'name')); + $headers['From'] = $user->incomingemail; + $headers['To'] = $name . ' <' . $user->email . '>'; + $headers['Subject'] = sprintf(_('New email address for posting to %s'), + common_config('site', 'name')); - mail_send($user->email, $headers, $body); + $body = sprintf(_("You have a new posting address on %1\$s.\n\n". + "Send email to %2\$s to post new messages.\n\n". + "More email instructions at %3\$s.\n\n". + "Faithfully yours,\n%4\$s"), + common_config('site', 'name'), + $user->incomingemail, + common_local_url('doc', array('title' => 'email')), + common_config('site', 'name')); + + mail_send($user->email, $headers, $body); } -function mail_new_incoming_address() { - $prefix = common_confirmation_code(64); - $suffix = mail_domain(); - return $prefix . '@' . $suffix; +/** + * generate a new address for incoming messages + * + * @todo check the database for uniqueness + * + * @return string new email address for incoming messages + */ + +function mail_new_incoming_address() +{ + $prefix = common_confirmation_code(64); + $suffix = mail_domain(); + return $prefix . '@' . $suffix; } -function mail_broadcast_notice_sms($notice) { +/** + * broadcast a notice to all subscribers with SMS notification on + * + * This function sends SMS messages to all users who have sms addresses; + * have sms notification on; and have sms enabled for this particular + * subscription. + * + * @param Notice $notice The notice to broadcast + * + * @return success flag + */ - # Now, get users subscribed to this profile +function mail_broadcast_notice_sms($notice) +{ + // Now, get users subscribed to this profile - $user = new User(); + $user = new User(); - $user->query('SELECT nickname, smsemail, incomingemail ' . - 'FROM user JOIN subscription ' . - 'ON user.id = subscription.subscriber ' . - 'WHERE subscription.subscribed = ' . $notice->profile_id . ' ' . - 'AND user.smsemail IS NOT NULL ' . - 'AND user.smsnotify = 1 ' . + $user->query('SELECT nickname, smsemail, incomingemail ' . + 'FROM user JOIN subscription ' . + 'ON user.id = subscription.subscriber ' . + 'WHERE subscription.subscribed = ' . $notice->profile_id . ' ' . + 'AND user.smsemail IS NOT null ' . + 'AND user.smsnotify = 1 ' . 'AND subscription.sms = 1 '); - while ($user->fetch()) { - common_log(LOG_INFO, - 'Sending notice ' . $notice->id . ' to ' . $user->smsemail, - __FILE__); - $success = mail_send_sms_notice_address($notice, $user->smsemail, $user->incomingemail); - if (!$success) { - # XXX: Not sure, but I think that's the right thing to do - common_log(LOG_WARNING, - 'Sending notice ' . $notice->id . ' to ' . $user->smsemail . ' FAILED, cancelling.', - __FILE__); - return false; - } - } - - $user->free(); - unset($user); - - return true; + while ($user->fetch()) { + common_log(LOG_INFO, + 'Sending notice ' . $notice->id . ' to ' . $user->smsemail, + __FILE__); + $success = mail_send_sms_notice_address($notice, + $user->smsemail, + $user->incomingemail); + if (!$success) { + // XXX: Not sure, but I think that's the right thing to do + common_log(LOG_WARNING, + 'Sending notice ' . $notice->id . ' to ' . + $user->smsemail . ' FAILED, cancelling.', + __FILE__); + return false; + } + } + + $user->free(); + unset($user); + + return true; } -function mail_send_sms_notice($notice, $user) { - return mail_send_sms_notice_address($notice, $user->smsemail, $user->incomingemail); +/** + * send a notice to a user via SMS + * + * A convenience wrapper around mail_send_sms_notice_address() + * + * @param Notice $notice notice to send + * @param User $user user to receive notice + * + * @see mail_send_sms_notice_address() + * + * @return boolean success flag + */ + +function mail_send_sms_notice($notice, $user) +{ + return mail_send_sms_notice_address($notice, + $user->smsemail, + $user->incomingemail); } -function mail_send_sms_notice_address($notice, $smsemail, $incomingemail) { +/** + * send a notice to an SMS email address from a given address + * + * We use the user's incoming email address as the "From" address to make + * replying to notices easier. + * + * @param Notice $notice notice to send + * @param string $smsemail email address to send to + * @param string $incomingemail email address to set as 'from' + * + * @return boolean success flag + */ + +function mail_send_sms_notice_address($notice, $smsemail, $incomingemail) +{ + $to = $nickname . ' <' . $smsemail . '>'; - $to = $nickname . ' <' . $smsemail . '>'; - $other = $notice->getProfile(); + $other = $notice->getProfile(); - common_log(LOG_INFO, "Sending notice " . $notice->id . " to " . $smsemail, __FILE__); + common_log(LOG_INFO, 'Sending notice ' . $notice->id . + ' to ' . $smsemail, __FILE__); - $headers = array(); - $headers['From'] = (isset($incomingemail)) ? $incomingemail : mail_notify_from(); - $headers['To'] = $to; - $headers['Subject'] = sprintf(_('%s status'), - $other->getBestName()); - $body = $notice->content; + $headers = array(); - return mail_send($smsemail, $headers, $body); + $headers['From'] = ($incomingemail) ? $incomingemail : mail_notify_from(); + $headers['To'] = $to; + $headers['Subject'] = sprintf(_('%s status'), + $other->getBestName()); + + $body = $notice->content; + + return mail_send($smsemail, $headers, $body); } -function mail_confirm_sms($code, $nickname, $address) { +/** + * send a message to confirm a claim for an SMS number + * + * @param string $code confirmation code + * @param string $nickname nickname of user claiming number + * @param string $address email address to send the confirmation to + * + * @see common_confirmation_code() + * + * @return void + */ + +function mail_confirm_sms($code, $nickname, $address) +{ + $recipients = $address; - $recipients = $address; + $headers['From'] = mail_notify_from(); + $headers['To'] = $nickname . ' <' . $address . '>'; + $headers['Subject'] = _('SMS confirmation'); - $headers['From'] = mail_notify_from(); - $headers['To'] = $nickname . ' <' . $address . '>'; - $headers['Subject'] = _('SMS confirmation'); + // FIXME: I18N - $body = "$nickname: confirm you own this phone number with this code:"; - $body .= "\n\n"; - $body .= $code; - $body .= "\n\n"; + $body = "$nickname: confirm you own this phone number with this code:"; + $body .= "\n\n"; + $body .= $code; + $body .= "\n\n"; - mail_send($recipients, $headers, $body); + mail_send($recipients, $headers, $body); } -function mail_notify_nudge($from, $to) { +/** + * send a mail message to notify a user of a 'nudge' + * + * @param User $from user nudging + * @param User $to user being nudged + * + * @return boolean success flag + */ + +function mail_notify_nudge($from, $to) +{ common_init_locale($to->language); - $subject = sprintf(_('You\'ve been nudged by %s'), $from->nickname); - - $from_profile = $from->getProfile(); - - $body = sprintf(_("%1\$s (%2\$s) is wondering what you are up to these days and is inviting you to post some news.\n\n". - "So let's hear from you :)\n\n". - "%3\$s\n\n". - "Don't reply to this email; it won't get to them.\n\n". - "With kind regards,\n". - "%4\$s\n"), - $from_profile->getBestName(), - $from->nickname, - common_local_url('all', array('nickname' => $to->nickname)), - common_config('site', 'name')); + $subject = sprintf(_('You\'ve been nudged by %s'), $from->nickname); + + $from_profile = $from->getProfile(); + + $body = sprintf(_("%1\$s (%2\$s) is wondering what you are up to ". + "these days and is inviting you to post some news.\n\n". + "So let's hear from you :)\n\n". + "%3\$s\n\n". + "Don't reply to this email; it won't get to them.\n\n". + "With kind regards,\n". + "%4\$s\n"), + $from_profile->getBestName(), + $from->nickname, + common_local_url('all', array('nickname' => $to->nickname)), + common_config('site', 'name')); common_init_locale(); - return mail_to_user($to, $subject, $body); + return mail_to_user($to, $subject, $body); } -function mail_notify_message($message, $from=NULL, $to=NULL) { +/** + * send a message to notify a user of a direct message (DM) + * + * This function checks to see if the recipient wants notification + * of DMs and has a configured email address. + * + * @param Message $message message to notify about + * @param User $from user sending message; default to sender + * @param User $to user receiving message; default to recipient + * + * @return boolean success code + */ - if (is_null($from)) { - $from = User::staticGet('id', $message->from_profile); - } +function mail_notify_message($message, $from=null, $to=null) +{ + if (is_null($from)) { + $from = User::staticGet('id', $message->from_profile); + } - if (is_null($to)) { - $to = User::staticGet('id', $message->to_profile); - } + if (is_null($to)) { + $to = User::staticGet('id', $message->to_profile); + } - if (is_null($to->email) || !$to->emailnotifymsg) { - return true; - } + if (is_null($to->email) || !$to->emailnotifymsg) { + return true; + } common_init_locale($to->language); - $subject = sprintf(_('New private message from %s'), $from->nickname); - - $from_profile = $from->getProfile(); - - $body = sprintf(_("%1\$s (%2\$s) sent you a private message:\n\n". - "------------------------------------------------------\n". - "%3\$s\n". - "------------------------------------------------------\n\n". - "You can reply to their message here:\n\n". - "%4\$s\n\n". - "Don't reply to this email; it won't get to them.\n\n". - "With kind regards,\n". - "%5\$s\n"), - $from_profile->getBestName(), - $from->nickname, - $message->content, - common_local_url('newmessage', array('to' => $from->id)), - common_config('site', 'name')); + $subject = sprintf(_('New private message from %s'), $from->nickname); + + $from_profile = $from->getProfile(); + + $body = sprintf(_("%1\$s (%2\$s) sent you a private message:\n\n". + "------------------------------------------------------\n". + "%3\$s\n". + "------------------------------------------------------\n\n". + "You can reply to their message here:\n\n". + "%4\$s\n\n". + "Don't reply to this email; it won't get to them.\n\n". + "With kind regards,\n". + "%5\$s\n"), + $from_profile->getBestName(), + $from->nickname, + $message->content, + common_local_url('newmessage', array('to' => $from->id)), + common_config('site', 'name')); common_init_locale(); - return mail_to_user($to, $subject, $body); + return mail_to_user($to, $subject, $body); } -function mail_notify_fave($other, $user, $notice) { +/** + * notify a user that one of their notices has been chosen as a 'fave' + * + * Doesn't check that the user has an email address nor if they + * want to receive notification of faves. Maybe this happens higher + * up the stack...? + * + * @param User $other The user whose notice was faved + * @param User $user The user who faved the notice + * @param Notice $notice The notice that was faved + * + * @return void + */ + +function mail_notify_fave($other, $user, $notice) +{ + $profile = $user->getProfile(); + + $bestname = $profile->getBestName(); - $profile = $user->getProfile(); - $bestname = $profile->getBestName(); common_init_locale($other->language); - $subject = sprintf(_('%s added your notice as a favorite'), $bestname); - $body = sprintf(_("%1\$s just added your notice from %2\$s as one of their favorites.\n\n" . - "In case you forgot, you can see the text of your notice here:\n\n" . - "%3\$s\n\n" . - "You can see the list of %1\$s's favorites here:\n\n" . - "%4\$s\n\n" . - "Faithfully yours,\n" . - "%5\$s\n"), - $bestname, - common_exact_date($notice->created), - common_local_url('shownotice', array('notice' => $notice->id)), - common_local_url('showfavorites', array('nickname' => $user->nickname)), - common_config('site', 'name')); + + $subject = sprintf(_('%s added your notice as a favorite'), $bestname); + + $body = sprintf(_("%1\$s just added your notice from %2\$s". + " as one of their favorites.\n\n" . + "In case you forgot, you can see the text". + " of your notice here:\n\n" . + "%3\$s\n\n" . + "You can see the list of %1\$s's favorites here:\n\n" . + "%4\$s\n\n" . + "Faithfully yours,\n" . + "%5\$s\n"), + $bestname, + common_exact_date($notice->created), + common_local_url('shownotice', + array('notice' => $notice->id)), + common_local_url('showfavorites', + array('nickname' => $user->nickname)), + common_config('site', 'name')); common_init_locale(); - mail_to_user($other, $subject, $body); + mail_to_user($other, $subject, $body); } diff --git a/lib/mailbox.php b/lib/mailbox.php index 4ed8d1758..bdc360a35 100644 --- a/lib/mailbox.php +++ b/lib/mailbox.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 + * common superclass for direct messages inbox and outbox + * + * 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,158 +18,269 @@ * * 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 Message + * @package Laconica + * @author Evan Prodromou <evan@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); } +if (!defined('LACONICA')) { + exit(1); +} -require_once(INSTALLDIR.'/lib/personal.php'); +require_once INSTALLDIR.'/lib/personal.php'; define('MESSAGES_PER_PAGE', 20); -class MailboxAction extends PersonalAction { - - function handle($args) { - - parent::handle($args); - - $nickname = common_canonical_nickname($this->arg('nickname')); - $user = User::staticGet('nickname', $nickname); - - if (!$user) { - $this->client_error(_('No such user.'), 404); - return; - } - - $cur = common_current_user(); - - if (!$cur || $cur->id != $user->id) { - $this->client_error(_('Only the user can read their own mailboxes.'), 403); - return; - } - - $profile = $user->getProfile(); - - if (!$profile) { - $this->server_error(_('User has no profile.')); - return; - } - - $page = $this->trimmed('page'); - - if (!$page) { - $page = 1; - } - - $this->show_page($user, $page); - } - - function get_title($user, $page) { - return ''; - } - - function get_instructions() { - return ''; - } - - function show_top() { - - $cur = common_current_user(); - - common_message_form(NULL, $cur, NULL); - - $this->views_menu(); - } - - function show_page($user, $page) { - - common_show_header($this->get_title($user, $page), - NULL, NULL, - array($this, 'show_top')); - - $this->show_box($user, $page); - - common_show_footer(); - } - - function show_box($user, $page) { - - $message = $this->get_messages($user, $page); - - if ($message) { - - $cnt = 0; - common_element_start('ul', array('id' => 'messages')); - - while ($message->fetch() && $cnt <= MESSAGES_PER_PAGE) { - $cnt++; - - if ($cnt > MESSAGES_PER_PAGE) { - break; - } - - $this->show_message($message); - } - - common_element_end('ul'); - - common_pagination($page > 1, $cnt > MESSAGES_PER_PAGE, - $page, $this->trimmed('action'), - array('nickname' => $user->nickname)); - - $message->free(); - unset($message); - } - } - - # returns the profile we want to show with the message - - function get_message_profile($message) { - return NULL; - } - - function show_message($message) { - - common_element_start('li', array('class' => 'message_single', - 'id' => 'message-' . $message->id)); - - $profile = $this->get_message_profile($message); - - $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE); - common_element_start('a', array('href' => $profile->profileurl)); - common_element('img', array('src' => ($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_STREAM_SIZE), - 'class' => 'avatar stream', - 'width' => AVATAR_STREAM_SIZE, - 'height' => AVATAR_STREAM_SIZE, - 'alt' => - ($profile->fullname) ? $profile->fullname : - $profile->nickname)); - common_element_end('a'); - common_element('a', array('href' => $profile->profileurl, - 'class' => 'nickname'), - $profile->nickname); - # FIXME: URL, image, video, audio - common_element_start('p', array('class' => 'content')); - common_raw($message->rendered); - common_element_end('p'); - - $messageurl = common_local_url('showmessage', array('message' => $message->id)); - - # XXX: we need to figure this out better. Is this right? - if (strcmp($message->uri, $messageurl) != 0 && preg_match('/^http/', $message->uri)) { - $messageurl = $message->uri; - } - common_element_start('p', 'time'); - common_element('a', array('class' => 'permalink', - 'href' => $messageurl, - 'title' => common_exact_date($message->created)), - common_date_string($message->created)); - if ($message->source) { - common_text(_(' from ')); - $this->source_link($message->source); - } - - common_element_end('p'); - - common_element_end('li'); - } +/** + * common superclass for direct messages inbox and outbox + * + * @category Message + * @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/ + * @see InboxAction + * @see OutboxAction + */ + +class MailboxAction extends PersonalAction +{ + /** + * output page based on arguments + * + * @param array $args HTTP arguments (from $_REQUEST) + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + $nickname = common_canonical_nickname($this->arg('nickname')); + + $user = User::staticGet('nickname', $nickname); + + if (!$user) { + $this->client_error(_('No such user.'), 404); + return; + } + + $cur = common_current_user(); + + if (!$cur || $cur->id != $user->id) { + $this->client_error(_('Only the user can read their own mailboxes.'), + 403); + return; + } + + $profile = $user->getProfile(); + + if (!$profile) { + $this->server_error(_('User has no profile.')); + return; + } + + $page = $this->trimmed('page'); + + if (!$page) { + $page = 1; + } + + $this->showPage($user, $page); + } + + /** + * returns the title of the page + * + * @param User $user current user + * @param int $page current page + * + * @return string localised title of the page + */ + + function getTitle($user, $page) + { + return ''; + } + + /** + * instructions for using this page + * + * @return string localised instructions for using the page + */ + + function getInstructions() + { + return ''; + } + + /** + * do structured output for the "instructions" are of the page + * + * @return void + */ + + function showTop() + { + $cur = common_current_user(); + + common_message_form(null, $cur, null); + + $this->views_menu(); + } + + /** + * show a full page of output + * + * @param User $user The current user + * @param int $page The page the user is on + * + * @return void + */ + + function showPage($user, $page) + { + common_show_header($this->getTitle($user, $page), + null, null, + array($this, 'showTop')); + + $this->showBox($user, $page); + + common_show_footer(); + } + + /** + * retrieve the messages appropriate for this mailbox + * + * Does a query for the right messages + * + * @param User $user The current user + * @param int $page The page the user is on + * + * @return Message data object with stream for messages + */ + + function getMessages($user, $page) + { + return null; + } + + /** + * show the messages for a mailbox in list format + * + * Includes the pagination links (before, after). + * + * @param User $user The current user + * @param int $page The page the user is on + * + * @return void + */ + + function showBox($user, $page) + { + $message = $this->getMessages($user, $page); + + if ($message) { + + $cnt = 0; + common_element_start('ul', array('id' => 'messages')); + + while ($message->fetch() && $cnt <= MESSAGES_PER_PAGE) { + $cnt++; + + if ($cnt > MESSAGES_PER_PAGE) { + break; + } + + $this->showMessage($message); + } + + common_element_end('ul'); + + common_pagination($page > 1, $cnt > MESSAGES_PER_PAGE, + $page, $this->trimmed('action'), + array('nickname' => $user->nickname)); + + $message->free(); + unset($message); + } + } + + /** + * returns the profile we want to show with the message + * + * For inboxes, we show the sender; for outboxes, the recipient. + * + * @param Message $message The message to get the profile for + * + * @return Profile The profile that matches the message + */ + + function getMessageProfile($message) + { + return null; + } + + /** + * show a single message in the list format + * + * @param Message $message the message to show + * + * @return void + */ + + function showMessage($message) + { + common_element_start('li', array('class' => 'message_single', + 'id' => 'message-' . $message->id)); + + $profile = $this->getMessageProfile($message); + + $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE); + common_element_start('a', array('href' => $profile->profileurl)); + common_element('img', array('src' => ($avatar) ? + common_avatar_display_url($avatar) : + common_default_avatar(AVATAR_STREAM_SIZE), + 'class' => 'avatar stream', + 'width' => AVATAR_STREAM_SIZE, + 'height' => AVATAR_STREAM_SIZE, + 'alt' => + ($profile->fullname) ? $profile->fullname : + $profile->nickname)); + common_element_end('a'); + common_element('a', array('href' => $profile->profileurl, + 'class' => 'nickname'), + $profile->nickname); + // FIXME: URL, image, video, audio + common_element_start('p', array('class' => 'content')); + common_raw($message->rendered); + common_element_end('p'); + + $messageurl = common_local_url('showmessage', + array('message' => $message->id)); + + // XXX: we need to figure this out better. Is this right? + if (strcmp($message->uri, $messageurl) != 0 && + preg_match('/^http/', $message->uri)) { + $messageurl = $message->uri; + } + common_element_start('p', 'time'); + common_element('a', array('class' => 'permalink', + 'href' => $messageurl, + 'title' => common_exact_date($message->created)), + common_date_string($message->created)); + if ($message->source) { + common_text(_(' from ')); + $this->source_link($message->source); + } + + common_element_end('p'); + + common_element_end('li'); + } } diff --git a/lib/noticelist.php b/lib/noticelist.php index 415c062e4..71db067d0 100644 --- a/lib/noticelist.php +++ b/lib/noticelist.php @@ -1,173 +1,366 @@ <?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 + * widget for displaying a list of notices + * + * 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 + * 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/>. + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @category UI + * @package Laconica + * @author Evan Prodromou <evan@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); } +if (!defined('LACONICA')) { + exit(1); +} + +/** + * widget for displaying a list of notices + * + * There are a number of actions that display a list of notices, in + * reverse chronological order. This widget abstracts out most of the + * code for UI for notice lists. It's overridden to hide some + * data for e.g. the profile page. + * + * @category UI + * @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/ + * @see Notice + * @see StreamAction + * @see NoticeListItem + * @see ProfileNoticeList + */ -class NoticeList { +class NoticeList +{ + /** the current stream of notices being displayed. */ - var $notice = NULL; + var $notice = null; - function __construct($notice) { + /** + * constructor + * + * @param Notice $notice stream of notices from DB_DataObject + */ + + function __construct($notice) + { $this->notice = $notice; } - function show() { + /** + * show the list of notices + * + * "Uses up" the stream by looping through it. So, probably can't + * be called twice on the same list. + * + * @return int count of notices listed. + */ - common_element_start('ul', array('id' => 'notices')); + function show() + { + common_element_start('ul', array('id' => 'notices')); - $cnt = 0; + $cnt = 0; - while ($this->notice->fetch() && $cnt <= NOTICES_PER_PAGE) { - $cnt++; + while ($this->notice->fetch() && $cnt <= NOTICES_PER_PAGE) { + $cnt++; - if ($cnt > NOTICES_PER_PAGE) { - break; - } + if ($cnt > NOTICES_PER_PAGE) { + break; + } - $item = $this->new_list_item($this->notice); + $item = $this->newListItem($this->notice); $item->show(); - } + } - common_element_end('ul'); + common_element_end('ul'); return $cnt; - } + } - function new_list_item($notice) { + /** + * returns a new list item for the current notice + * + * Recipe (factory?) method; overridden by sub-classes to give + * a different list item class. + * + * @param Notice $notice the current notice + * + * @return NoticeListItem a list item for displaying the notice + */ + + function newListItem($notice) + { return new NoticeListItem($notice); } } -class NoticeListItem { +/** + * widget for displaying a single notice + * + * This widget has the core smarts for showing a single notice: what to display, + * where, and under which circumstances. Its key method is show(); this is a recipe + * that calls all the other show*() methods to build up a single notice. The + * ProfileNoticeListItem subclass, for example, overrides showAuthor() to skip + * author info (since that's implicit by the data in the page). + * + * @category UI + * @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/ + * @see NoticeList + * @see ProfileNoticeListItem + */ + +class NoticeListItem +{ + /** The notice this item will show. */ - var $notice = NULL; - var $profile = NULL; + var $notice = null; - function __construct($notice) { - $this->notice = $notice; - $this->profile = $notice->getProfile(); - } - - function show() { - $this->show_start(); - $this->show_fave_form(); - $this->show_author(); - $this->show_content(); - $this->show_start_time_section(); - $this->show_notice_link(); - $this->show_notice_source(); - $this->show_reply_to(); - $this->show_reply_link(); - $this->show_delete_link(); - $this->show_end_time_section(); - $this->show_end(); - } - - function show_start() { - # XXX: RDFa - common_element_start('li', array('class' => 'notice_single hentry', - 'id' => 'notice-' . $this->notice->id)); - } - - function show_fave_form() { + /** The profile of the author of the notice, extracted once for convenience. */ + + var $profile = null; + + /** + * constructor + * + * Also initializes the profile attribute. + * + * @param Notice $notice The notice we'll display + */ + + function __construct($notice) + { + $this->notice = $notice; + $this->profile = $notice->getProfile(); + } + + /** + * recipe function for displaying a single notice. + * + * This uses all the other methods to correctly display a notice. Override + * it or one of the others to fine-tune the output. + * + * @return void + */ + + function show() + { + $this->showStart(); + $this->showFaveForm(); + $this->showAuthor(); + $this->showContent(); + $this->startTimeSection(); + $this->showNoticeLink(); + $this->showNoticeSource(); + $this->showReplyTo(); + $this->showReplyLink(); + $this->showDeleteLink(); + $this->endTimeSection(); + $this->showEnd(); + } + + /** + * start a single notice. + * + * @return void + */ + + function showStart() + { + // XXX: RDFa + common_element_start('li', array('class' => 'notice_single hentry', + 'id' => 'notice-' . $this->notice->id)); + } + + /** + * show the "favorite" form + * + * @return void + */ + + function showFaveForm() + { $user = common_current_user(); - if ($user) { - if ($user->hasFave($this->notice)) { - common_disfavor_form($this->notice); - } else { - common_favor_form($this->notice); - } - } - } - - function show_author() { - common_element_start('span', 'vcard author'); - $this->show_avatar(); - $this->show_nickname(); - common_element_end('span'); - } - - function show_avatar() { - $avatar = $this->profile->getAvatar(AVATAR_STREAM_SIZE); - common_element_start('a', array('href' => $this->profile->profileurl)); - common_element('img', array('src' => ($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_STREAM_SIZE), - 'class' => 'avatar stream photo', - 'width' => AVATAR_STREAM_SIZE, - 'height' => AVATAR_STREAM_SIZE, - 'alt' => - ($this->profile->fullname) ? $this->profile->fullname : - $this->profile->nickname)); - common_element_end('a'); - } - - function show_nickname() { - common_element('a', array('href' => $this->profile->profileurl, - 'class' => 'nickname fn url'), - $this->profile->nickname); - } - - function show_content() { - # FIXME: URL, image, video, audio - common_element_start('p', array('class' => 'content entry-title')); - if ($this->notice->rendered) { - common_raw($this->notice->rendered); - } else { - # XXX: may be some uncooked notices in the DB, - # we cook them right now. This should probably disappear in future - # versions (>> 0.4.x) - common_raw(common_render_content($this->notice->content, $this->notice)); - } - common_element_end('p'); - } - - function show_start_time_section() { - common_element_start('p', 'time'); - } - - function show_notice_link() { - $noticeurl = common_local_url('shownotice', array('notice' => $this->notice->id)); - # XXX: we need to figure this out better. Is this right? - if (strcmp($this->notice->uri, $noticeurl) != 0 && preg_match('/^http/', $this->notice->uri)) { - $noticeurl = $this->notice->uri; - } - common_element_start('a', array('class' => 'permalink', - 'rel' => 'bookmark', - 'href' => $noticeurl)); - common_element('abbr', array('class' => 'published', - 'title' => common_date_iso8601($this->notice->created)), - common_date_string($this->notice->created)); - common_element_end('a'); - } - - function show_notice_source() { - if ($this->notice->source) { - common_element('span', null, _(' from ')); + if ($user) { + if ($user->hasFave($this->notice)) { + common_disfavor_form($this->notice); + } else { + common_favor_form($this->notice); + } + } + } + + /** + * show the author of a notice + * + * By default, this shows the avatar and (linked) nickname of the author. + * + * @return void + */ + + function showAuthor() + { + common_element_start('span', 'vcard author'); + $this->showAvatar(); + $this->showNickname(); + common_element_end('span'); + } + + /** + * show the avatar of the notice's author + * + * This will use the default avatar if no avatar is assigned for the author. + * It makes a link to the author's profile. + * + * @return void + */ + + function showAvatar() + { + $avatar = $this->profile->getAvatar(AVATAR_STREAM_SIZE); + common_element_start('a', array('href' => $this->profile->profileurl)); + common_element('img', array('src' => ($avatar) ? + common_avatar_display_url($avatar) : + common_default_avatar(AVATAR_STREAM_SIZE), + 'class' => 'avatar stream photo', + 'width' => AVATAR_STREAM_SIZE, + 'height' => AVATAR_STREAM_SIZE, + 'alt' => + ($this->profile->fullname) ? + $this->profile->fullname : + $this->profile->nickname)); + common_element_end('a'); + } + + /** + * show the nickname of the author + * + * Links to the author's profile page + * + * @return void + */ + + function showNickname() + { + common_element('a', array('href' => $this->profile->profileurl, + 'class' => 'nickname fn url'), + $this->profile->nickname); + } + + /** + * show the content of the notice + * + * Shows the content of the notice. This is pre-rendered for efficiency + * at save time. Some very old notices might not be pre-rendered, so + * they're rendered on the spot. + * + * @return void + */ + + function showContent() + { + // FIXME: URL, image, video, audio + common_element_start('p', array('class' => 'content entry-title')); + if ($this->notice->rendered) { + common_raw($this->notice->rendered); + } else { + // XXX: may be some uncooked notices in the DB, + // we cook them right now. This should probably disappear in future + // versions (>> 0.4.x) + common_raw(common_render_content($this->notice->content, $this->notice)); + } + common_element_end('p'); + } + + /** + * show the "time" section of a notice + * + * This is the greyed-out section that appears beneath the content, including + * links to delete or reply to the notice. Probably should be called something + * else. + * + * @return void + */ + + function startTimeSection() + { + common_element_start('p', 'time'); + } + + /** + * show the link to the main page for the notice + * + * Displays a link to the page for a notice, with "relative" time. Tries to + * get remote notice URLs correct, but doesn't always succeed. + * + * @return void + */ + + function showNoticeLink() + { + $noticeurl = common_local_url('shownotice', + array('notice' => $this->notice->id)); + // XXX: we need to figure this out better. Is this right? + if (strcmp($this->notice->uri, $noticeurl) != 0 && + preg_match('/^http/', $this->notice->uri)) { + $noticeurl = $this->notice->uri; + } + common_element_start('a', array('class' => 'permalink', + 'rel' => 'bookmark', + 'href' => $noticeurl)); + $dt = common_date_iso8601($this->notice->created); + common_element('abbr', array('class' => 'published', + 'title' => $dt), + common_date_string($this->notice->created)); + common_element_end('a'); + } + + /** + * Show the source of the notice + * + * Either the name (and link) of the API client that posted the notice, + * or one of other other channels. + * + * @return void + */ + + function showNoticeSource() + { + if ($this->notice->source) { + common_element('span', null, _(' from ')); $source_name = _($this->notice->source); switch ($this->notice->source) { - case 'web': - case 'xmpp': - case 'mail': - case 'omb': - case 'api': + case 'web': + case 'xmpp': + case 'mail': + case 'omb': + case 'api': common_element('span', 'noticesource', $source_name); break; - default: + default: $ns = Notice_source::staticGet($this->notice->source); if ($ns) { common_element('a', array('href' => $ns->url), @@ -177,48 +370,98 @@ class NoticeListItem { } break; } - } + } } - function show_reply_to() { - if ($this->notice->reply_to) { - $replyurl = common_local_url('shownotice', array('notice' => $this->notice->reply_to)); - common_text(' ('); - common_element('a', array('class' => 'inreplyto', - 'href' => $replyurl), - _('in reply to...')); - common_text(')'); - } + /** + * show link to notice this notice is a reply to + * + * If this notice is a reply, show a link to the notice it is replying to. The + * heavy lifting for figuring out replies happens at save time. + * + * @return void + */ + + function showReplyTo() + { + if ($this->notice->reply_to) { + $replyurl = common_local_url('shownotice', + array('notice' => $this->notice->reply_to)); + common_text(' ('); + common_element('a', array('class' => 'inreplyto', + 'href' => $replyurl), + _('in reply to...')); + common_text(')'); + } } - function show_reply_link() { - common_element_start('a', - array('href' => common_local_url('newnotice', - array('replyto' => $this->profile->nickname)), - 'onclick' => 'return doreply("'.$this->profile->nickname.'", '.$this->notice->id.');', - 'title' => _('reply'), - 'class' => 'replybutton')); - common_raw(' →'); - common_element_end('a'); + /** + * show a link to reply to the current notice + * + * Should either do the reply in the current notice form (if available), or + * link out to the notice-posting form. A little flakey, doesn't always work. + * + * @return void + */ + + function showReplyLink() + { + $reply_url = common_local_url('newnotice', + array('replyto' => $this->profile->nickname)); + + $reply_js = + 'return doreply("'.$this->profile->nickname.'",'.$this->notice->id.');'; + + common_element_start('a', + array('href' => $reply_url, + 'onclick' => $reply_js, + 'title' => _('reply'), + 'class' => 'replybutton')); + common_raw(' →'); + common_element_end('a'); } - function show_delete_link() { + /** + * if the user is the author, let them delete the notice + * + * @return void + */ + + function showDeleteLink() + { $user = common_current_user(); - if ($user && $this->notice->profile_id == $user->id) { - $deleteurl = common_local_url('deletenotice', array('notice' => $this->notice->id)); - common_element_start('a', array('class' => 'deletenotice', - 'href' => $deleteurl, - 'title' => _('delete'))); - common_raw(' ×'); - common_element_end('a'); - } + if ($user && $this->notice->profile_id == $user->id) { + $deleteurl = common_local_url('deletenotice', + array('notice' => $this->notice->id)); + common_element_start('a', array('class' => 'deletenotice', + 'href' => $deleteurl, + 'title' => _('delete'))); + common_raw(' ×'); + common_element_end('a'); + } } - function show_end_time_section() { - common_element_end('p'); + /** + * end the time section + * + * @return void + */ + + function endTimeSection() + { + common_element_end('p'); } - function show_end() { - common_element_end('li'); + /** + * finish the notice + * + * Close the last elements in the notice list item + * + * @return void + */ + + function showEnd() + { + common_element_end('li'); } } diff --git a/lib/oauthstore.php b/lib/oauthstore.php index d7f9c9ff1..7ad3be20e 100644 --- a/lib/oauthstore.php +++ b/lib/oauthstore.php @@ -21,124 +21,132 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/omb.php'); -class LaconicaOAuthDataStore extends OAuthDataStore { +class LaconicaOAuthDataStore extends OAuthDataStore +{ - # We keep a record of who's contacted us + // We keep a record of who's contacted us - function lookup_consumer($consumer_key) { - $con = Consumer::staticGet('consumer_key', $consumer_key); - if (!$con) { - $con = new Consumer(); - $con->consumer_key = $consumer_key; - $con->seed = common_good_rand(16); - $con->created = DB_DataObject_Cast::dateTime(); - if (!$con->insert()) { - return NULL; - } - } - return new OAuthConsumer($con->consumer_key, ''); - } + function lookup_consumer($consumer_key) + { + $con = Consumer::staticGet('consumer_key', $consumer_key); + if (!$con) { + $con = new Consumer(); + $con->consumer_key = $consumer_key; + $con->seed = common_good_rand(16); + $con->created = DB_DataObject_Cast::dateTime(); + if (!$con->insert()) { + return null; + } + } + return new OAuthConsumer($con->consumer_key, ''); + } - function lookup_token($consumer, $token_type, $token_key) { - $t = new Token(); - $t->consumer_key = $consumer->key; - $t->tok = $token_key; - $t->type = ($token_type == 'access') ? 1 : 0; - if ($t->find(true)) { - return new OAuthToken($t->tok, $t->secret); - } else { - return NULL; - } - } + function lookup_token($consumer, $token_type, $token_key) + { + $t = new Token(); + $t->consumer_key = $consumer->key; + $t->tok = $token_key; + $t->type = ($token_type == 'access') ? 1 : 0; + if ($t->find(true)) { + return new OAuthToken($t->tok, $t->secret); + } else { + return null; + } + } - function lookup_nonce($consumer, $token, $nonce, $timestamp) { - $n = new Nonce(); - $n->consumer_key = $consumer->key; - $n->tok = $token->key; - $n->nonce = $nonce; - if ($n->find(TRUE)) { - return TRUE; - } else { - $n->timestamp = $timestamp; - $n->created = DB_DataObject_Cast::dateTime(); - $n->insert(); - return FALSE; - } - } + function lookup_nonce($consumer, $token, $nonce, $timestamp) + { + $n = new Nonce(); + $n->consumer_key = $consumer->key; + $n->tok = $token->key; + $n->nonce = $nonce; + if ($n->find(true)) { + return true; + } else { + $n->timestamp = $timestamp; + $n->created = DB_DataObject_Cast::dateTime(); + $n->insert(); + return false; + } + } - function new_request_token($consumer) { - $t = new Token(); - $t->consumer_key = $consumer->key; - $t->tok = common_good_rand(16); - $t->secret = common_good_rand(16); - $t->type = 0; # request - $t->state = 0; # unauthorized - $t->created = DB_DataObject_Cast::dateTime(); - if (!$t->insert()) { - return NULL; - } else { - return new OAuthToken($t->tok, $t->secret); - } - } + function new_request_token($consumer) + { + $t = new Token(); + $t->consumer_key = $consumer->key; + $t->tok = common_good_rand(16); + $t->secret = common_good_rand(16); + $t->type = 0; // request + $t->state = 0; // unauthorized + $t->created = DB_DataObject_Cast::dateTime(); + if (!$t->insert()) { + return null; + } else { + return new OAuthToken($t->tok, $t->secret); + } + } - # defined in OAuthDataStore, but not implemented anywhere + // defined in OAuthDataStore, but not implemented anywhere - function fetch_request_token($consumer) { - return $this->new_request_token($consumer); - } + function fetch_request_token($consumer) + { + return $this->new_request_token($consumer); + } - function new_access_token($token, $consumer) { - common_debug('new_access_token("'.$token->key.'","'.$consumer->key.'")', __FILE__); - $rt = new Token(); - $rt->consumer_key = $consumer->key; - $rt->tok = $token->key; - $rt->type = 0; # request - if ($rt->find(TRUE) && $rt->state == 1) { # authorized - common_debug('request token found.', __FILE__); - $at = new Token(); - $at->consumer_key = $consumer->key; - $at->tok = common_good_rand(16); - $at->secret = common_good_rand(16); - $at->type = 1; # access - $at->created = DB_DataObject_Cast::dateTime(); - if (!$at->insert()) { - $e = $at->_lastError; - common_debug('access token "'.$at->tok.'" not inserted: "'.$e->message.'"', __FILE__); - return NULL; - } else { - common_debug('access token "'.$at->tok.'" inserted', __FILE__); - # burn the old one - $orig_rt = clone($rt); - $rt->state = 2; # used - if (!$rt->update($orig_rt)) { - return NULL; - } - common_debug('request token "'.$rt->tok.'" updated', __FILE__); - # Update subscription - # XXX: mixing levels here - $sub = Subscription::staticGet('token', $rt->tok); - if (!$sub) { - return NULL; - } - common_debug('subscription for request token found', __FILE__); - $orig_sub = clone($sub); - $sub->token = $at->tok; - $sub->secret = $at->secret; - if (!$sub->update($orig_sub)) { - return NULL; - } else { - common_debug('subscription updated to use access token', __FILE__); - return new OAuthToken($at->tok, $at->secret); - } - } - } else { - return NULL; - } - } + function new_access_token($token, $consumer) + { + common_debug('new_access_token("'.$token->key.'","'.$consumer->key.'")', __FILE__); + $rt = new Token(); + $rt->consumer_key = $consumer->key; + $rt->tok = $token->key; + $rt->type = 0; // request + if ($rt->find(true) && $rt->state == 1) { // authorized + common_debug('request token found.', __FILE__); + $at = new Token(); + $at->consumer_key = $consumer->key; + $at->tok = common_good_rand(16); + $at->secret = common_good_rand(16); + $at->type = 1; // access + $at->created = DB_DataObject_Cast::dateTime(); + if (!$at->insert()) { + $e = $at->_lastError; + common_debug('access token "'.$at->tok.'" not inserted: "'.$e->message.'"', __FILE__); + return null; + } else { + common_debug('access token "'.$at->tok.'" inserted', __FILE__); + // burn the old one + $orig_rt = clone($rt); + $rt->state = 2; // used + if (!$rt->update($orig_rt)) { + return null; + } + common_debug('request token "'.$rt->tok.'" updated', __FILE__); + // Update subscription + // XXX: mixing levels here + $sub = Subscription::staticGet('token', $rt->tok); + if (!$sub) { + return null; + } + common_debug('subscription for request token found', __FILE__); + $orig_sub = clone($sub); + $sub->token = $at->tok; + $sub->secret = $at->secret; + if (!$sub->update($orig_sub)) { + return null; + } else { + common_debug('subscription updated to use access token', __FILE__); + return new OAuthToken($at->tok, $at->secret); + } + } + } else { + return null; + } + } - # defined in OAuthDataStore, but not implemented anywhere + // defined in OAuthDataStore, but not implemented anywhere - function fetch_access_token($consumer) { - return $this->new_access_token($consumer); - } + function fetch_access_token($consumer) + { + return $this->new_access_token($consumer); + } } diff --git a/lib/omb.php b/lib/omb.php index 96736b4d4..f2dbef5ba 100644 --- a/lib/omb.php +++ b/lib/omb.php @@ -43,257 +43,271 @@ define('OAUTH_AUTH_HEADER', OAUTH_NAMESPACE.'parameters/auth-header'); define('OAUTH_POST_BODY', OAUTH_NAMESPACE.'parameters/post-body'); define('OAUTH_HMAC_SHA1', OAUTH_NAMESPACE.'signature/HMAC-SHA1'); -function omb_oauth_consumer() { - static $con = NULL; - if (!$con) { - $con = new OAuthConsumer(common_root_url(), ''); - } - return $con; +function omb_oauth_consumer() +{ + static $con = null; + if (!$con) { + $con = new OAuthConsumer(common_root_url(), ''); + } + return $con; } -function omb_oauth_server() { - static $server = null; - if (!$server) { - $server = new OAuthServer(omb_oauth_datastore()); - $server->add_signature_method(omb_hmac_sha1()); - } - return $server; +function omb_oauth_server() +{ + static $server = null; + if (!$server) { + $server = new OAuthServer(omb_oauth_datastore()); + $server->add_signature_method(omb_hmac_sha1()); + } + return $server; } -function omb_oauth_datastore() { - static $store = NULL; - if (!$store) { - $store = new LaconicaOAuthDataStore(); - } - return $store; +function omb_oauth_datastore() +{ + static $store = null; + if (!$store) { + $store = new LaconicaOAuthDataStore(); + } + return $store; } -function omb_hmac_sha1() { - static $hmac_method = NULL; - if (!$hmac_method) { - $hmac_method = new OAuthSignatureMethod_HMAC_SHA1(); - } - return $hmac_method; +function omb_hmac_sha1() +{ + static $hmac_method = null; + if (!$hmac_method) { + $hmac_method = new OAuthSignatureMethod_HMAC_SHA1(); + } + return $hmac_method; } -function omb_get_services($xrd, $type) { - return $xrd->services(array(omb_service_filter($type))); +function omb_get_services($xrd, $type) +{ + return $xrd->services(array(omb_service_filter($type))); } -function omb_service_filter($type) { - return create_function('$s', - 'return omb_match_service($s, \''.$type.'\');'); +function omb_service_filter($type) +{ + return create_function('$s', + 'return omb_match_service($s, \''.$type.'\');'); } -function omb_match_service($service, $type) { - return in_array($type, $service->getTypes()); +function omb_match_service($service, $type) +{ + return in_array($type, $service->getTypes()); } -function omb_service_uri($service) { - if (!$service) { - return NULL; - } - $uris = $service->getURIs(); - if (!$uris) { - return NULL; - } - return $uris[0]; +function omb_service_uri($service) +{ + if (!$service) { + return null; + } + $uris = $service->getURIs(); + if (!$uris) { + return null; + } + return $uris[0]; } -function omb_local_id($service) { - if (!$service) { - return NULL; - } - $els = $service->getElements('xrd:LocalID'); - if (!$els) { - return NULL; - } - $el = $els[0]; - return $service->parser->content($el); +function omb_local_id($service) +{ + if (!$service) { + return null; + } + $els = $service->getElements('xrd:LocalID'); + if (!$els) { + return null; + } + $el = $els[0]; + return $service->parser->content($el); } -function omb_broadcast_remote_subscribers($notice) { +function omb_broadcast_remote_subscribers($notice) +{ - # First, get remote users subscribed to this profile - $rp = new Remote_profile(); + # First, get remote users subscribed to this profile + $rp = new Remote_profile(); - $rp->query('SELECT postnoticeurl, token, secret ' . - 'FROM subscription JOIN remote_profile ' . - 'ON subscription.subscriber = remote_profile.id ' . - 'WHERE subscription.subscribed = ' . $notice->profile_id . ' '); + $rp->query('SELECT postnoticeurl, token, secret ' . + 'FROM subscription JOIN remote_profile ' . + 'ON subscription.subscriber = remote_profile.id ' . + 'WHERE subscription.subscribed = ' . $notice->profile_id . ' '); - $posted = array(); + $posted = array(); - while ($rp->fetch()) { - if (!$posted[$rp->postnoticeurl]) { - common_log(LOG_DEBUG, 'Posting to ' . $rp->postnoticeurl); - if (omb_post_notice_keys($notice, $rp->postnoticeurl, $rp->token, $rp->secret)) { - common_log(LOG_DEBUG, 'Finished to ' . $rp->postnoticeurl); - $posted[$rp->postnoticeurl] = TRUE; - } else { - common_log(LOG_DEBUG, 'Failed posting to ' . $rp->postnoticeurl); - } - } - } + while ($rp->fetch()) { + if (!$posted[$rp->postnoticeurl]) { + common_log(LOG_DEBUG, 'Posting to ' . $rp->postnoticeurl); + if (omb_post_notice_keys($notice, $rp->postnoticeurl, $rp->token, $rp->secret)) { + common_log(LOG_DEBUG, 'Finished to ' . $rp->postnoticeurl); + $posted[$rp->postnoticeurl] = true; + } else { + common_log(LOG_DEBUG, 'Failed posting to ' . $rp->postnoticeurl); + } + } + } - $rp->free(); - unset($rp); + $rp->free(); + unset($rp); - return true; + return true; } -function omb_post_notice($notice, $remote_profile, $subscription) { - return omb_post_notice_keys($notice, $remote_profile->postnoticeurl, $subscription->token, $subscription->secret); +function omb_post_notice($notice, $remote_profile, $subscription) +{ + return omb_post_notice_keys($notice, $remote_profile->postnoticeurl, $subscription->token, $subscription->secret); } -function omb_post_notice_keys($notice, $postnoticeurl, $tk, $secret) { +function omb_post_notice_keys($notice, $postnoticeurl, $tk, $secret) +{ - common_debug('Posting notice ' . $notice->id . ' to ' . $postnoticeurl, __FILE__); + common_debug('Posting notice ' . $notice->id . ' to ' . $postnoticeurl, __FILE__); - $user = User::staticGet('id', $notice->profile_id); + $user = User::staticGet('id', $notice->profile_id); - if (!$user) { - common_debug('Failed to get user for notice ' . $notice->id . ', profile = ' . $notice->profile_id, __FILE__); - return false; - } + if (!$user) { + common_debug('Failed to get user for notice ' . $notice->id . ', profile = ' . $notice->profile_id, __FILE__); + return false; + } - $con = omb_oauth_consumer(); + $con = omb_oauth_consumer(); - $token = new OAuthToken($tk, $secret); + $token = new OAuthToken($tk, $secret); - $url = $postnoticeurl; - $parsed = parse_url($url); - $params = array(); - parse_str($parsed['query'], $params); + $url = $postnoticeurl; + $parsed = parse_url($url); + $params = array(); + parse_str($parsed['query'], $params); - $req = OAuthRequest::from_consumer_and_token($con, $token, - 'POST', $url, $params); + $req = OAuthRequest::from_consumer_and_token($con, $token, + 'POST', $url, $params); - $req->set_parameter('omb_version', OMB_VERSION_01); - $req->set_parameter('omb_listenee', $user->uri); - $req->set_parameter('omb_notice', $notice->uri); - $req->set_parameter('omb_notice_content', $notice->content); - $req->set_parameter('omb_notice_url', common_local_url('shownotice', - array('notice' => - $notice->id))); - $req->set_parameter('omb_notice_license', common_config('license', 'url')); + $req->set_parameter('omb_version', OMB_VERSION_01); + $req->set_parameter('omb_listenee', $user->uri); + $req->set_parameter('omb_notice', $notice->uri); + $req->set_parameter('omb_notice_content', $notice->content); + $req->set_parameter('omb_notice_url', common_local_url('shownotice', + array('notice' => + $notice->id))); + $req->set_parameter('omb_notice_license', common_config('license', 'url')); - $user->free(); - unset($user); + $user->free(); + unset($user); - $req->sign_request(omb_hmac_sha1(), $con, $token); + $req->sign_request(omb_hmac_sha1(), $con, $token); - # We re-use this tool's fetcher, since it's pretty good + # We re-use this tool's fetcher, since it's pretty good - $fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); + $fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); - if (!$fetcher) { - common_log(LOG_WARNING, 'Failed to initialize Yadis fetcher.', __FILE__); - return false; - } + if (!$fetcher) { + common_log(LOG_WARNING, 'Failed to initialize Yadis fetcher.', __FILE__); + return false; + } - $result = $fetcher->post($req->get_normalized_http_url(), - $req->to_postdata(), + $result = $fetcher->post($req->get_normalized_http_url(), + $req->to_postdata(), array('User-Agent' => 'Laconica/' . LACONICA_VERSION)); - common_debug('Got HTTP result "'.print_r($result,TRUE).'"', __FILE__); - - if ($result->status == 403) { # not authorized, don't send again - common_debug('403 result, deleting subscription', __FILE__); - # FIXME: figure out how to delete this - # $subscription->delete(); - return false; - } else if ($result->status != 200) { - common_debug('Error status '.$result->status, __FILE__); - return false; - } else { # success! - parse_str($result->body, $return); - if ($return['omb_version'] == OMB_VERSION_01) { - return true; - } else { - return false; - } - } + common_debug('Got HTTP result "'.print_r($result,true).'"', __FILE__); + + if ($result->status == 403) { # not authorized, don't send again + common_debug('403 result, deleting subscription', __FILE__); + # FIXME: figure out how to delete this + # $subscription->delete(); + return false; + } else if ($result->status != 200) { + common_debug('Error status '.$result->status, __FILE__); + return false; + } else { # success! + parse_str($result->body, $return); + if ($return['omb_version'] == OMB_VERSION_01) { + return true; + } else { + return false; + } + } } -function omb_broadcast_profile($profile) { - # First, get remote users subscribed to this profile - # XXX: use a join here rather than looping through results - $sub = new Subscription(); - $sub->subscribed = $profile->id; - if ($sub->find()) { - $updated = array(); - while ($sub->fetch()) { - $rp = Remote_profile::staticGet('id', $sub->subscriber); - if ($rp) { - if (!$updated[$rp->updateprofileurl]) { - if (omb_update_profile($profile, $rp, $sub)) { - $updated[$rp->updateprofileurl] = TRUE; - } - } - } - } - } +function omb_broadcast_profile($profile) +{ + # First, get remote users subscribed to this profile + # XXX: use a join here rather than looping through results + $sub = new Subscription(); + $sub->subscribed = $profile->id; + if ($sub->find()) { + $updated = array(); + while ($sub->fetch()) { + $rp = Remote_profile::staticGet('id', $sub->subscriber); + if ($rp) { + if (!$updated[$rp->updateprofileurl]) { + if (omb_update_profile($profile, $rp, $sub)) { + $updated[$rp->updateprofileurl] = true; + } + } + } + } + } } -function omb_update_profile($profile, $remote_profile, $subscription) { - global $config; # for license URL - $user = User::staticGet($profile->id); - $con = omb_oauth_consumer(); - $token = new OAuthToken($subscription->token, $subscription->secret); - $url = $remote_profile->updateprofileurl; - $parsed = parse_url($url); - $params = array(); - parse_str($parsed['query'], $params); - $req = OAuthRequest::from_consumer_and_token($con, $token, - "POST", $url, $params); - $req->set_parameter('omb_version', OMB_VERSION_01); - $req->set_parameter('omb_listenee', $user->uri); - $req->set_parameter('omb_listenee_profile', common_profile_url($profile->nickname)); - $req->set_parameter('omb_listenee_nickname', $profile->nickname); - - # We use blanks to force emptying any existing values in these optional fields - - $req->set_parameter('omb_listenee_fullname', - ($profile->fullname) ? $profile->fullname : ''); - $req->set_parameter('omb_listenee_homepage', - ($profile->homepage) ? $profile->homepage : ''); - $req->set_parameter('omb_listenee_bio', - ($profile->bio) ? $profile->bio : ''); - $req->set_parameter('omb_listenee_location', - ($profile->location) ? $profile->location : ''); - - $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); - $req->set_parameter('omb_listenee_avatar', - ($avatar) ? $avatar->url : ''); - - $req->sign_request(omb_hmac_sha1(), $con, $token); - - # We re-use this tool's fetcher, since it's pretty good - - $fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); - - common_debug('request URL = '.$req->get_normalized_http_url(), __FILE__); - common_debug('postdata = '.$req->to_postdata(), __FILE__); - $result = $fetcher->post($req->get_normalized_http_url(), - $req->to_postdata(), +function omb_update_profile($profile, $remote_profile, $subscription) +{ + global $config; # for license URL + $user = User::staticGet($profile->id); + $con = omb_oauth_consumer(); + $token = new OAuthToken($subscription->token, $subscription->secret); + $url = $remote_profile->updateprofileurl; + $parsed = parse_url($url); + $params = array(); + parse_str($parsed['query'], $params); + $req = OAuthRequest::from_consumer_and_token($con, $token, + "POST", $url, $params); + $req->set_parameter('omb_version', OMB_VERSION_01); + $req->set_parameter('omb_listenee', $user->uri); + $req->set_parameter('omb_listenee_profile', common_profile_url($profile->nickname)); + $req->set_parameter('omb_listenee_nickname', $profile->nickname); + + # We use blanks to force emptying any existing values in these optional fields + + $req->set_parameter('omb_listenee_fullname', + ($profile->fullname) ? $profile->fullname : ''); + $req->set_parameter('omb_listenee_homepage', + ($profile->homepage) ? $profile->homepage : ''); + $req->set_parameter('omb_listenee_bio', + ($profile->bio) ? $profile->bio : ''); + $req->set_parameter('omb_listenee_location', + ($profile->location) ? $profile->location : ''); + + $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); + $req->set_parameter('omb_listenee_avatar', + ($avatar) ? $avatar->url : ''); + + $req->sign_request(omb_hmac_sha1(), $con, $token); + + # We re-use this tool's fetcher, since it's pretty good + + $fetcher = Auth_Yadis_Yadis::getHTTPFetcher(); + + common_debug('request URL = '.$req->get_normalized_http_url(), __FILE__); + common_debug('postdata = '.$req->to_postdata(), __FILE__); + $result = $fetcher->post($req->get_normalized_http_url(), + $req->to_postdata(), array('User-Agent' => 'Laconica/' . LACONICA_VERSION)); - common_debug('Got HTTP result "'.print_r($result,TRUE).'"', __FILE__); - - if ($result->status == 403) { # not authorized, don't send again - common_debug('403 result, deleting subscription', __FILE__); - $subscription->delete(); - return false; - } else if ($result->status != 200) { - common_debug('Error status '.$result->status, __FILE__); - return false; - } else { # success! - parse_str($result->body, $return); - if ($return['omb_version'] == OMB_VERSION_01) { - return true; - } else { - return false; - } - } + common_debug('Got HTTP result "'.print_r($result,true).'"', __FILE__); + + if ($result->status == 403) { # not authorized, don't send again + common_debug('403 result, deleting subscription', __FILE__); + $subscription->delete(); + return false; + } else if ($result->status != 200) { + common_debug('Error status '.$result->status, __FILE__); + return false; + } else { # success! + parse_str($result->body, $return); + if ($return['omb_version'] == OMB_VERSION_01) { + return true; + } else { + return false; + } + } } diff --git a/lib/openid.php b/lib/openid.php index 6e501c2b1..1e7f318fb 100644 --- a/lib/openid.php +++ b/lib/openid.php @@ -31,212 +31,223 @@ require_once('Auth/OpenID/MySQLStore.php'); define('OPENID_COOKIE_EXPIRY', round(365.25 * 24 * 60 * 60)); define('OPENID_COOKIE_KEY', 'lastusedopenid'); -function oid_store() { - static $store = NULL; - if (!$store) { - # Can't be called statically - $user = new User(); - $conn = $user->getDatabaseConnection(); - $store = new Auth_OpenID_MySQLStore($conn); - } - return $store; +function oid_store() +{ + static $store = null; + if (!$store) { + # Can't be called statically + $user = new User(); + $conn = $user->getDatabaseConnection(); + $store = new Auth_OpenID_MySQLStore($conn); + } + return $store; } -function oid_consumer() { - $store = oid_store(); - $consumer = new Auth_OpenID_Consumer($store); - return $consumer; +function oid_consumer() +{ + $store = oid_store(); + $consumer = new Auth_OpenID_Consumer($store); + return $consumer; } -function oid_clear_last() { - oid_set_last(''); +function oid_clear_last() +{ + oid_set_last(''); } -function oid_set_last($openid_url) { - common_set_cookie(OPENID_COOKIE_KEY, - $openid_url, - time() + OPENID_COOKIE_EXPIRY); +function oid_set_last($openid_url) +{ + common_set_cookie(OPENID_COOKIE_KEY, + $openid_url, + time() + OPENID_COOKIE_EXPIRY); } -function oid_get_last() { - $openid_url = $_COOKIE[OPENID_COOKIE_KEY]; - if ($openid_url && strlen($openid_url) > 0) { - return $openid_url; - } else { - return NULL; - } +function oid_get_last() +{ + $openid_url = $_COOKIE[OPENID_COOKIE_KEY]; + if ($openid_url && strlen($openid_url) > 0) { + return $openid_url; + } else { + return null; + } } -function oid_link_user($id, $canonical, $display) { +function oid_link_user($id, $canonical, $display) +{ - $oid = new User_openid(); - $oid->user_id = $id; - $oid->canonical = $canonical; - $oid->display = $display; - $oid->created = DB_DataObject_Cast::dateTime(); + $oid = new User_openid(); + $oid->user_id = $id; + $oid->canonical = $canonical; + $oid->display = $display; + $oid->created = DB_DataObject_Cast::dateTime(); - if (!$oid->insert()) { - $err = PEAR::getStaticProperty('DB_DataObject','lastError'); - common_debug('DB error ' . $err->code . ': ' . $err->message, __FILE__); - return false; - } + if (!$oid->insert()) { + $err = PEAR::getStaticProperty('DB_DataObject','lastError'); + common_debug('DB error ' . $err->code . ': ' . $err->message, __FILE__); + return false; + } - return true; + return true; } -function oid_get_user($openid_url) { - $user = NULL; - $oid = User_openid::staticGet('canonical', $openid_url); - if ($oid) { - $user = User::staticGet('id', $oid->user_id); - } - return $user; +function oid_get_user($openid_url) +{ + $user = null; + $oid = User_openid::staticGet('canonical', $openid_url); + if ($oid) { + $user = User::staticGet('id', $oid->user_id); + } + return $user; } -function oid_check_immediate($openid_url, $backto=NULL) { - if (!$backto) { - $action = $_REQUEST['action']; - $args = common_copy_args($_GET); - unset($args['action']); - $backto = common_local_url($action, $args); - } - common_debug('going back to "' . $backto . '"', __FILE__); - - common_ensure_session(); - - $_SESSION['openid_immediate_backto'] = $backto; - common_debug('passed-in variable is "' . $backto . '"', __FILE__); - common_debug('session variable is "' . $_SESSION['openid_immediate_backto'] . '"', __FILE__); - - oid_authenticate($openid_url, - 'finishimmediate', - true); +function oid_check_immediate($openid_url, $backto=null) +{ + if (!$backto) { + $action = $_REQUEST['action']; + $args = common_copy_args($_GET); + unset($args['action']); + $backto = common_local_url($action, $args); + } + common_debug('going back to "' . $backto . '"', __FILE__); + + common_ensure_session(); + + $_SESSION['openid_immediate_backto'] = $backto; + common_debug('passed-in variable is "' . $backto . '"', __FILE__); + common_debug('session variable is "' . $_SESSION['openid_immediate_backto'] . '"', __FILE__); + + oid_authenticate($openid_url, + 'finishimmediate', + true); } -function oid_authenticate($openid_url, $returnto, $immediate=false) { - - $consumer = oid_consumer(); - - if (!$consumer) { - common_server_error(_('Cannot instantiate OpenID consumer object.')); - return false; - } - - common_ensure_session(); - - $auth_request = $consumer->begin($openid_url); - - // Handle failure status return values. - if (!$auth_request) { - return _('Not a valid OpenID.'); - } else if (Auth_OpenID::isFailure($auth_request)) { - return sprintf(_('OpenID failure: %s'), $auth_request->message); - } - - $sreg_request = Auth_OpenID_SRegRequest::build(// Required - array(), - // Optional - array('nickname', - 'email', - 'fullname', - 'language', - 'timezone', - 'postcode', - 'country')); - - if ($sreg_request) { - $auth_request->addExtension($sreg_request); - } - - $trust_root = common_local_url('public'); - $process_url = common_local_url($returnto); - - if ($auth_request->shouldSendRedirect()) { - $redirect_url = $auth_request->redirectURL($trust_root, - $process_url, - $immediate); - if (!$redirect_url) { - } else if (Auth_OpenID::isFailure($redirect_url)) { - return sprintf(_('Could not redirect to server: %s'), $redirect_url->message); - } else { - common_redirect($redirect_url); - } - } else { - // Generate form markup and render it. - $form_id = 'openid_message'; - $form_html = $auth_request->formMarkup($trust_root, $process_url, - $immediate, array('id' => $form_id)); - - # XXX: This is cheap, but things choke if we don't escape ampersands - # in the HTML attributes - - $form_html = preg_replace('/&/', '&', $form_html); - - // Display an error if the form markup couldn't be generated; - // otherwise, render the HTML. - if (Auth_OpenID::isFailure($form_html)) { - $this->show_form(sprintf(_('Could not create OpenID form: %s'), $form_html->message)); - } else { - common_show_header(_('OpenID Auto-Submit'), NULL, NULL, '_oid_print_instructions'); - common_raw($form_html); - common_element('script', NULL, - '$(document).ready(function() { ' . - ' $("#'. $form_id .'").submit(); '. - '});'); - common_show_footer(); - } - } +function oid_authenticate($openid_url, $returnto, $immediate=false) +{ + + $consumer = oid_consumer(); + + if (!$consumer) { + common_server_error(_('Cannot instantiate OpenID consumer object.')); + return false; + } + + common_ensure_session(); + + $auth_request = $consumer->begin($openid_url); + + // Handle failure status return values. + if (!$auth_request) { + return _('Not a valid OpenID.'); + } else if (Auth_OpenID::isFailure($auth_request)) { + return sprintf(_('OpenID failure: %s'), $auth_request->message); + } + + $sreg_request = Auth_OpenID_SRegRequest::build(// Required + array(), + // Optional + array('nickname', + 'email', + 'fullname', + 'language', + 'timezone', + 'postcode', + 'country')); + + if ($sreg_request) { + $auth_request->addExtension($sreg_request); + } + + $trust_root = common_local_url('public'); + $process_url = common_local_url($returnto); + + if ($auth_request->shouldSendRedirect()) { + $redirect_url = $auth_request->redirectURL($trust_root, + $process_url, + $immediate); + if (!$redirect_url) { + } else if (Auth_OpenID::isFailure($redirect_url)) { + return sprintf(_('Could not redirect to server: %s'), $redirect_url->message); + } else { + common_redirect($redirect_url); + } + } else { + // Generate form markup and render it. + $form_id = 'openid_message'; + $form_html = $auth_request->formMarkup($trust_root, $process_url, + $immediate, array('id' => $form_id)); + + # XXX: This is cheap, but things choke if we don't escape ampersands + # in the HTML attributes + + $form_html = preg_replace('/&/', '&', $form_html); + + // Display an error if the form markup couldn't be generated; + // otherwise, render the HTML. + if (Auth_OpenID::isFailure($form_html)) { + $this->show_form(sprintf(_('Could not create OpenID form: %s'), $form_html->message)); + } else { + common_show_header(_('OpenID Auto-Submit'), null, null, '_oid_print_instructions'); + common_raw($form_html); + common_element('script', null, + '$(document).ready(function() { ' . + ' $("#'. $form_id .'").submit(); '. + '});'); + common_show_footer(); + } + } } # Half-assed attempt at a module-private function -function _oid_print_instructions() { - common_element('div', 'instructions', - _('This form should automatically submit itself. '. - 'If not, click the submit button to go to your '. - 'OpenID provider.')); +function _oid_print_instructions() +{ + common_element('div', 'instructions', + _('This form should automatically submit itself. '. + 'If not, click the submit button to go to your '. + 'OpenID provider.')); } # update a user from sreg parameters -function oid_update_user(&$user, &$sreg) { +function oid_update_user(&$user, &$sreg) +{ - $profile = $user->getProfile(); + $profile = $user->getProfile(); - $orig_profile = clone($profile); + $orig_profile = clone($profile); - if ($sreg['fullname'] && strlen($sreg['fullname']) <= 255) { - $profile->fullname = $sreg['fullname']; - } + if ($sreg['fullname'] && strlen($sreg['fullname']) <= 255) { + $profile->fullname = $sreg['fullname']; + } - if ($sreg['country']) { - if ($sreg['postcode']) { - # XXX: use postcode to get city and region - # XXX: also, store postcode somewhere -- it's valuable! - $profile->location = $sreg['postcode'] . ', ' . $sreg['country']; - } else { - $profile->location = $sreg['country']; - } - } + if ($sreg['country']) { + if ($sreg['postcode']) { + # XXX: use postcode to get city and region + # XXX: also, store postcode somewhere -- it's valuable! + $profile->location = $sreg['postcode'] . ', ' . $sreg['country']; + } else { + $profile->location = $sreg['country']; + } + } - # XXX save language if it's passed - # XXX save timezone if it's passed + # XXX save language if it's passed + # XXX save timezone if it's passed - if (!$profile->update($orig_profile)) { - common_server_error(_('Error saving the profile.')); - return false; - } + if (!$profile->update($orig_profile)) { + common_server_error(_('Error saving the profile.')); + return false; + } - $orig_user = clone($user); + $orig_user = clone($user); - if ($sreg['email'] && Validate::email($sreg['email'], true)) { - $user->email = $sreg['email']; - } + if ($sreg['email'] && Validate::email($sreg['email'], true)) { + $user->email = $sreg['email']; + } - if (!$user->update($orig_user)) { - common_server_error(_('Error saving the user.')); - return false; - } + if (!$user->update($orig_user)) { + common_server_error(_('Error saving the user.')); + return false; + } - return true; + return true; } diff --git a/lib/personal.php b/lib/personal.php index 86433b486..02b01fece 100644 --- a/lib/personal.php +++ b/lib/personal.php @@ -19,188 +19,195 @@ if (!defined('LACONICA')) { exit(1); } -class PersonalAction extends Action { - - function is_readonly() { - return true; - } - - function handle($args) { - parent::handle($args); - common_set_returnto($this->self_url()); - } - - function views_menu() { - - $user = NULL; - $action = $this->trimmed('action'); - $nickname = $this->trimmed('nickname'); - - if ($nickname) { - $user = User::staticGet('nickname', $nickname); - $user_profile = $user->getProfile(); - } else { - $user_profile = false; - } - - common_element_start('ul', array('id' => 'nav_views')); - - common_menu_item(common_local_url('all', array('nickname' => - $nickname)), - _('Personal'), - sprintf(_('%s and friends'), (($user_profile && $user_profile->fullname) ? $user_profile->fullname : $nickname)), - $action == 'all'); - common_menu_item(common_local_url('replies', array('nickname' => - $nickname)), - _('Replies'), - sprintf(_('Replies to %s'), (($user_profile && $user_profile->fullname) ? $user_profile->fullname : $nickname)), - $action == 'replies'); - common_menu_item(common_local_url('showstream', array('nickname' => - $nickname)), - _('Profile'), - ($user_profile && $user_profile->fullname) ? $user_profile->fullname : $nickname, - $action == 'showstream'); - common_menu_item(common_local_url('showfavorites', array('nickname' => - $nickname)), - _('Favorites'), - sprintf(_('%s\'s favorite notices'), ($user_profile) ? $user_profile->getBestName() : _('User')), - $action == 'showfavorites'); - - $cur = common_current_user(); - - if ($cur && $cur->id == $user->id) { - - common_menu_item(common_local_url('inbox', array('nickname' => - $nickname)), - _('Inbox'), - _('Your incoming messages'), - $action == 'inbox'); - common_menu_item(common_local_url('outbox', array('nickname' => - $nickname)), - _('Outbox'), - _('Your sent messages'), - $action == 'outbox'); - } - - common_element_end('ul'); - } - - function show_feeds_list($feeds) { - common_element_start('div', array('class' => 'feeds')); - common_element('p', null, 'Feeds:'); - common_element_start('ul', array('class' => 'xoxo')); - - foreach ($feeds as $key => $value) { - $this->common_feed_item($feeds[$key]); - } - common_element_end('ul'); - common_element_end('div'); - } - - function common_feed_item($feed) { - $nickname = $this->trimmed('nickname'); - - switch($feed['item']) { - case 'notices': default: - $feed_classname = $feed['type']; - $feed_mimetype = "application/".$feed['type']."+xml"; - $feed_title = "$nickname's ".$feed['version']." notice feed"; - $feed['textContent'] = "RSS"; - break; - - case 'allrss': - $feed_classname = $feed['type']; - $feed_mimetype = "application/".$feed['type']."+xml"; - $feed_title = $feed['version']." feed for $nickname and friends"; - $feed['textContent'] = "RSS"; - break; - - case 'repliesrss': - $feed_classname = $feed['type']; - $feed_mimetype = "application/".$feed['type']."+xml"; - $feed_title = $feed['version']." feed for replies to $nickname"; - $feed['textContent'] = "RSS"; - break; - - case 'publicrss': - $feed_classname = $feed['type']; - $feed_mimetype = "application/".$feed['type']."+xml"; - $feed_title = "Public timeline ".$feed['version']." feed"; - $feed['textContent'] = "RSS"; - break; - - case 'publicatom': - $feed_classname = "atom"; - $feed_mimetype = "application/".$feed['type']."+xml"; - $feed_title = "Public timeline ".$feed['version']." feed"; - $feed['textContent'] = "Atom"; - break; - - case 'tagrss': - $feed_classname = $feed['type']; - $feed_mimetype = "application/".$feed['type']."+xml"; - $feed_title = $feed['version']." feed for this tag"; - $feed['textContent'] = "RSS"; - break; - - case 'favoritedrss': - $feed_classname = $feed['type']; - $feed_mimetype = "application/".$feed['type']."+xml"; - $feed_title = "Favorited ".$feed['version']." feed"; - $feed['textContent'] = "RSS"; - break; - - case 'foaf': - $feed_classname = "foaf"; - $feed_mimetype = "application/".$feed['type']."+xml"; - $feed_title = "$nickname's FOAF file"; - $feed['textContent'] = "FOAF"; - break; - - case 'favoritesrss': - $feed_classname = "favorites"; - $feed_mimetype = "application/".$feed['type']."+xml"; - $feed_title = "Feed for favorites of $nickname"; - $feed['textContent'] = "RSS"; - break; - - case 'usertimeline': - $feed_classname = "atom"; - $feed_mimetype = "application/".$feed['type']."+xml"; - $feed_title = "$nickname's ".$feed['version']." notice feed"; - $feed['textContent'] = "Atom"; - break; - } - common_element_start('li'); - common_element('a', array('href' => $feed['href'], - 'class' => $feed_classname, - 'type' => $feed_mimetype, - 'title' => $feed_title), - $feed['textContent']); - common_element_end('li'); - } - - - function source_link($source) { - $source_name = _($source); - switch ($source) { - case 'web': - case 'xmpp': - case 'mail': - case 'omb': - case 'api': - common_element('span', 'noticesource', $source_name); - break; - default: - $ns = Notice_source::staticGet($source); - if ($ns) { - common_element('a', array('href' => $ns->url), - $ns->name); - } else { - common_element('span', 'noticesource', $source_name); - } - break; - } - return; - } +class PersonalAction extends Action +{ + + function is_readonly() + { + return true; + } + + function handle($args) + { + parent::handle($args); + common_set_returnto($this->self_url()); + } + + function views_menu() + { + + $user = null; + $action = $this->trimmed('action'); + $nickname = $this->trimmed('nickname'); + + if ($nickname) { + $user = User::staticGet('nickname', $nickname); + $user_profile = $user->getProfile(); + } else { + $user_profile = false; + } + + common_element_start('ul', array('id' => 'nav_views')); + + common_menu_item(common_local_url('all', array('nickname' => + $nickname)), + _('Personal'), + sprintf(_('%s and friends'), (($user_profile && $user_profile->fullname) ? $user_profile->fullname : $nickname)), + $action == 'all'); + common_menu_item(common_local_url('replies', array('nickname' => + $nickname)), + _('Replies'), + sprintf(_('Replies to %s'), (($user_profile && $user_profile->fullname) ? $user_profile->fullname : $nickname)), + $action == 'replies'); + common_menu_item(common_local_url('showstream', array('nickname' => + $nickname)), + _('Profile'), + ($user_profile && $user_profile->fullname) ? $user_profile->fullname : $nickname, + $action == 'showstream'); + common_menu_item(common_local_url('showfavorites', array('nickname' => + $nickname)), + _('Favorites'), + sprintf(_('%s\'s favorite notices'), ($user_profile) ? $user_profile->getBestName() : _('User')), + $action == 'showfavorites'); + + $cur = common_current_user(); + + if ($cur && $cur->id == $user->id) { + + common_menu_item(common_local_url('inbox', array('nickname' => + $nickname)), + _('Inbox'), + _('Your incoming messages'), + $action == 'inbox'); + common_menu_item(common_local_url('outbox', array('nickname' => + $nickname)), + _('Outbox'), + _('Your sent messages'), + $action == 'outbox'); + } + + common_element_end('ul'); + } + + function show_feeds_list($feeds) + { + common_element_start('div', array('class' => 'feeds')); + common_element('p', null, 'Feeds:'); + common_element_start('ul', array('class' => 'xoxo')); + + foreach ($feeds as $key => $value) { + $this->common_feed_item($feeds[$key]); + } + common_element_end('ul'); + common_element_end('div'); + } + + function common_feed_item($feed) + { + $nickname = $this->trimmed('nickname'); + + switch($feed['item']) { + case 'notices': default: + $feed_classname = $feed['type']; + $feed_mimetype = "application/".$feed['type']."+xml"; + $feed_title = "$nickname's ".$feed['version']." notice feed"; + $feed['textContent'] = "RSS"; + break; + + case 'allrss': + $feed_classname = $feed['type']; + $feed_mimetype = "application/".$feed['type']."+xml"; + $feed_title = $feed['version']." feed for $nickname and friends"; + $feed['textContent'] = "RSS"; + break; + + case 'repliesrss': + $feed_classname = $feed['type']; + $feed_mimetype = "application/".$feed['type']."+xml"; + $feed_title = $feed['version']." feed for replies to $nickname"; + $feed['textContent'] = "RSS"; + break; + + case 'publicrss': + $feed_classname = $feed['type']; + $feed_mimetype = "application/".$feed['type']."+xml"; + $feed_title = "Public timeline ".$feed['version']." feed"; + $feed['textContent'] = "RSS"; + break; + + case 'publicatom': + $feed_classname = "atom"; + $feed_mimetype = "application/".$feed['type']."+xml"; + $feed_title = "Public timeline ".$feed['version']." feed"; + $feed['textContent'] = "Atom"; + break; + + case 'tagrss': + $feed_classname = $feed['type']; + $feed_mimetype = "application/".$feed['type']."+xml"; + $feed_title = $feed['version']." feed for this tag"; + $feed['textContent'] = "RSS"; + break; + + case 'favoritedrss': + $feed_classname = $feed['type']; + $feed_mimetype = "application/".$feed['type']."+xml"; + $feed_title = "Favorited ".$feed['version']." feed"; + $feed['textContent'] = "RSS"; + break; + + case 'foaf': + $feed_classname = "foaf"; + $feed_mimetype = "application/".$feed['type']."+xml"; + $feed_title = "$nickname's FOAF file"; + $feed['textContent'] = "FOAF"; + break; + + case 'favoritesrss': + $feed_classname = "favorites"; + $feed_mimetype = "application/".$feed['type']."+xml"; + $feed_title = "Feed for favorites of $nickname"; + $feed['textContent'] = "RSS"; + break; + + case 'usertimeline': + $feed_classname = "atom"; + $feed_mimetype = "application/".$feed['type']."+xml"; + $feed_title = "$nickname's ".$feed['version']." notice feed"; + $feed['textContent'] = "Atom"; + break; + } + common_element_start('li'); + common_element('a', array('href' => $feed['href'], + 'class' => $feed_classname, + 'type' => $feed_mimetype, + 'title' => $feed_title), + $feed['textContent']); + common_element_end('li'); + } + + + function source_link($source) + { + $source_name = _($source); + switch ($source) { + case 'web': + case 'xmpp': + case 'mail': + case 'omb': + case 'api': + common_element('span', 'noticesource', $source_name); + break; + default: + $ns = Notice_source::staticGet($source); + if ($ns) { + common_element('a', array('href' => $ns->url), + $ns->name); + } else { + common_element('span', 'noticesource', $source_name); + } + break; + } + return; + } } diff --git a/lib/profilelist.php b/lib/profilelist.php index 9079ea9d7..bda05daf3 100644 --- a/lib/profilelist.php +++ b/lib/profilelist.php @@ -22,148 +22,154 @@ if (!defined('LACONICA')) { exit(1); } define('PROFILES_PER_PAGE', 20); -class ProfileList { - - var $profile = NULL; - var $owner = NULL; - var $action = NULL; - - function __construct($profile, $owner=NULL, $action=NULL) { - $this->profile = $profile; - $this->owner = $owner; - $this->action = $action; - } - - function show_list() { - - common_element_start('ul', array('id' => 'profiles', 'class' => 'profile_list')); - - $cnt = 0; - - while ($this->profile->fetch()) { - $cnt++; - if($cnt > PROFILES_PER_PAGE) { - break; - } - $this->show(); - } - - common_element_end('ul'); - - return $cnt; - } - - function show() { - - common_element_start('li', array('class' => 'profile_single', - 'id' => 'profile-' . $this->profile->id)); - - $user = common_current_user(); - - if ($user && $user->id != $this->profile->id) { - # XXX: special-case for user looking at own - # subscriptions page - if ($user->isSubscribed($this->profile)) { - common_unsubscribe_form($this->profile); - } else { - common_subscribe_form($this->profile); - } - } - - $avatar = $this->profile->getAvatar(AVATAR_STREAM_SIZE); - common_element_start('a', array('href' => $this->profile->profileurl)); - common_element('img', array('src' => ($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_STREAM_SIZE), - 'class' => 'avatar stream', - 'width' => AVATAR_STREAM_SIZE, - 'height' => AVATAR_STREAM_SIZE, - 'alt' => - ($this->profile->fullname) ? $this->profile->fullname : - $this->profile->nickname)); - common_element_end('a'); - common_element_start('p'); - common_element_start('a', array('href' => $this->profile->profileurl, - 'class' => 'nickname')); - common_raw($this->highlight($this->profile->nickname)); - common_element_end('a'); - if ($this->profile->fullname) { - common_text(' | '); - common_element_start('span', 'fullname'); - common_raw($this->highlight($this->profile->fullname)); - common_element_end('span'); - } - if ($this->profile->location) { - common_text(' | '); - common_element_start('span', 'location'); - common_raw($this->highlight($this->profile->location)); - common_element_end('span'); - } - common_element_end('p'); - if ($this->profile->homepage) { - common_element_start('p', 'website'); - common_element_start('a', array('href' => $this->profile->homepage)); - common_raw($this->highlight($this->profile->homepage)); - common_element_end('a'); - common_element_end('p'); - } - if ($this->profile->bio) { - common_element_start('p', 'bio'); - common_raw($this->highlight($this->profile->bio)); - common_element_end('p'); - } - - # If we're on a list with an owner (subscriptions or subscribers)... - - if ($this->owner) { - # Get tags - $tags = Profile_tag::getTags($this->owner->id, $this->profile->id); - - common_element_start('div', 'tags_user'); - common_element_start('dl'); - common_element_start('dt'); - if ($user->id == $this->owner->id) { - common_element('a', array('href' => common_local_url('tagother', - array('id' => $this->profile->id))), - _('Tags')); - } else { - common_text(_('Tags')); - } - common_text(":"); - common_element_end('dt'); - common_element_start('dd'); - if ($tags) { - common_element_start('ul', 'tags xoxo'); - foreach ($tags as $tag) { - common_element_start('li'); - common_element('a', array('rel' => 'tag', - 'href' => common_local_url($this->action, - array('nickname' => $this->owner->nickname, - 'tag' => $tag))), - $tag); - common_element_end('li'); - } - common_element_end('ul'); - } else { - common_text(_('(none)')); - } - common_element_end('dd'); - common_element_end('dl'); - common_element_end('div'); - } +class ProfileList +{ + + var $profile = null; + var $owner = null; + var $action = null; + + function __construct($profile, $owner=null, $action=null) + { + $this->profile = $profile; + $this->owner = $owner; + $this->action = $action; + } + + function show_list() + { + + common_element_start('ul', array('id' => 'profiles', 'class' => 'profile_list')); + + $cnt = 0; + + while ($this->profile->fetch()) { + $cnt++; + if($cnt > PROFILES_PER_PAGE) { + break; + } + $this->show(); + } + + common_element_end('ul'); + + return $cnt; + } + + function show() + { + + common_element_start('li', array('class' => 'profile_single', + 'id' => 'profile-' . $this->profile->id)); + + $user = common_current_user(); + + if ($user && $user->id != $this->profile->id) { + # XXX: special-case for user looking at own + # subscriptions page + if ($user->isSubscribed($this->profile)) { + common_unsubscribe_form($this->profile); + } else { + common_subscribe_form($this->profile); + } + } + + $avatar = $this->profile->getAvatar(AVATAR_STREAM_SIZE); + common_element_start('a', array('href' => $this->profile->profileurl)); + common_element('img', array('src' => ($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_STREAM_SIZE), + 'class' => 'avatar stream', + 'width' => AVATAR_STREAM_SIZE, + 'height' => AVATAR_STREAM_SIZE, + 'alt' => + ($this->profile->fullname) ? $this->profile->fullname : + $this->profile->nickname)); + common_element_end('a'); + common_element_start('p'); + common_element_start('a', array('href' => $this->profile->profileurl, + 'class' => 'nickname')); + common_raw($this->highlight($this->profile->nickname)); + common_element_end('a'); + if ($this->profile->fullname) { + common_text(' | '); + common_element_start('span', 'fullname'); + common_raw($this->highlight($this->profile->fullname)); + common_element_end('span'); + } + if ($this->profile->location) { + common_text(' | '); + common_element_start('span', 'location'); + common_raw($this->highlight($this->profile->location)); + common_element_end('span'); + } + common_element_end('p'); + if ($this->profile->homepage) { + common_element_start('p', 'website'); + common_element_start('a', array('href' => $this->profile->homepage)); + common_raw($this->highlight($this->profile->homepage)); + common_element_end('a'); + common_element_end('p'); + } + if ($this->profile->bio) { + common_element_start('p', 'bio'); + common_raw($this->highlight($this->profile->bio)); + common_element_end('p'); + } + + # If we're on a list with an owner (subscriptions or subscribers)... + + if ($this->owner) { + # Get tags + $tags = Profile_tag::getTags($this->owner->id, $this->profile->id); + + common_element_start('div', 'tags_user'); + common_element_start('dl'); + common_element_start('dt'); + if ($user->id == $this->owner->id) { + common_element('a', array('href' => common_local_url('tagother', + array('id' => $this->profile->id))), + _('Tags')); + } else { + common_text(_('Tags')); + } + common_text(":"); + common_element_end('dt'); + common_element_start('dd'); + if ($tags) { + common_element_start('ul', 'tags xoxo'); + foreach ($tags as $tag) { + common_element_start('li'); + common_element('a', array('rel' => 'tag', + 'href' => common_local_url($this->action, + array('nickname' => $this->owner->nickname, + 'tag' => $tag))), + $tag); + common_element_end('li'); + } + common_element_end('ul'); + } else { + common_text(_('(none)')); + } + common_element_end('dd'); + common_element_end('dl'); + common_element_end('div'); + } if ($user && $user->id == $this->owner->id) { $this->show_owner_controls($this->profile); } - common_element_end('li'); - } + common_element_end('li'); + } /* Override this in subclasses. */ - function show_owner_controls($profile) { + function show_owner_controls($profile) + { return; } - function highlight($text) { - return htmlspecialchars($text); - } + function highlight($text) + { + return htmlspecialchars($text); + } }
\ No newline at end of file diff --git a/lib/queuehandler.php b/lib/queuehandler.php index 23f295c45..9ce9e32b3 100644 --- a/lib/queuehandler.php +++ b/lib/queuehandler.php @@ -25,108 +25,122 @@ require_once(INSTALLDIR.'/lib/daemon.php'); require_once(INSTALLDIR.'/classes/Queue_item.php'); require_once(INSTALLDIR.'/classes/Notice.php'); -class QueueHandler extends Daemon { +class QueueHandler extends Daemon +{ - var $_id = 'generic'; + var $_id = 'generic'; - function QueueHandler($id=NULL) { - if ($id) { - $this->set_id($id); - } - } - - function class_name() { - return ucfirst($this->transport()) . 'Handler'; - } + function QueueHandler($id=null) + { + if ($id) { + $this->set_id($id); + } + } + + function class_name() + { + return ucfirst($this->transport()) . 'Handler'; + } - function name() { - return strtolower($this->class_name().'.'.$this->get_id()); - } - - function get_id() { - return $this->_id; - } + function name() + { + return strtolower($this->class_name().'.'.$this->get_id()); + } + + function get_id() + { + return $this->_id; + } - function set_id($id) { - $this->_id = $id; - } - - function transport() { - return NULL; - } - - function start() { - } - - function finish() { - } + function set_id($id) + { + $this->_id = $id; + } + + function transport() + { + return null; + } + + function start() + { + } + + function finish() + { + } - function handle_notice($notice) { - return true; - } - - function run() { - if (!$this->start()) { - return false; - } - $this->log(LOG_INFO, 'checking for queued notices'); - $transport = $this->transport(); - do { - $qi = Queue_item::top($transport); - if ($qi) { - $this->log(LOG_INFO, 'Got item enqueued '.common_exact_date($qi->created)); - $notice = Notice::staticGet($qi->notice_id); - if ($notice) { - $this->log(LOG_INFO, 'broadcasting notice ID = ' . $notice->id); - # XXX: what to do if broadcast fails? - $result = $this->handle_notice($notice); - if (!$result) { - $this->log(LOG_WARNING, 'Failed broadcast for notice ID = ' . $notice->id); - $orig = $qi; - $qi->claimed = NULL; - $qi->update($orig); - $this->log(LOG_WARNING, 'Abandoned claim for notice ID = ' . $notice->id); - continue; - } - $this->log(LOG_INFO, 'finished broadcasting notice ID = ' . $notice->id); - $notice->free(); - unset($notice); - $notice = NULL; - } else { - $this->log(LOG_WARNING, 'queue item for notice that does not exist'); - } - $qi->delete(); - $qi->free(); - unset($qi); - $this->idle(0); - } else { - $this->clear_old_claims(); - $this->idle(5); - } - } while (true); - if (!$this->finish()) { - return false; - } - return true; - } + function handle_notice($notice) + { + return true; + } + + function run() + { + if (!$this->start()) { + return false; + } + $this->log(LOG_INFO, 'checking for queued notices'); + $transport = $this->transport(); + do { + $qi = Queue_item::top($transport); + if ($qi) { + $this->log(LOG_INFO, 'Got item enqueued '.common_exact_date($qi->created)); + $notice = Notice::staticGet($qi->notice_id); + if ($notice) { + $this->log(LOG_INFO, 'broadcasting notice ID = ' . $notice->id); + # XXX: what to do if broadcast fails? + $result = $this->handle_notice($notice); + if (!$result) { + $this->log(LOG_WARNING, 'Failed broadcast for notice ID = ' . $notice->id); + $orig = $qi; + $qi->claimed = null; + $qi->update($orig); + $this->log(LOG_WARNING, 'Abandoned claim for notice ID = ' . $notice->id); + continue; + } + $this->log(LOG_INFO, 'finished broadcasting notice ID = ' . $notice->id); + $notice->free(); + unset($notice); + $notice = null; + } else { + $this->log(LOG_WARNING, 'queue item for notice that does not exist'); + } + $qi->delete(); + $qi->free(); + unset($qi); + $this->idle(0); + } else { + $this->clear_old_claims(); + $this->idle(5); + } + } while (true); + if (!$this->finish()) { + return false; + } + return true; + } - function idle($timeout=0) { - if ($timeout>0) { - sleep($timeout); - } - } - - function clear_old_claims() { - $qi = new Queue_item(); - $qi->transport = $this->transport(); - $qi->whereAdd('now() - claimed > '.CLAIM_TIMEOUT); - $qi->update(DB_DATAOBJECT_WHEREADD_ONLY); - $qi->free(); - unset($qi); - } - - function log($level, $msg) { - common_log($level, $this->class_name() . ' ('. $this->get_id() .'): '.$msg); - } + function idle($timeout=0) + { + if ($timeout>0) { + sleep($timeout); + } + } + + function clear_old_claims() + { + $qi = new Queue_item(); + $qi->transport = $this->transport(); + $qi->whereAdd('now() - claimed > '.CLAIM_TIMEOUT); + $qi->update(DB_DATAOBJECT_WHEREADD_ONLY); + $qi->free(); + unset($qi); + } + + function log($level, $msg) + { + common_log($level, $this->class_name() . ' ('. $this->get_id() .'): '.$msg); + } } -
\ No newline at end of file +
\ No newline at end of file diff --git a/lib/rssaction.php b/lib/rssaction.php index 777511506..9564cfb46 100644 --- a/lib/rssaction.php +++ b/lib/rssaction.php @@ -21,169 +21,183 @@ if (!defined('LACONICA')) { exit(1); } define('DEFAULT_RSS_LIMIT', 48); -class Rss10Action extends Action { - - # This will contain the details of each feed item's author and be used to generate SIOC data. - var $creators = array(); - - function is_readonly() { - return true; - } - - function handle($args) { - parent::handle($args); - $limit = (int) $this->trimmed('limit'); - if ($limit == 0) { - $limit = DEFAULT_RSS_LIMIT; - } - $this->show_rss($limit); - } - - function init() { - return true; - } - - function get_notices() { - return array(); - } - - function get_channel() { - return array('url' => '', - 'title' => '', - 'link' => '', - 'description' => ''); - } - - function get_image() { - return NULL; - } - - function show_rss($limit=0) { - - if (!$this->init()) { - return; - } - - $notices = $this->get_notices($limit); - - $this->init_rss(); - $this->show_channel($notices); - $this->show_image(); - - foreach ($notices as $n) { - $this->show_item($n); - } - - $this->show_creators(); - $this->end_rss(); - } - - function show_channel($notices) { - - $channel = $this->get_channel(); - $image = $this->get_image(); - - common_element_start('channel', array('rdf:about' => $channel['url'])); - common_element('title', NULL, $channel['title']); - common_element('link', NULL, $channel['link']); - common_element('description', NULL, $channel['description']); - common_element('cc:licence', array('rdf:resource' => common_config('license','url'))); - - if ($image) { - common_element('image', array('rdf:resource' => $image)); - } - - common_element_start('items'); - common_element_start('rdf:Seq'); - - foreach ($notices as $notice) { - common_element('sioct:MicroblogPost', array('rdf:resource' => $notice->uri)); - } - - common_element_end('rdf:Seq'); - common_element_end('items'); - - common_element_end('channel'); - } - - function show_image() { - $image = $this->get_image(); - if ($image) { - $channel = $this->get_channel(); - common_element_start('image', array('rdf:about' => $image)); - common_element('title', NULL, $channel['title']); - common_element('link', NULL, $channel['link']); - common_element('url', NULL, $image); - common_element_end('image'); - } - } - - function show_item($notice) { - $profile = Profile::staticGet($notice->profile_id); - $nurl = common_local_url('shownotice', array('notice' => $notice->id)); - $creator_uri = common_profile_uri($profile); - common_element_start('item', array('rdf:about' => $notice->uri)); - $title = $profile->nickname . ': ' . common_xml_safe_str(trim($notice->content)); - common_element('title', NULL, $title); - common_element('link', NULL, $nurl); - common_element('description', NULL, $profile->nickname."'s status on ".common_exact_date($notice->created)); - common_element('dc:date', NULL, common_date_w3dtf($notice->created)); - common_element('dc:creator', NULL, ($profile->fullname) ? $profile->fullname : $profile->nickname); - common_element('sioc:has_creator', array('rdf:resource' => $creator_uri)); - common_element('laconica:postIcon', array('rdf:resource' => common_profile_avatar_url($profile))); - common_element('cc:licence', array('rdf:resource' => common_config('license', 'url'))); - common_element_end('item'); - $this->creators[$creator_uri] = $profile; - } - - function show_creators() { - foreach ($this->creators as $uri => $profile) { - $id = $profile->id; - $nickname = $profile->nickname; - common_element_start('sioc:User', array('rdf:about' => $uri)); - common_element('foaf:nick', NULL, $nickname); - if ($profile->fullname) { - common_element('foaf:name', NULL, $profile->fullname); - } - common_element('sioc:id', NULL, $id); - $avatar = common_profile_avatar_url($profile); - common_element('sioc:avatar', array('rdf:resource' => $avatar)); - common_element_end('sioc:User'); - } - } - - function init_rss() { - $channel = $this->get_channel(); - header('Content-Type: application/rdf+xml'); - - common_start_xml(); - common_element_start('rdf:RDF', array('xmlns:rdf' => - 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', - 'xmlns:dc' => - 'http://purl.org/dc/elements/1.1/', - 'xmlns:cc' => - 'http://web.resource.org/cc/', +class Rss10Action extends Action +{ + + # This will contain the details of each feed item's author and be used to generate SIOC data. + var $creators = array(); + + function is_readonly() + { + return true; + } + + function handle($args) + { + parent::handle($args); + $limit = (int) $this->trimmed('limit'); + if ($limit == 0) { + $limit = DEFAULT_RSS_LIMIT; + } + $this->show_rss($limit); + } + + function init() + { + return true; + } + + function get_notices() + { + return array(); + } + + function get_channel() + { + return array('url' => '', + 'title' => '', + 'link' => '', + 'description' => ''); + } + + function get_image() + { + return null; + } + + function show_rss($limit=0) + { + + if (!$this->init()) { + return; + } + + $notices = $this->get_notices($limit); + + $this->init_rss(); + $this->show_channel($notices); + $this->show_image(); + + foreach ($notices as $n) { + $this->show_item($n); + } + + $this->show_creators(); + $this->end_rss(); + } + + function show_channel($notices) + { + + $channel = $this->get_channel(); + $image = $this->get_image(); + + common_element_start('channel', array('rdf:about' => $channel['url'])); + common_element('title', null, $channel['title']); + common_element('link', null, $channel['link']); + common_element('description', null, $channel['description']); + common_element('cc:licence', array('rdf:resource' => common_config('license','url'))); + + if ($image) { + common_element('image', array('rdf:resource' => $image)); + } + + common_element_start('items'); + common_element_start('rdf:Seq'); + + foreach ($notices as $notice) { + common_element('sioct:MicroblogPost', array('rdf:resource' => $notice->uri)); + } + + common_element_end('rdf:Seq'); + common_element_end('items'); + + common_element_end('channel'); + } + + function show_image() + { + $image = $this->get_image(); + if ($image) { + $channel = $this->get_channel(); + common_element_start('image', array('rdf:about' => $image)); + common_element('title', null, $channel['title']); + common_element('link', null, $channel['link']); + common_element('url', null, $image); + common_element_end('image'); + } + } + + function show_item($notice) + { + $profile = Profile::staticGet($notice->profile_id); + $nurl = common_local_url('shownotice', array('notice' => $notice->id)); + $creator_uri = common_profile_uri($profile); + common_element_start('item', array('rdf:about' => $notice->uri)); + $title = $profile->nickname . ': ' . common_xml_safe_str(trim($notice->content)); + common_element('title', null, $title); + common_element('link', null, $nurl); + common_element('description', null, $profile->nickname."'s status on ".common_exact_date($notice->created)); + common_element('dc:date', null, common_date_w3dtf($notice->created)); + common_element('dc:creator', null, ($profile->fullname) ? $profile->fullname : $profile->nickname); + common_element('sioc:has_creator', array('rdf:resource' => $creator_uri)); + common_element('laconica:postIcon', array('rdf:resource' => common_profile_avatar_url($profile))); + common_element('cc:licence', array('rdf:resource' => common_config('license', 'url'))); + common_element_end('item'); + $this->creators[$creator_uri] = $profile; + } + + function show_creators() + { + foreach ($this->creators as $uri => $profile) { + $id = $profile->id; + $nickname = $profile->nickname; + common_element_start('sioc:User', array('rdf:about' => $uri)); + common_element('foaf:nick', null, $nickname); + if ($profile->fullname) { + common_element('foaf:name', null, $profile->fullname); + } + common_element('sioc:id', null, $id); + $avatar = common_profile_avatar_url($profile); + common_element('sioc:avatar', array('rdf:resource' => $avatar)); + common_element_end('sioc:User'); + } + } + + function init_rss() + { + $channel = $this->get_channel(); + header('Content-Type: application/rdf+xml'); + + common_start_xml(); + common_element_start('rdf:RDF', array('xmlns:rdf' => + 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', + 'xmlns:dc' => + 'http://purl.org/dc/elements/1.1/', + 'xmlns:cc' => + 'http://web.resource.org/cc/', 'xmlns:content' => 'http://purl.org/rss/1.0/modules/content/', - 'xmlns:foaf' => - 'http://xmlns.com/foaf/0.1/', - 'xmlns:sioc' => - 'http://rdfs.org/sioc/ns#', - 'xmlns:sioct' => - 'http://rdfs.org/sioc/types#', - 'xmlns:laconica' => - 'http://laconi.ca/ont/', - 'xmlns' => 'http://purl.org/rss/1.0/')); - common_element_start('sioc:Site', array('rdf:about' => common_root_url())); - common_element('sioc:name', NULL, common_config('site', 'name')); - common_element_start('sioc:container_of'); - common_element('sioc:Container', array('rdf:about' => - $channel['url'])); - common_element_end('sioc:container_of'); - common_element_end('sioc:Site'); - } - - function end_rss() { - common_element_end('rdf:RDF'); - } + 'xmlns:foaf' => + 'http://xmlns.com/foaf/0.1/', + 'xmlns:sioc' => + 'http://rdfs.org/sioc/ns#', + 'xmlns:sioct' => + 'http://rdfs.org/sioc/types#', + 'xmlns:laconica' => + 'http://laconi.ca/ont/', + 'xmlns' => 'http://purl.org/rss/1.0/')); + common_element_start('sioc:Site', array('rdf:about' => common_root_url())); + common_element('sioc:name', null, common_config('site', 'name')); + common_element_start('sioc:container_of'); + common_element('sioc:Container', array('rdf:about' => + $channel['url'])); + common_element_end('sioc:container_of'); + common_element_end('sioc:Site'); + } + + function end_rss() + { + common_element_end('rdf:RDF'); + } } diff --git a/lib/search_engines.php b/lib/search_engines.php index 7fcc1ffcb..559107910 100644 --- a/lib/search_engines.php +++ b/lib/search_engines.php @@ -19,33 +19,40 @@ if (!defined('LACONICA')) { exit(1); } -class SearchEngine { +class SearchEngine +{ protected $target; protected $table; - function __construct($target, $table) { + function __construct($target, $table) + { $this->target = $target; $this->table = $table; } - function query($q) { + function query($q) + { } - function limit($offset, $count, $rss = false) { + function limit($offset, $count, $rss = false) + { return $this->target->limit($offset, $count); } - function set_sort_mode($mode) { + function set_sort_mode($mode) + { if ('chron' === $mode) return $this->target->orderBy('created desc'); } } -class SphinxSearch extends SearchEngine { +class SphinxSearch extends SearchEngine +{ private $sphinx; private $connected; - function __construct($target, $table) { + function __construct($target, $table) + { $fp = @fsockopen(common_config('sphinx', 'server'), common_config('sphinx', 'port')); if (!$fp) { $this->connected = false; @@ -58,11 +65,13 @@ class SphinxSearch extends SearchEngine { $this->connected = true; } - function is_connected() { + function is_connected() + { return $this->connected; } - function limit($offset, $count, $rss = false) { + function limit($offset, $count, $rss = false) + { //FIXME without LARGEST_POSSIBLE, the most recent results aren't returned // this probably has a large impact on performance $LARGEST_POSSIBLE = 1e6; @@ -78,7 +87,8 @@ class SphinxSearch extends SearchEngine { return $this->target->limit(0, $count); } - function query($q) { + function query($q) + { $result = $this->sphinx->query($q, $this->table); if (!isset($result['matches'])) return false; $id_set = join(', ', array_keys($result['matches'])); @@ -86,7 +96,8 @@ class SphinxSearch extends SearchEngine { return true; } - function set_sort_mode($mode) { + function set_sort_mode($mode) + { if ('chron' === $mode) { $this->sphinx->SetSortMode(SPH_SORT_ATTR_DESC, 'created_ts'); return $this->target->orderBy('created desc'); @@ -94,19 +105,23 @@ class SphinxSearch extends SearchEngine { } } -class MySQLSearch extends SearchEngine { - function query($q) { +class MySQLSearch extends SearchEngine +{ + function query($q) + { if ('identica_people' === $this->table) return $this->target->whereAdd('MATCH(nickname, fullname, location, bio, homepage) ' . - 'against (\''.addslashes($q).'\')'); + 'against (\''.addslashes($q).'\')'); if ('identica_notices' === $this->table) return $this->target->whereAdd('MATCH(content) ' . - 'against (\''.addslashes($q).'\')'); + 'against (\''.addslashes($q).'\')'); } } -class PGSearch extends SearchEngine { - function query($q) { +class PGSearch extends SearchEngine +{ + function query($q) + { if ('identica_people' === $this->table) return $this->target->whereAdd('textsearch @@ plainto_tsquery(\''.addslashes($q).'\')'); if ('identica_notices' === $this->table) diff --git a/lib/searchaction.php b/lib/searchaction.php index f99883b25..bc90fac1a 100644 --- a/lib/searchaction.php +++ b/lib/searchaction.php @@ -19,92 +19,100 @@ if (!defined('LACONICA')) { exit(1); } -class SearchAction extends Action { +class SearchAction extends Action +{ - function is_readonly() { - return true; - } + function is_readonly() + { + return true; + } - function handle($args) { - parent::handle($args); - $this->show_form(); - } + function handle($args) + { + parent::handle($args); + $this->show_form(); + } - function show_top($arr=NULL) { - if ($arr) { - $error = $arr[1]; - } - if ($error) { - common_element('p', 'error', $error); - } else { - $instr = $this->get_instructions(); - $output = common_markup_to_html($instr); - common_element_start('div', 'instructions'); - common_raw($output); - common_element_end('div'); - } - $this->search_menu(); - } + function show_top($arr=null) + { + if ($arr) { + $error = $arr[1]; + } + if ($error) { + common_element('p', 'error', $error); + } else { + $instr = $this->get_instructions(); + $output = common_markup_to_html($instr); + common_element_start('div', 'instructions'); + common_raw($output); + common_element_end('div'); + } + $this->search_menu(); + } - function get_title() { - return NULL; - } + function get_title() + { + return null; + } - function show_header($arr) { - return; - } + function show_header($arr) + { + return; + } - function show_form($error=NULL) { - global $config; + function show_form($error=null) + { + global $config; - $q = $this->trimmed('q'); - $page = $this->trimmed('page', 1); + $q = $this->trimmed('q'); + $page = $this->trimmed('page', 1); - common_show_header($this->get_title(), array($this, 'show_header'), array($q, $error), - array($this, 'show_top')); - common_element_start('form', array('method' => 'get', - 'id' => 'login', - 'action' => common_local_url($this->trimmed('action')))); - common_element_start('p'); - if (!isset($config['site']['fancy']) || !$config['site']['fancy']) { - common_element('input', array('name' => 'action', - 'type' => 'hidden', - 'value' => $this->trimmed('action'))); - } - common_element('input', array('name' => 'q', - 'id' => 'q', - 'type' => 'text', - 'class' => 'input_text', - 'value' => ($q) ? $q : '')); - common_text(' '); - common_element('input', array('type' => 'submit', - 'id' => 'search', - 'name' => 'search', - 'class' => 'submit', - 'value' => _('Search'))); + common_show_header($this->get_title(), array($this, 'show_header'), array($q, $error), + array($this, 'show_top')); + common_element_start('form', array('method' => 'get', + 'id' => 'login', + 'action' => common_local_url($this->trimmed('action')))); + common_element_start('p'); + if (!isset($config['site']['fancy']) || !$config['site']['fancy']) { + common_element('input', array('name' => 'action', + 'type' => 'hidden', + 'value' => $this->trimmed('action'))); + } + common_element('input', array('name' => 'q', + 'id' => 'q', + 'type' => 'text', + 'class' => 'input_text', + 'value' => ($q) ? $q : '')); + common_text(' '); + common_element('input', array('type' => 'submit', + 'id' => 'search', + 'name' => 'search', + 'class' => 'submit', + 'value' => _('Search'))); - common_element_end('p'); - common_element_end('form'); - if ($q) { - $this->show_results($q, $page); - } - common_show_footer(); - } + common_element_end('p'); + common_element_end('form'); + if ($q) { + $this->show_results($q, $page); + } + common_show_footer(); + } - function search_menu() { - # action => array('prompt', 'title', $args) - $action = $this->trimmed('action'); - $menu = - array('peoplesearch' => - array( - _('People'), - _('Find people on this site'), - ($action != 'peoplesearch' && $this->trimmed('q')) ? array('q' => $this->trimmed('q')) : NULL), - 'noticesearch' => - array( _('Text'), - _('Find content of notices'), - ($action != 'noticesearch' && $this->trimmed('q')) ? array('q' => $this->trimmed('q')) : NULL) - ); - $this->nav_menu($menu); - } + function search_menu() + { + # action => array('prompt', 'title', $args) + $action = $this->trimmed('action'); + $menu = + array('peoplesearch' => + array( + _('People'), + _('Find people on this site'), + ($action != 'peoplesearch' && $this->trimmed('q')) ? array('q' => $this->trimmed('q')) : null), + 'noticesearch' => + array( _('Text'), + _('Find content of notices'), + ($action != 'noticesearch' && $this->trimmed('q')) ? array('q' => $this->trimmed('q')) : null) + ); + $this->nav_menu($menu); + } } diff --git a/lib/settingsaction.php b/lib/settingsaction.php index 9e783431f..03bac3a93 100644 --- a/lib/settingsaction.php +++ b/lib/settingsaction.php @@ -19,17 +19,19 @@ if (!defined('LACONICA')) { exit(1); } -class SettingsAction extends Action { +class SettingsAction extends Action +{ - function handle($args) { + function handle($args) + { parent::handle($args); if (!common_logged_in()) { common_user_error(_('Not logged in.')); return; } else if (!common_is_real_login()) { - # Cookie theft means that automatic logins can't - # change important settings or see private info, and - # _all_ our settings are important + # Cookie theft means that automatic logins can't + # change important settings or see private info, and + # _all_ our settings are important common_set_returnto($this->self_url()); common_redirect(common_local_url('login')); } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { @@ -40,75 +42,93 @@ class SettingsAction extends Action { } # override! - function handle_post() { + function handle_post() + { return false; } - function show_form($msg=NULL, $success=false) { + function show_form($msg=null, $success=false) + { return false; } - function message($msg, $success) { + function message($msg, $success) + { if ($msg) { common_element('div', ($success) ? 'success' : 'error', $msg); } } - function form_header($title, $msg=NULL, $success=false) { - common_show_header($title, - NULL, - array($msg, $success), - array($this, 'show_top')); - } + function form_header($title, $msg=NULL, $success=false) + { + common_show_header($title, + array($this, 'show_header'), + array($msg, $success), + array($this, 'show_top')); + } + + function show_header() + { + common_element('link', array('rel' => 'stylesheet', + 'type' => 'text/css', + 'href' => common_path('js/jcrop/jquery.Jcrop.css?version='.LACONICA_VERSION), + 'media' => 'screen, projection, tv')); + common_element('script', array('type' => 'text/javascript', + 'src' => common_path('js/jcrop/jquery.Jcrop.pack.js'))); + common_element('script', array('type' => 'text/javascript', + 'src' => common_path('js/jcrop/jquery.Jcrop.go.js'))); + } - function show_top($arr) { - $msg = $arr[0]; - $success = $arr[1]; - if ($msg) { - $this->message($msg, $success); - } else { - $inst = $this->get_instructions(); - $output = common_markup_to_html($inst); - common_element_start('div', 'instructions'); - common_raw($output); - common_element_end('div'); - } - $this->settings_menu(); - } + function show_top($arr) + { + $msg = $arr[0]; + $success = $arr[1]; + if ($msg) { + $this->message($msg, $success); + } else { + $inst = $this->get_instructions(); + $output = common_markup_to_html($inst); + common_element_start('div', 'instructions'); + common_raw($output); + common_element_end('div'); + } + $this->settings_menu(); + } - function settings_menu() { + function settings_menu() + { # action => array('prompt', 'title') - $menu = - array('profilesettings' => - array(_('Profile'), - _('Change your profile settings')), - 'emailsettings' => - array(_('Email'), - _('Change email handling')), - 'openidsettings' => - array(_('OpenID'), - _('Add or remove OpenIDs')), - 'smssettings' => - array(_('SMS'), - _('Updates by SMS')), - 'imsettings' => - array(_('IM'), - _('Updates by instant messenger (IM)')), - 'twittersettings' => - array(_('Twitter'), - _('Twitter integration options')), - 'othersettings' => - array(_('Other'), - _('Other options'))); - + $menu = + array('profilesettings' => + array(_('Profile'), + _('Change your profile settings')), + 'emailsettings' => + array(_('Email'), + _('Change email handling')), + 'openidsettings' => + array(_('OpenID'), + _('Add or remove OpenIDs')), + 'smssettings' => + array(_('SMS'), + _('Updates by SMS')), + 'imsettings' => + array(_('IM'), + _('Updates by instant messenger (IM)')), + 'twittersettings' => + array(_('Twitter'), + _('Twitter integration options')), + 'othersettings' => + array(_('Other'), + _('Other options'))); + $action = $this->trimmed('action'); common_element_start('ul', array('id' => 'nav_views')); foreach ($menu as $menuaction => $menudesc) { - if ($menuaction == 'imsettings' && - !common_config('xmpp', 'enabled')) { - continue; - } + if ($menuaction == 'imsettings' && + !common_config('xmpp', 'enabled')) { + continue; + } common_menu_item(common_local_url($menuaction), $menudesc[0], $menudesc[1], diff --git a/lib/stream.php b/lib/stream.php index 27ab78137..73758adee 100644 --- a/lib/stream.php +++ b/lib/stream.php @@ -10,11 +10,11 @@ * * 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 + * 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/>. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ if (!defined('LACONICA')) { exit(1); } @@ -22,33 +22,36 @@ if (!defined('LACONICA')) { exit(1); } require_once(INSTALLDIR.'/lib/personal.php'); require_once(INSTALLDIR.'/lib/noticelist.php'); -class StreamAction extends PersonalAction { +class StreamAction extends PersonalAction +{ - function public_views_menu() { + function public_views_menu() + { - $action = $this->trimmed('action'); + $action = $this->trimmed('action'); - common_element_start('ul', array('id' => 'nav_views')); + common_element_start('ul', array('id' => 'nav_views')); - common_menu_item(common_local_url('public'), _('Public'), - _('Public timeline'), $action == 'public'); + common_menu_item(common_local_url('public'), _('Public'), + _('Public timeline'), $action == 'public'); - common_menu_item(common_local_url('tag'), _('Recent tags'), - _('Recent tags'), $action == 'tag'); + common_menu_item(common_local_url('tag'), _('Recent tags'), + _('Recent tags'), $action == 'tag'); - if (count(common_config('nickname', 'featured')) > 0) { - common_menu_item(common_local_url('featured'), _('Featured'), - _('Featured users'), $action == 'featured'); - } + if (count(common_config('nickname', 'featured')) > 0) { + common_menu_item(common_local_url('featured'), _('Featured'), + _('Featured users'), $action == 'featured'); + } - common_menu_item(common_local_url('favorited'), _('Popular'), - _("Popular notices"), $action == 'favorited'); + common_menu_item(common_local_url('favorited'), _('Popular'), + _("Popular notices"), $action == 'favorited'); - common_element_end('ul'); + common_element_end('ul'); - } + } - function show_notice_list($notice) { + function show_notice_list($notice) + { $nl = new NoticeList($notice); return $nl->show(); } diff --git a/lib/subs.php b/lib/subs.php index 91fc8445d..6fa1dcad3 100644 --- a/lib/subs.php +++ b/lib/subs.php @@ -25,15 +25,16 @@ require_once('XMPPHP/XMPP.php'); Returns true or an error message. */ -function subs_subscribe_user($user, $other_nickname) { +function subs_subscribe_user($user, $other_nickname) +{ - $other = User::staticGet('nickname', $other_nickname); + $other = User::staticGet('nickname', $other_nickname); - if (!$other) { - return _('No such user.'); - } + if (!$other) { + return _('No such user.'); + } - return subs_subscribe_to($user, $other); + return subs_subscribe_to($user, $other); } /* Subscribe user $user to other user $other. @@ -41,100 +42,105 @@ function subs_subscribe_user($user, $other_nickname) { * Because the other way is quite a bit more complicated. */ -function subs_subscribe_to($user, $other) { +function subs_subscribe_to($user, $other) +{ - if ($user->isSubscribed($other)) { - return _('Already subscribed!.'); - } + if ($user->isSubscribed($other)) { + return _('Already subscribed!.'); + } if ($other->hasBlocked($user)) { - return _('User has blocked you.'); + return _('User has blocked you.'); } - if (!$user->subscribeTo($other)) { - return _('Could not subscribe.'); - return; - } + if (!$user->subscribeTo($other)) { + return _('Could not subscribe.'); + return; + } subs_notify($other, $user); - if (common_config('memcached', 'enabled')) { - $cache = new Memcache(); - if ($cache->connect(common_config('memcached', 'server'), common_config('memcached', 'port'))) { - $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id)); - } - } - - if ($other->autosubscribe && !$other->isSubscribed($user) && !$user->hasBlocked($other)) { - if (!$other->subscribeTo($user)) { - return _('Could not subscribe other to you.'); - } - if (common_config('memcached', 'enabled')) { - $cache = new Memcache(); - if ($cache->connect(common_config('memcached', 'server'), common_config('memcached', 'port'))) { - $cache->delete(common_cache_key('user:notices_with_friends:' . $other->id)); - } - } - - subs_notify($user, $other); - } - - return true; + if (common_config('memcached', 'enabled')) { + $cache = new Memcache(); + if ($cache->connect(common_config('memcached', 'server'), common_config('memcached', 'port'))) { + $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id)); + } + } + + if ($other->autosubscribe && !$other->isSubscribed($user) && !$user->hasBlocked($other)) { + if (!$other->subscribeTo($user)) { + return _('Could not subscribe other to you.'); + } + if (common_config('memcached', 'enabled')) { + $cache = new Memcache(); + if ($cache->connect(common_config('memcached', 'server'), common_config('memcached', 'port'))) { + $cache->delete(common_cache_key('user:notices_with_friends:' . $other->id)); + } + } + + subs_notify($user, $other); + } + + return true; } -function subs_notify($listenee, $listener) { - # XXX: add other notifications (Jabber, SMS) here - # XXX: queue this and handle it offline - # XXX: Whatever happens, do it in Twitter-like API, too - subs_notify_email($listenee, $listener); +function subs_notify($listenee, $listener) +{ + # XXX: add other notifications (Jabber, SMS) here + # XXX: queue this and handle it offline + # XXX: Whatever happens, do it in Twitter-like API, too + subs_notify_email($listenee, $listener); } -function subs_notify_email($listenee, $listener) { - mail_subscribe_notify($listenee, $listener); +function subs_notify_email($listenee, $listener) +{ + mail_subscribe_notify($listenee, $listener); } /* Unsubscribe $user from nickname $other_nickname Returns true or an error message. */ -function subs_unsubscribe_user($user, $other_nickname) { +function subs_unsubscribe_user($user, $other_nickname) +{ - $other = User::staticGet('nickname', $other_nickname); + $other = User::staticGet('nickname', $other_nickname); - if (!$other) { - return _('No such user.'); - } + if (!$other) { + return _('No such user.'); + } - return subs_unsubscribe_to($user, $other->getProfile()); + return subs_unsubscribe_to($user, $other->getProfile()); } /* Unsubscribe user $user from profile $other * NB: other can be a remote user. */ -function subs_unsubscribe_to($user, $other) { +function subs_unsubscribe_to($user, $other) +{ - if (!$user->isSubscribed($other)) - return _('Not subscribed!.'); + if (!$user->isSubscribed($other)) + return _('Not subscribed!.'); - $sub = DB_DataObject::factory('subscription'); + $sub = DB_DataObject::factory('subscription'); - $sub->subscriber = $user->id; - $sub->subscribed = $other->id; + $sub->subscriber = $user->id; + $sub->subscribed = $other->id; - $sub->find(true); + $sub->find(true); - // note we checked for existence above + // note we checked for existence above - if (!$sub->delete()) - return _('Couldn\'t delete subscription.'); + if (!$sub->delete()) + return _('Couldn\'t delete subscription.'); - if (common_config('memcached', 'enabled')) { - $cache = new Memcache(); - if ($cache->connect(common_config('memcached', 'server'), common_config('memcached', 'port'))) { - $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id)); - } - } + if (common_config('memcached', 'enabled')) { + $cache = new Memcache(); + if ($cache->connect(common_config('memcached', 'server'), common_config('memcached', 'port'))) { + $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id)); + } + } - return true; + return true; } diff --git a/lib/theme.php b/lib/theme.php index 80982aa82..6f365bd99 100644 --- a/lib/theme.php +++ b/lib/theme.php @@ -19,17 +19,19 @@ if (!defined('LACONICA')) { exit(1); } -function theme_file($relative) { - $theme = common_config('site', 'theme'); - return INSTALLDIR.'/theme/'.$theme.'/'.$relative; +function theme_file($relative) +{ + $theme = common_config('site', 'theme'); + return INSTALLDIR.'/theme/'.$theme.'/'.$relative; } -function theme_path($relative) { - $theme = common_config('site', 'theme'); - $server = common_config('theme', 'server'); - if ($server) { - return 'http://'.$server.'/'.$theme.'/'.$relative; - } else { - return common_path('theme/'.$theme.'/'.$relative); - } +function theme_path($relative) +{ + $theme = common_config('site', 'theme'); + $server = common_config('theme', 'server'); + if ($server) { + return 'http://'.$server.'/'.$theme.'/'.$relative; + } else { + return common_path('theme/'.$theme.'/'.$relative); + } }
\ No newline at end of file diff --git a/lib/twitter.php b/lib/twitter.php index 7f75a1afd..5eb15005a 100644 --- a/lib/twitter.php +++ b/lib/twitter.php @@ -19,184 +19,190 @@ if (!defined('LACONICA')) { exit(1); } -function get_twitter_data($uri, $screen_name, $password) { - - $options = array( - CURLOPT_USERPWD => sprintf("%s:%s", $screen_name, $password), - CURLOPT_RETURNTRANSFER => true, - CURLOPT_FAILONERROR => true, - CURLOPT_HEADER => false, - CURLOPT_FOLLOWLOCATION => true, - // CURLOPT_USERAGENT => "identi.ca", - CURLOPT_CONNECTTIMEOUT => 120, +function get_twitter_data($uri, $screen_name, $password) +{ + + $options = array( + CURLOPT_USERPWD => sprintf("%s:%s", $screen_name, $password), + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FAILONERROR => true, + CURLOPT_HEADER => false, + CURLOPT_FOLLOWLOCATION => true, + # CURLOPT_USERAGENT => "identi.ca", + CURLOPT_CONNECTTIMEOUT => 120, CURLOPT_TIMEOUT => 120, - # Twitter is strict about accepting invalid "Expect" headers CURLOPT_HTTPHEADER => array('Expect:') - ); + ); - $ch = curl_init($uri); + $ch = curl_init($uri); curl_setopt_array($ch, $options); $data = curl_exec($ch); $errmsg = curl_error($ch); - if ($errmsg) { - common_debug("Twitter bridge - cURL error: $errmsg - trying to load: $uri with user $twit_user.", - __FILE__); - } + if ($errmsg) { + common_debug("Twitter bridge - cURL error: $errmsg - trying to load: $uri with user $twit_user.", + __FILE__); + } - curl_close($ch); + curl_close($ch); - return $data; + return $data; } -function twitter_user_info($screen_name, $password) { +function twitter_user_info($screen_name, $password) +{ - $uri = "http://twitter.com/users/show/$screen_name.json"; - $data = get_twitter_data($uri, $screen_name, $password); + $uri = "http://twitter.com/users/show/$screen_name.json"; + $data = get_twitter_data($uri, $screen_name, $password); - if (!$data) { - return false; - } + if (!$data) { + return false; + } - $twit_user = json_decode($data); + $twit_user = json_decode($data); - if (!$twit_user) { - return false; - } + if (!$twit_user) { + return false; + } - return $twit_user; + return $twit_user; } -function update_twitter_user($fuser, $twitter_id, $screen_name) { +function update_twitter_user($fuser, $twitter_id, $screen_name) +{ - $original = clone($fuser); - $fuser->nickname = $screen_name; - $fuser->uri = 'http://twitter.com/' . $screen_name; - $result = $fuser->updateKeys($original); + $original = clone($fuser); + $fuser->nickname = $screen_name; + $fuser->uri = 'http://twitter.com/' . $screen_name; + $result = $fuser->updateKeys($original); - if (!$result) { - common_log_db_error($fuser, 'UPDATE', __FILE__); - return false; - } + if (!$result) { + common_log_db_error($fuser, 'UPDATE', __FILE__); + return false; + } - return true; + return true; } -function add_twitter_user($twitter_id, $screen_name) { +function add_twitter_user($twitter_id, $screen_name) +{ - // Otherwise, create a new Twitter user - $fuser = DB_DataObject::factory('foreign_user'); + // Otherwise, create a new Twitter user + $fuser = DB_DataObject::factory('foreign_user'); - $fuser->nickname = $screen_name; - $fuser->uri = 'http://twitter.com/' . $screen_name; - $fuser->id = $twitter_id; - $fuser->service = 1; // Twitter - $fuser->created = common_sql_now(); - $result = $fuser->insert(); + $fuser->nickname = $screen_name; + $fuser->uri = 'http://twitter.com/' . $screen_name; + $fuser->id = $twitter_id; + $fuser->service = 1; // Twitter + $fuser->created = common_sql_now(); + $result = $fuser->insert(); - if (!$result) { - common_debug("Twitter bridge - failed to add new Twitter user: $twitter_id - $screen_name."); - common_log_db_error($fuser, 'INSERT', __FILE__); - return false; - } + if (!$result) { + common_debug("Twitter bridge - failed to add new Twitter user: $twitter_id - $screen_name."); + common_log_db_error($fuser, 'INSERT', __FILE__); + return false; + } - common_debug("Twitter bridge - Added new Twitter user: $screen_name ($twitter_id)."); + common_debug("Twitter bridge - Added new Twitter user: $screen_name ($twitter_id)."); - return true; + return true; } // Creates or Updates a Twitter user -function save_twitter_user($twitter_id, $screen_name) { +function save_twitter_user($twitter_id, $screen_name) +{ - // Check to see whether the Twitter user is already in the system, - // and update its screen name and uri if so. - $fuser = Foreign_user::getForeignUser($twitter_id, 1); + // Check to see whether the Twitter user is already in the system, + // and update its screen name and uri if so. + $fuser = Foreign_user::getForeignUser($twitter_id, 1); - if ($fuser) { + if ($fuser) { - // Only update if Twitter screen name has changed - if ($fuser->nickname != $screen_name) { + // Only update if Twitter screen name has changed + if ($fuser->nickname != $screen_name) { - common_debug('Twitter bridge - Updated nickname (and URI) for Twitter user ' . - "$fuser->id to $screen_name, was $fuser->nickname"); + common_debug('Twitter bridge - Updated nickname (and URI) for Twitter user ' . + "$fuser->id to $screen_name, was $fuser->nickname"); - return update_twitter_user($fuser, $twitter_id, $screen_name); - } + return update_twitter_user($fuser, $twitter_id, $screen_name); + } - } else { - return add_twitter_user($twitter_id, $screen_name); - } + } else { + return add_twitter_user($twitter_id, $screen_name); + } - return true; + return true; } -function retreive_twitter_friends($twitter_id, $screen_name, $password) { +function retreive_twitter_friends($twitter_id, $screen_name, $password) +{ - $uri = "http://twitter.com/statuses/friends/$twitter_id.json?page="; - $twitter_user = twitter_user_info($screen_name, $password); + $uri = "http://twitter.com/statuses/friends/$twitter_id.json?page="; + $twitter_user = twitter_user_info($screen_name, $password); - // Calculate how many pages to get... - $pages = ceil($twitter_user->friends_count / 100); + // Calculate how many pages to get... + $pages = ceil($twitter_user->friends_count / 100); - if ($pages == 0) { - common_debug("Twitter bridge - Twitter user $screen_name has no friends! Lame."); - } + if ($pages == 0) { + common_debug("Twitter bridge - Twitter user $screen_name has no friends! Lame."); + } - $friends = array(); + $friends = array(); - for ($i = 1; $i <= $pages; $i++) { + for ($i = 1; $i <= $pages; $i++) { - $data = get_twitter_data($uri . $i, $screen_name, $password); + $data = get_twitter_data($uri . $i, $screen_name, $password); - if (!$data) { - return NULL; - } + if (!$data) { + return null; + } - $more_friends = json_decode($data); + $more_friends = json_decode($data); - if (!$more_friends) { - return NULL; - } + if (!$more_friends) { + return null; + } - $friends = array_merge($friends, $more_friends); - } + $friends = array_merge($friends, $more_friends); + } - return $friends; + return $friends; } -function save_twitter_friends($user, $twitter_id, $screen_name, $password) { +function save_twitter_friends($user, $twitter_id, $screen_name, $password) +{ - $friends = retreive_twitter_friends($twitter_id, $screen_name, $password); + $friends = retreive_twitter_friends($twitter_id, $screen_name, $password); - if (is_null($friends)) { - common_debug("Twitter bridge - Couldn't get friends data from Twitter."); - return false; - } + if (is_null($friends)) { + common_debug("Twitter bridge - Couldn't get friends data from Twitter."); + return false; + } foreach ($friends as $friend) { - $friend_name = $friend->screen_name; - $friend_id = $friend->id; + $friend_name = $friend->screen_name; + $friend_id = $friend->id; - // Update or create the Foreign_user record - if (!save_twitter_user($friend_id, $friend_name)) { - return false; - } + // Update or create the Foreign_user record + if (!save_twitter_user($friend_id, $friend_name)) { + return false; + } - // Check to see if there's a related local user - $flink = Foreign_link::getByForeignID($friend_id, 1); + // Check to see if there's a related local user + $flink = Foreign_link::getByForeignID($friend_id, 1); - if ($flink) { + if ($flink) { - // Get associated user and subscribe her - $friend_user = User::staticGet('id', $flink->user_id); - subs_subscribe_to($user, $friend_user); - common_debug("Twitter bridge - subscribed $friend_user->nickname to $user->nickname."); - } - } + // Get associated user and subscribe her + $friend_user = User::staticGet('id', $flink->user_id); + subs_subscribe_to($user, $friend_user); + common_debug("Twitter bridge - subscribed $friend_user->nickname to $user->nickname."); + } + } - return true; + return true; } diff --git a/lib/twitterapi.php b/lib/twitterapi.php index 2eb127525..50bcb06fe 100644 --- a/lib/twitterapi.php +++ b/lib/twitterapi.php @@ -19,639 +19,675 @@ if (!defined('LACONICA')) { exit(1); } -class TwitterapiAction extends Action { +class TwitterapiAction extends Action +{ - var $auth_user; + var $auth_user; - function handle($args) { - parent::handle($args); - } + function handle($args) + { + parent::handle($args); + } - function twitter_user_array($profile, $get_notice=false) { + function twitter_user_array($profile, $get_notice=false) + { - $twitter_user = array(); + $twitter_user = array(); - $twitter_user['name'] = $profile->getBestName(); - $twitter_user['followers_count'] = $this->count_subscriptions($profile); - $twitter_user['screen_name'] = $profile->nickname; - $twitter_user['description'] = ($profile->bio) ? $profile->bio : NULL; - $twitter_user['location'] = ($profile->location) ? $profile->location : NULL; - $twitter_user['id'] = intval($profile->id); + $twitter_user['name'] = $profile->getBestName(); + $twitter_user['followers_count'] = $this->count_subscriptions($profile); + $twitter_user['screen_name'] = $profile->nickname; + $twitter_user['description'] = ($profile->bio) ? $profile->bio : null; + $twitter_user['location'] = ($profile->location) ? $profile->location : null; + $twitter_user['id'] = intval($profile->id); - $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE); + $avatar = $profile->getAvatar(AVATAR_STREAM_SIZE); - $twitter_user['profile_image_url'] = ($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_STREAM_SIZE); - $twitter_user['protected'] = 'false'; # not supported by Laconica yet - $twitter_user['url'] = ($profile->homepage) ? $profile->homepage : NULL; + $twitter_user['profile_image_url'] = ($avatar) ? common_avatar_display_url($avatar) : common_default_avatar(AVATAR_STREAM_SIZE); + $twitter_user['protected'] = 'false'; # not supported by Laconica yet + $twitter_user['url'] = ($profile->homepage) ? $profile->homepage : null; - if ($get_notice) { - $notice = $profile->getCurrentNotice(); - if ($notice) { - # don't get user! - $twitter_user['status'] = $this->twitter_status_array($notice, false); - } - } + if ($get_notice) { + $notice = $profile->getCurrentNotice(); + if ($notice) { + # don't get user! + $twitter_user['status'] = $this->twitter_status_array($notice, false); + } + } - return $twitter_user; - } + return $twitter_user; + } - function twitter_status_array($notice, $include_user=true) { + function twitter_status_array($notice, $include_user=true) + { - $profile = $notice->getProfile(); + $profile = $notice->getProfile(); - $twitter_status = array(); - $twitter_status['text'] = $notice->content; - $twitter_status['truncated'] = 'false'; # Not possible on Laconica - $twitter_status['created_at'] = $this->date_twitter($notice->created); - $twitter_status['in_reply_to_status_id'] = ($notice->reply_to) ? intval($notice->reply_to) : NULL; - $twitter_status['source'] = $this->source_link($notice->source); - $twitter_status['id'] = intval($notice->id); - $twitter_status['in_reply_to_user_id'] = ($notice->reply_to) ? $this->replier_by_reply(intval($notice->reply_to)) : NULL; + $twitter_status = array(); + $twitter_status['text'] = $notice->content; + $twitter_status['truncated'] = 'false'; # Not possible on Laconica + $twitter_status['created_at'] = $this->date_twitter($notice->created); + $twitter_status['in_reply_to_status_id'] = ($notice->reply_to) ? intval($notice->reply_to) : null; + $twitter_status['source'] = $this->source_link($notice->source); + $twitter_status['id'] = intval($notice->id); + $twitter_status['in_reply_to_user_id'] = ($notice->reply_to) ? $this->replier_by_reply(intval($notice->reply_to)) : null; - if (isset($this->auth_user)) { - $twitter_status['favorited'] = ($this->auth_user->hasFave($notice)) ? 'true' : 'false'; - } else { - $twitter_status['favorited'] = 'false'; - } + if (isset($this->auth_user)) { + $twitter_status['favorited'] = ($this->auth_user->hasFave($notice)) ? 'true' : 'false'; + } else { + $twitter_status['favorited'] = 'false'; + } - if ($include_user) { - # Don't get notice (recursive!) - $twitter_user = $this->twitter_user_array($profile, false); - $twitter_status['user'] = $twitter_user; - } + if ($include_user) { + # Don't get notice (recursive!) + $twitter_user = $this->twitter_user_array($profile, false); + $twitter_status['user'] = $twitter_user; + } - return $twitter_status; - } + return $twitter_status; + } - function twitter_rss_entry_array($notice) { + function twitter_rss_entry_array($notice) + { - $profile = $notice->getProfile(); + $profile = $notice->getProfile(); - $server = common_config('site', 'server'); - $entry = array(); + $server = common_config('site', 'server'); + $entry = array(); # We trim() to avoid extraneous whitespace in the output - $entry['content'] = common_xml_safe_str(trim($notice->rendered)); - $entry['title'] = $profile->nickname . ': ' . common_xml_safe_str(trim($notice->content)); - $entry['link'] = common_local_url('shownotice', array('notice' => $notice->id)); - $entry['published'] = common_date_iso8601($notice->created); - $entry['id'] = "tag:$server,2008:$entry[link]"; - $entry['updated'] = $entry['published']; - - # RSS Item specific - $entry['description'] = $entry['content']; - $entry['pubDate'] = common_date_rfc2822($notice->created); - $entry['guid'] = $entry['link']; - - return $entry; - } - - function twitter_rss_dmsg_array($message) { - - $server = common_config('site', 'server'); - $entry = array(); - - $entry['title'] = sprintf('Message from %s to %s', - $message->getFrom()->nickname, $message->getTo()->nickname); - - $entry['content'] = common_xml_safe_str(trim($message->content)); - $entry['link'] = common_local_url('showmessage', array('message' => $message->id)); - $entry['published'] = common_date_iso8601($message->created); - $entry['id'] = "tag:$server,2008:$entry[link]"; - $entry['updated'] = $entry['published']; - - # RSS Item specific - $entry['description'] = $entry['content']; - $entry['pubDate'] = common_date_rfc2822($message->created); - $entry['guid'] = $entry['link']; - - return $entry; - } - - function twitter_dmsg_array($message) { - - $twitter_dm = array(); - - $from_profile = $message->getFrom(); - $to_profile = $message->getTo(); - - $twitter_dm['id'] = $message->id; - $twitter_dm['sender_id'] = $message->from_profile; - $twitter_dm['text'] = trim($message->content); - $twitter_dm['recipient_id'] = $message->to_profile; - $twitter_dm['created_at'] = $this->date_twitter($message->created); - $twitter_dm['sender_screen_name'] = $from_profile->nickname; - $twitter_dm['recipient_screen_name'] = $to_profile->nickname; - $twitter_dm['sender'] = $this->twitter_user_array($from_profile, false); - $twitter_dm['recipient'] = $this->twitter_user_array($to_profile, false); - - return $twitter_dm; - } - - function show_twitter_xml_status($twitter_status) { - common_element_start('status'); - foreach($twitter_status as $element => $value) { - switch ($element) { - case 'user': - $this->show_twitter_xml_user($twitter_status['user']); - break; - case 'text': - common_element($element, NULL, common_xml_safe_str($value)); - break; - default: - common_element($element, NULL, $value); - } - } - common_element_end('status'); - } - - function show_twitter_xml_user($twitter_user, $role='user') { - common_element_start($role); - foreach($twitter_user as $element => $value) { - if ($element == 'status') { - $this->show_twitter_xml_status($twitter_user['status']); - } else { - common_element($element, NULL, $value); - } - } - common_element_end($role); - } - - function show_twitter_rss_item($entry) { - common_element_start('item'); - common_element('title', NULL, $entry['title']); - common_element('description', NULL, $entry['description']); - common_element('pubDate', NULL, $entry['pubDate']); - common_element('guid', NULL, $entry['guid']); - common_element('link', NULL, $entry['link']); - common_element_end('item'); - } - - function show_twitter_atom_entry($entry) { - common_element_start('entry'); - common_element('title', NULL, $entry['title']); - common_element('content', array('type' => 'html'), $entry['content']); - common_element('id', NULL, $entry['id']); - common_element('published', NULL, $entry['published']); - common_element('updated', NULL, $entry['updated']); - common_element('link', array('href' => $entry['link'], 'rel' => 'alternate', 'type' => 'text/html'), NULL); - common_element_end('entry'); - } - - function show_json_objects($objects) { - print(json_encode($objects)); - } - - function show_single_xml_status($notice) { - $this->init_document('xml'); - $twitter_status = $this->twitter_status_array($notice); - $this->show_twitter_xml_status($twitter_status); - $this->end_document('xml'); - } - - function show_single_json_status($notice) { - $this->init_document('json'); - $status = $this->twitter_status_array($notice); - $this->show_json_objects($status); - $this->end_document('json'); - } - - function show_single_xml_dmsg($message) { - $this->init_document('xml'); - $dmsg = $this->twitter_dmsg_array($message); - $this->show_twitter_xml_dmsg($dmsg); - $this->end_document('xml'); - } - - function show_single_json_dmsg($message) { - $this->init_document('json'); - $dmsg = $this->twitter_dmsg_array($message); - $this->show_json_objects($dmsg); - $this->end_document('json'); - } - - function show_twitter_xml_dmsg($twitter_dm) { - common_element_start('direct_message'); - foreach($twitter_dm as $element => $value) { - switch ($element) { - case 'sender': - case 'recipient': - $this->show_twitter_xml_user($value, $element); - break; - case 'text': - common_element($element, NULL, common_xml_safe_str($value)); - break; - default: - common_element($element, NULL, $value); - } - } - common_element_end('direct_message'); - } - - function show_xml_timeline($notice) { - - $this->init_document('xml'); - common_element_start('statuses', array('type' => 'array')); - - if (is_array($notice)) { - foreach ($notice as $n) { - $twitter_status = $this->twitter_status_array($n); - $this->show_twitter_xml_status($twitter_status); - } - } else { - while ($notice->fetch()) { - $twitter_status = $this->twitter_status_array($notice); - $this->show_twitter_xml_status($twitter_status); - } - } - - common_element_end('statuses'); - $this->end_document('xml'); - } - - function show_rss_timeline($notice, $title, $link, $subtitle, $suplink=NULL) { - - $this->init_document('rss'); - - common_element_start('channel'); - common_element('title', NULL, $title); - common_element('link', NULL, $link); - if (!is_null($suplink)) { - # For FriendFeed's SUP protocol - common_element('link', array('xmlns' => 'http://www.w3.org/2005/Atom', - 'rel' => 'http://api.friendfeed.com/2008/03#sup', - 'href' => $suplink, - 'type' => 'application/json')); - } - common_element('description', NULL, $subtitle); - common_element('language', NULL, 'en-us'); - common_element('ttl', NULL, '40'); - - if (is_array($notice)) { - foreach ($notice as $n) { - $entry = $this->twitter_rss_entry_array($n); - $this->show_twitter_rss_item($entry); - } - } else { - while ($notice->fetch()) { - $entry = $this->twitter_rss_entry_array($notice); - $this->show_twitter_rss_item($entry); - } - } - - common_element_end('channel'); - $this->end_twitter_rss(); - } - - function show_atom_timeline($notice, $title, $id, $link, $subtitle=NULL, $suplink=NULL) { - - $this->init_document('atom'); - - common_element('title', NULL, $title); - common_element('id', NULL, $id); - common_element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), NULL); - if (!is_null($suplink)) { - # For FriendFeed's SUP protocol - common_element('link', array('rel' => 'http://api.friendfeed.com/2008/03#sup', - 'href' => $suplink, - 'type' => 'application/json')); - } - common_element('subtitle', NULL, $subtitle); - - if (is_array($notice)) { - foreach ($notice as $n) { - $entry = $this->twitter_rss_entry_array($n); - $this->show_twitter_atom_entry($entry); - } - } else { - while ($notice->fetch()) { - $entry = $this->twitter_rss_entry_array($notice); - $this->show_twitter_atom_entry($entry); - } - } - - $this->end_document('atom'); - - } - - function show_json_timeline($notice) { - - $this->init_document('json'); - - $statuses = array(); - - if (is_array($notice)) { - foreach ($notice as $n) { - $twitter_status = $this->twitter_status_array($n); - array_push($statuses, $twitter_status); - } - } else { - while ($notice->fetch()) { - $twitter_status = $this->twitter_status_array($notice); - array_push($statuses, $twitter_status); - } - } - - $this->show_json_objects($statuses); - - $this->end_document('json'); - } - - // Anyone know what date format this is? - // Twitter's dates look like this: "Mon Jul 14 23:52:38 +0000 2008" -- Zach - function date_twitter($dt) { - $t = strtotime($dt); - return date("D M d G:i:s O Y", $t); - } - - function replier_by_reply($reply_id) { - $notice = Notice::staticGet($reply_id); - if ($notice) { - $profile = $notice->getProfile(); - if ($profile) { - return intval($profile->id); - } else { - common_debug('Can\'t find a profile for notice: ' . $notice->id, __FILE__); - } - } else { - common_debug("Can't get notice: $reply_id", __FILE__); - } - return NULL; - } - - // XXX: Candidate for a general utility method somewhere? - function count_subscriptions($profile) { - - $count = 0; - $sub = new Subscription(); - $sub->subscribed = $profile->id; - - $count = $sub->find(); - - if ($count > 0) { - return $count - 1; - } else { - return 0; - } - } - - function init_document($type='xml') { - switch ($type) { - case 'xml': - header('Content-Type: application/xml; charset=utf-8'); - common_start_xml(); - break; - case 'json': - header('Content-Type: application/json; charset=utf-8'); - - // Check for JSONP callback - $callback = $this->arg('callback'); - if ($callback) { - print $callback . '('; - } - break; - case 'rss': - header("Content-Type: application/rss+xml; charset=utf-8"); - $this->init_twitter_rss(); - break; - case 'atom': - header('Content-Type: application/atom+xml; charset=utf-8'); - $this->init_twitter_atom(); - break; - default: - $this->client_error(_('Not a supported data format.')); - break; - } - - return; - } - - function end_document($type='xml') { - switch ($type) { - case 'xml': - common_end_xml(); - break; - case 'json': - - // Check for JSONP callback - $callback = $this->arg('callback'); - if ($callback) { - print ')'; - } - break; - case 'rss': - $this->end_twitter_rss(); - break; - case 'atom': - $this->end_twitter_rss(); - break; - default: - $this->client_error(_('Not a supported data format.')); - break; - } - return; - } - - function client_error($msg, $code = 400, $content_type = 'json') { - - static $status = array(400 => 'Bad Request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Long', - 415 => 'Unsupported Media Type', - 416 => 'Requested Range Not Satisfiable', - 417 => 'Expectation Failed'); - - $action = $this->trimmed('action'); - - common_debug("User error '$code' on '$action': $msg", __FILE__); - - if (!array_key_exists($code, $status)) { - $code = 400; - } - - $status_string = $status[$code]; - header('HTTP/1.1 '.$code.' '.$status_string); - - if ($content_type == 'xml') { - $this->init_document('xml'); - common_element_start('hash'); - common_element('error', NULL, $msg); - common_element('request', NULL, $_SERVER['REQUEST_URI']); - common_element_end('hash'); - $this->end_document('xml'); - } else { - $this->init_document('json'); - $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']); - print(json_encode($error_array)); - $this->end_document('json'); - } - - } - - function init_twitter_rss() { - common_start_xml(); - common_element_start('rss', array('version' => '2.0')); - } - - function end_twitter_rss() { - common_element_end('rss'); - common_end_xml(); - } - - function init_twitter_atom() { - common_start_xml(); - common_element_start('feed', array('xmlns' => 'http://www.w3.org/2005/Atom', 'xml:lang' => 'en-US')); - } - - function end_twitter_atom() { - common_end_xml(); - common_element_end('feed'); - } - - function show_profile($profile, $content_type='xml', $notice=NULL) { - $profile_array = $this->twitter_user_array($profile, true); - switch ($content_type) { - case 'xml': - $this->show_twitter_xml_user($profile_array); - break; - case 'json': - $this->show_json_objects($profile_array); - break; - default: - $this->client_error(_('Not a supported data format.')); - return; - } - return; - } - - function get_user($id, $apidata=NULL) { - if (!$id) { - return $apidata['user']; - } else if (is_numeric($id)) { - return User::staticGet($id); - } else { - $nickname = common_canonical_nickname($id); - return User::staticGet('nickname', $nickname); - } - } - - function get_profile($id) { - if (is_numeric($id)) { - return Profile::staticGet($id); - } else { - $user = User::staticGet('nickname', $id); - if ($user) { - return $user->getProfile(); - } else { - return NULL; - } - } - } - - function source_link($source) { - $source_name = _($source); - switch ($source) { - case 'web': - case 'xmpp': - case 'mail': - case 'omb': - case 'api': - break; - default: - $ns = Notice_source::staticGet($source); - if ($ns) { - $source_name = '<a href="' . $ns->url . '">' . $ns->name . '</a>'; - } - break; - } - return $source_name; - } - - function show_extended_profile($user, $apidata) { - - $this->auth_user = $apidata['user']; - - $profile = $user->getProfile(); - - if (!$profile) { - common_server_error(_('User has no profile.')); - return; - } - - $twitter_user = $this->twitter_user_array($profile, true); - - // Add in extended user fields offered up by this method - $twitter_user['created_at'] = $this->date_twitter($profile->created); - - $subbed = DB_DataObject::factory('subscription'); - $subbed->subscriber = $profile->id; - $subbed_count = (int) $subbed->count() - 1; - - $notices = DB_DataObject::factory('notice'); - $notices->profile_id = $profile->id; - $notice_count = (int) $notices->count(); - - $twitter_user['friends_count'] = (is_int($subbed_count)) ? $subbed_count : 0; - $twitter_user['statuses_count'] = (is_int($notice_count)) ? $notice_count : 0; - - // Other fields Twitter sends... - $twitter_user['profile_background_color'] = ''; - $twitter_user['profile_text_color'] = ''; - $twitter_user['profile_link_color'] = ''; - $twitter_user['profile_sidebar_fill_color'] = ''; - - $faves = DB_DataObject::factory('fave'); - $faves->user_id = $user->id; - $faves_count = (int) $faves->count(); - $twitter_user['favourites_count'] = $faves_count; - - $timezone = 'UTC'; - - if ($user->timezone) { - $timezone = $user->timezone; - } - - $t = new DateTime; - $t->setTimezone(new DateTimeZone($timezone)); - $twitter_user['utc_offset'] = $t->format('Z'); - $twitter_user['time_zone'] = $timezone; - - $following = 'false'; - - if (isset($this->auth_user)) { - if ($this->auth_user->isSubscribed($profile)) { - $following = 'true'; - } - - // Not implemented yet - $twitter_user['notifications'] = 'false'; - } - - $twitter_user['following'] = $following; - - if ($apidata['content-type'] == 'xml') { - $this->init_document('xml'); - $this->show_twitter_xml_user($twitter_user); - $this->end_document('xml'); - } elseif ($apidata['content-type'] == 'json') { - $this->init_document('json'); - $this->show_json_objects($twitter_user); - $this->end_document('json'); - } - - } + $entry['content'] = common_xml_safe_str(trim($notice->rendered)); + $entry['title'] = $profile->nickname . ': ' . common_xml_safe_str(trim($notice->content)); + $entry['link'] = common_local_url('shownotice', array('notice' => $notice->id)); + $entry['published'] = common_date_iso8601($notice->created); + $entry['id'] = "tag:$server,2008:$entry[link]"; + $entry['updated'] = $entry['published']; + + # RSS Item specific + $entry['description'] = $entry['content']; + $entry['pubDate'] = common_date_rfc2822($notice->created); + $entry['guid'] = $entry['link']; + + return $entry; + } + + function twitter_rss_dmsg_array($message) + { + + $server = common_config('site', 'server'); + $entry = array(); + + $entry['title'] = sprintf('Message from %s to %s', + $message->getFrom()->nickname, $message->getTo()->nickname); + + $entry['content'] = common_xml_safe_str(trim($message->content)); + $entry['link'] = common_local_url('showmessage', array('message' => $message->id)); + $entry['published'] = common_date_iso8601($message->created); + $entry['id'] = "tag:$server,2008:$entry[link]"; + $entry['updated'] = $entry['published']; + + # RSS Item specific + $entry['description'] = $entry['content']; + $entry['pubDate'] = common_date_rfc2822($message->created); + $entry['guid'] = $entry['link']; + + return $entry; + } + + function twitter_dmsg_array($message) + { + + $twitter_dm = array(); + + $from_profile = $message->getFrom(); + $to_profile = $message->getTo(); + + $twitter_dm['id'] = $message->id; + $twitter_dm['sender_id'] = $message->from_profile; + $twitter_dm['text'] = trim($message->content); + $twitter_dm['recipient_id'] = $message->to_profile; + $twitter_dm['created_at'] = $this->date_twitter($message->created); + $twitter_dm['sender_screen_name'] = $from_profile->nickname; + $twitter_dm['recipient_screen_name'] = $to_profile->nickname; + $twitter_dm['sender'] = $this->twitter_user_array($from_profile, false); + $twitter_dm['recipient'] = $this->twitter_user_array($to_profile, false); + + return $twitter_dm; + } + + function show_twitter_xml_status($twitter_status) + { + common_element_start('status'); + foreach($twitter_status as $element => $value) { + switch ($element) { + case 'user': + $this->show_twitter_xml_user($twitter_status['user']); + break; + case 'text': + common_element($element, null, common_xml_safe_str($value)); + break; + default: + common_element($element, null, $value); + } + } + common_element_end('status'); + } + + function show_twitter_xml_user($twitter_user, $role='user') + { + common_element_start($role); + foreach($twitter_user as $element => $value) { + if ($element == 'status') { + $this->show_twitter_xml_status($twitter_user['status']); + } else { + common_element($element, null, $value); + } + } + common_element_end($role); + } + + function show_twitter_rss_item($entry) + { + common_element_start('item'); + common_element('title', null, $entry['title']); + common_element('description', null, $entry['description']); + common_element('pubDate', null, $entry['pubDate']); + common_element('guid', null, $entry['guid']); + common_element('link', null, $entry['link']); + common_element_end('item'); + } + + function show_twitter_atom_entry($entry) + { + common_element_start('entry'); + common_element('title', null, $entry['title']); + common_element('content', array('type' => 'html'), $entry['content']); + common_element('id', null, $entry['id']); + common_element('published', null, $entry['published']); + common_element('updated', null, $entry['updated']); + common_element('link', array('href' => $entry['link'], 'rel' => 'alternate', 'type' => 'text/html'), null); + common_element_end('entry'); + } + + function show_json_objects($objects) + { + print(json_encode($objects)); + } + + function show_single_xml_status($notice) + { + $this->init_document('xml'); + $twitter_status = $this->twitter_status_array($notice); + $this->show_twitter_xml_status($twitter_status); + $this->end_document('xml'); + } + + function show_single_json_status($notice) + { + $this->init_document('json'); + $status = $this->twitter_status_array($notice); + $this->show_json_objects($status); + $this->end_document('json'); + } + + function show_single_xml_dmsg($message) + { + $this->init_document('xml'); + $dmsg = $this->twitter_dmsg_array($message); + $this->show_twitter_xml_dmsg($dmsg); + $this->end_document('xml'); + } + + function show_single_json_dmsg($message) + { + $this->init_document('json'); + $dmsg = $this->twitter_dmsg_array($message); + $this->show_json_objects($dmsg); + $this->end_document('json'); + } + + function show_twitter_xml_dmsg($twitter_dm) + { + common_element_start('direct_message'); + foreach($twitter_dm as $element => $value) { + switch ($element) { + case 'sender': + case 'recipient': + $this->show_twitter_xml_user($value, $element); + break; + case 'text': + common_element($element, null, common_xml_safe_str($value)); + break; + default: + common_element($element, null, $value); + } + } + common_element_end('direct_message'); + } + + function show_xml_timeline($notice) + { + + $this->init_document('xml'); + common_element_start('statuses', array('type' => 'array')); + + if (is_array($notice)) { + foreach ($notice as $n) { + $twitter_status = $this->twitter_status_array($n); + $this->show_twitter_xml_status($twitter_status); + } + } else { + while ($notice->fetch()) { + $twitter_status = $this->twitter_status_array($notice); + $this->show_twitter_xml_status($twitter_status); + } + } + + common_element_end('statuses'); + $this->end_document('xml'); + } + + function show_rss_timeline($notice, $title, $link, $subtitle, $suplink=null) + { + + $this->init_document('rss'); + + common_element_start('channel'); + common_element('title', null, $title); + common_element('link', null, $link); + if (!is_null($suplink)) { + # For FriendFeed's SUP protocol + common_element('link', array('xmlns' => 'http://www.w3.org/2005/Atom', + 'rel' => 'http://api.friendfeed.com/2008/03#sup', + 'href' => $suplink, + 'type' => 'application/json')); + } + common_element('description', null, $subtitle); + common_element('language', null, 'en-us'); + common_element('ttl', null, '40'); + + if (is_array($notice)) { + foreach ($notice as $n) { + $entry = $this->twitter_rss_entry_array($n); + $this->show_twitter_rss_item($entry); + } + } else { + while ($notice->fetch()) { + $entry = $this->twitter_rss_entry_array($notice); + $this->show_twitter_rss_item($entry); + } + } + + common_element_end('channel'); + $this->end_twitter_rss(); + } + + function show_atom_timeline($notice, $title, $id, $link, $subtitle=null, $suplink=null) + { + + $this->init_document('atom'); + + common_element('title', null, $title); + common_element('id', null, $id); + common_element('link', array('href' => $link, 'rel' => 'alternate', 'type' => 'text/html'), null); + if (!is_null($suplink)) { + # For FriendFeed's SUP protocol + common_element('link', array('rel' => 'http://api.friendfeed.com/2008/03#sup', + 'href' => $suplink, + 'type' => 'application/json')); + } + common_element('subtitle', null, $subtitle); + + if (is_array($notice)) { + foreach ($notice as $n) { + $entry = $this->twitter_rss_entry_array($n); + $this->show_twitter_atom_entry($entry); + } + } else { + while ($notice->fetch()) { + $entry = $this->twitter_rss_entry_array($notice); + $this->show_twitter_atom_entry($entry); + } + } + + $this->end_document('atom'); + + } + + function show_json_timeline($notice) + { + + $this->init_document('json'); + + $statuses = array(); + + if (is_array($notice)) { + foreach ($notice as $n) { + $twitter_status = $this->twitter_status_array($n); + array_push($statuses, $twitter_status); + } + } else { + while ($notice->fetch()) { + $twitter_status = $this->twitter_status_array($notice); + array_push($statuses, $twitter_status); + } + } + + $this->show_json_objects($statuses); + + $this->end_document('json'); + } + + // Anyone know what date format this is? + // Twitter's dates look like this: "Mon Jul 14 23:52:38 +0000 2008" -- Zach + function date_twitter($dt) + { + $t = strtotime($dt); + return date("D M d G:i:s O Y", $t); + } + + function replier_by_reply($reply_id) + { + $notice = Notice::staticGet($reply_id); + if ($notice) { + $profile = $notice->getProfile(); + if ($profile) { + return intval($profile->id); + } else { + common_debug('Can\'t find a profile for notice: ' . $notice->id, __FILE__); + } + } else { + common_debug("Can't get notice: $reply_id", __FILE__); + } + return null; + } + + // XXX: Candidate for a general utility method somewhere? + function count_subscriptions($profile) + { + + $count = 0; + $sub = new Subscription(); + $sub->subscribed = $profile->id; + + $count = $sub->find(); + + if ($count > 0) { + return $count - 1; + } else { + return 0; + } + } + + function init_document($type='xml') + { + switch ($type) { + case 'xml': + header('Content-Type: application/xml; charset=utf-8'); + common_start_xml(); + break; + case 'json': + header('Content-Type: application/json; charset=utf-8'); + + // Check for JSONP callback + $callback = $this->arg('callback'); + if ($callback) { + print $callback . '('; + } + break; + case 'rss': + header("Content-Type: application/rss+xml; charset=utf-8"); + $this->init_twitter_rss(); + break; + case 'atom': + header('Content-Type: application/atom+xml; charset=utf-8'); + $this->init_twitter_atom(); + break; + default: + $this->client_error(_('Not a supported data format.')); + break; + } + + return; + } + + function end_document($type='xml') + { + switch ($type) { + case 'xml': + common_end_xml(); + break; + case 'json': + + // Check for JSONP callback + $callback = $this->arg('callback'); + if ($callback) { + print ')'; + } + break; + case 'rss': + $this->end_twitter_rss(); + break; + case 'atom': + $this->end_twitter_rss(); + break; + default: + $this->client_error(_('Not a supported data format.')); + break; + } + return; + } + + function client_error($msg, $code = 400, $content_type = 'json') + { + + static $status = array(400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed'); + + $action = $this->trimmed('action'); + + common_debug("User error '$code' on '$action': $msg", __FILE__); + + if (!array_key_exists($code, $status)) { + $code = 400; + } + + $status_string = $status[$code]; + header('HTTP/1.1 '.$code.' '.$status_string); + + if ($content_type == 'xml') { + $this->init_document('xml'); + common_element_start('hash'); + common_element('error', null, $msg); + common_element('request', null, $_SERVER['REQUEST_URI']); + common_element_end('hash'); + $this->end_document('xml'); + } else { + $this->init_document('json'); + $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']); + print(json_encode($error_array)); + $this->end_document('json'); + } + + } + + function init_twitter_rss() + { + common_start_xml(); + common_element_start('rss', array('version' => '2.0')); + } + + function end_twitter_rss() + { + common_element_end('rss'); + common_end_xml(); + } + + function init_twitter_atom() + { + common_start_xml(); + common_element_start('feed', array('xmlns' => 'http://www.w3.org/2005/Atom', 'xml:lang' => 'en-US')); + } + + function end_twitter_atom() + { + common_end_xml(); + common_element_end('feed'); + } + + function show_profile($profile, $content_type='xml', $notice=null) + { + $profile_array = $this->twitter_user_array($profile, true); + switch ($content_type) { + case 'xml': + $this->show_twitter_xml_user($profile_array); + break; + case 'json': + $this->show_json_objects($profile_array); + break; + default: + $this->client_error(_('Not a supported data format.')); + return; + } + return; + } + + function get_user($id, $apidata=null) + { + if (!$id) { + return $apidata['user']; + } else if (is_numeric($id)) { + return User::staticGet($id); + } else { + $nickname = common_canonical_nickname($id); + return User::staticGet('nickname', $nickname); + } + } + + function get_profile($id) + { + if (is_numeric($id)) { + return Profile::staticGet($id); + } else { + $user = User::staticGet('nickname', $id); + if ($user) { + return $user->getProfile(); + } else { + return null; + } + } + } + + function source_link($source) + { + $source_name = _($source); + switch ($source) { + case 'web': + case 'xmpp': + case 'mail': + case 'omb': + case 'api': + break; + default: + $ns = Notice_source::staticGet($source); + if ($ns) { + $source_name = '<a href="' . $ns->url . '">' . $ns->name . '</a>'; + } + break; + } + return $source_name; + } + + function show_extended_profile($user, $apidata) + { + + $this->auth_user = $apidata['user']; + + $profile = $user->getProfile(); + + if (!$profile) { + common_server_error(_('User has no profile.')); + return; + } + + $twitter_user = $this->twitter_user_array($profile, true); + + // Add in extended user fields offered up by this method + $twitter_user['created_at'] = $this->date_twitter($profile->created); + + $subbed = DB_DataObject::factory('subscription'); + $subbed->subscriber = $profile->id; + $subbed_count = (int) $subbed->count() - 1; + + $notices = DB_DataObject::factory('notice'); + $notices->profile_id = $profile->id; + $notice_count = (int) $notices->count(); + + $twitter_user['friends_count'] = (is_int($subbed_count)) ? $subbed_count : 0; + $twitter_user['statuses_count'] = (is_int($notice_count)) ? $notice_count : 0; + + // Other fields Twitter sends... + $twitter_user['profile_background_color'] = ''; + $twitter_user['profile_text_color'] = ''; + $twitter_user['profile_link_color'] = ''; + $twitter_user['profile_sidebar_fill_color'] = ''; + + $faves = DB_DataObject::factory('fave'); + $faves->user_id = $user->id; + $faves_count = (int) $faves->count(); + $twitter_user['favourites_count'] = $faves_count; + + $timezone = 'UTC'; + + if ($user->timezone) { + $timezone = $user->timezone; + } + + $t = new DateTime; + $t->setTimezone(new DateTimeZone($timezone)); + $twitter_user['utc_offset'] = $t->format('Z'); + $twitter_user['time_zone'] = $timezone; + + $following = 'false'; + + if (isset($this->auth_user)) { + if ($this->auth_user->isSubscribed($profile)) { + $following = 'true'; + } + + // Not implemented yet + $twitter_user['notifications'] = 'false'; + } + + $twitter_user['following'] = $following; + + if ($apidata['content-type'] == 'xml') { + $this->init_document('xml'); + $this->show_twitter_xml_user($twitter_user); + $this->end_document('xml'); + } elseif ($apidata['content-type'] == 'json') { + $this->init_document('json'); + $this->show_json_objects($twitter_user); + $this->end_document('json'); + } + + } }
\ No newline at end of file diff --git a/lib/util.php b/lib/util.php index 7518fbf55..fbe04c6c4 100644 --- a/lib/util.php +++ b/lib/util.php @@ -19,645 +19,679 @@ /* XXX: break up into separate modules (HTTP, HTML, user, files) */ -# Show a server error - -function common_server_error($msg, $code=500) { - static $status = array(500 => 'Internal Server Error', - 501 => 'Not Implemented', - 502 => 'Bad Gateway', - 503 => 'Service Unavailable', - 504 => 'Gateway Timeout', - 505 => 'HTTP Version Not Supported'); - - if (!array_key_exists($code, $status)) { - $code = 500; - } - - $status_string = $status[$code]; - - header('HTTP/1.1 '.$code.' '.$status_string); - header('Content-type: text/plain'); - - print $msg; - print "\n"; - exit(); -} - -# Show a user error -function common_user_error($msg, $code=400) { - static $status = array(400 => 'Bad Request', - 401 => 'Unauthorized', - 402 => 'Payment Required', - 403 => 'Forbidden', - 404 => 'Not Found', - 405 => 'Method Not Allowed', - 406 => 'Not Acceptable', - 407 => 'Proxy Authentication Required', - 408 => 'Request Timeout', - 409 => 'Conflict', - 410 => 'Gone', - 411 => 'Length Required', - 412 => 'Precondition Failed', - 413 => 'Request Entity Too Large', - 414 => 'Request-URI Too Long', - 415 => 'Unsupported Media Type', - 416 => 'Requested Range Not Satisfiable', - 417 => 'Expectation Failed'); - - if (!array_key_exists($code, $status)) { - $code = 400; - } - - $status_string = $status[$code]; - - header('HTTP/1.1 '.$code.' '.$status_string); - - common_show_header('Error'); - common_element('div', array('class' => 'error'), $msg); - common_show_footer(); +// Show a server error + +function common_server_error($msg, $code=500) +{ + static $status = array(500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Timeout', + 505 => 'HTTP Version Not Supported'); + + if (!array_key_exists($code, $status)) { + $code = 500; + } + + $status_string = $status[$code]; + + header('HTTP/1.1 '.$code.' '.$status_string); + header('Content-type: text/plain'); + + print $msg; + print "\n"; + exit(); +} + +// Show a user error +function common_user_error($msg, $code=400) +{ + static $status = array(400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Timeout', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Long', + 415 => 'Unsupported Media Type', + 416 => 'Requested Range Not Satisfiable', + 417 => 'Expectation Failed'); + + if (!array_key_exists($code, $status)) { + $code = 400; + } + + $status_string = $status[$code]; + + header('HTTP/1.1 '.$code.' '.$status_string); + + common_show_header('Error'); + common_element('div', array('class' => 'error'), $msg); + common_show_footer(); } $xw = null; -# Start an HTML element -function common_element_start($tag, $attrs=NULL) { - global $xw; - $xw->startElement($tag); - if (is_array($attrs)) { - foreach ($attrs as $name => $value) { - $xw->writeAttribute($name, $value); - } - } else if (is_string($attrs)) { - $xw->writeAttribute('class', $attrs); - } -} - -function common_element_end($tag) { - static $empty_tag = array('base', 'meta', 'link', 'hr', - 'br', 'param', 'img', 'area', - 'input', 'col'); - global $xw; - # XXX: check namespace - if (in_array($tag, $empty_tag)) { - $xw->endElement(); - } else { - $xw->fullEndElement(); - } -} - -function common_element($tag, $attrs=NULL, $content=NULL) { - common_element_start($tag, $attrs); - global $xw; - if (!is_null($content)) { - $xw->text($content); - } - common_element_end($tag); -} - -function common_start_xml($doc=NULL, $public=NULL, $system=NULL, $indent=true) { - global $xw; - $xw = new XMLWriter(); - $xw->openURI('php://output'); - $xw->setIndent($indent); - $xw->startDocument('1.0', 'UTF-8'); - if ($doc) { - $xw->writeDTD($doc, $public, $system); - } -} - -function common_end_xml() { - global $xw; - $xw->endDocument(); - $xw->flush(); -} - -function common_init_locale($language=null) { +// Start an HTML element +function common_element_start($tag, $attrs=null) +{ + global $xw; + $xw->startElement($tag); + if (is_array($attrs)) { + foreach ($attrs as $name => $value) { + $xw->writeAttribute($name, $value); + } + } else if (is_string($attrs)) { + $xw->writeAttribute('class', $attrs); + } +} + +function common_element_end($tag) +{ + static $empty_tag = array('base', 'meta', 'link', 'hr', + 'br', 'param', 'img', 'area', + 'input', 'col'); + global $xw; + // XXX: check namespace + if (in_array($tag, $empty_tag)) { + $xw->endElement(); + } else { + $xw->fullEndElement(); + } +} + +function common_element($tag, $attrs=null, $content=null) +{ + common_element_start($tag, $attrs); + global $xw; + if (!is_null($content)) { + $xw->text($content); + } + common_element_end($tag); +} + +function common_start_xml($doc=null, $public=null, $system=null, $indent=true) +{ + global $xw; + $xw = new XMLWriter(); + $xw->openURI('php://output'); + $xw->setIndent($indent); + $xw->startDocument('1.0', 'UTF-8'); + if ($doc) { + $xw->writeDTD($doc, $public, $system); + } +} + +function common_end_xml() +{ + global $xw; + $xw->endDocument(); + $xw->flush(); +} + +function common_init_locale($language=null) +{ if(!$language) { $language = common_language(); } putenv('LANGUAGE='.$language); putenv('LANG='.$language); return setlocale(LC_ALL, $language . ".utf8", - $language . ".UTF8", - $language . ".utf-8", - $language . ".UTF-8", - $language); -} - -function common_init_language() { - mb_internal_encoding('UTF-8'); - $language = common_language(); - # So we don't have to make people install the gettext locales - $locale_set = common_init_locale($language); - bindtextdomain("laconica", common_config('site','locale_path')); - bind_textdomain_codeset("laconica", "UTF-8"); - textdomain("laconica"); - setlocale(LC_CTYPE, 'C'); - if(!$locale_set) { - common_log(LOG_INFO,'Language requested:'.$language.' - locale could not be set:',__FILE__); - } + $language . ".UTF8", + $language . ".utf-8", + $language . ".UTF-8", + $language); +} + +function common_init_language() +{ + mb_internal_encoding('UTF-8'); + $language = common_language(); + // So we don't have to make people install the gettext locales + $locale_set = common_init_locale($language); + bindtextdomain("laconica", common_config('site','locale_path')); + bind_textdomain_codeset("laconica", "UTF-8"); + textdomain("laconica"); + setlocale(LC_CTYPE, 'C'); + if(!$locale_set) { + common_log(LOG_INFO,'Language requested:'.$language.' - locale could not be set:',__FILE__); + } } define('PAGE_TYPE_PREFS', 'text/html,application/xhtml+xml,application/xml;q=0.3,text/xml;q=0.2'); -function common_show_header($pagetitle, $callable=NULL, $data=NULL, $headercall=NULL) { +function common_show_header($pagetitle, $callable=null, $data=null, $headercall=null) +{ - global $config, $xw; + global $config, $xw; global $action; /* XXX: kind of cheating here. */ - common_start_html(); - - common_element_start('head'); - common_element('title', NULL, - $pagetitle . " - " . $config['site']['name']); - common_element('link', array('rel' => 'stylesheet', - 'type' => 'text/css', - 'href' => theme_path('display.css') . '?version=' . LACONICA_VERSION, - 'media' => 'screen, projection, tv')); - foreach (array(6,7) as $ver) { - if (file_exists(theme_file('ie'.$ver.'.css'))) { - # Yes, IE people should be put in jail. - $xw->writeComment('[if lte IE '.$ver.']><link rel="stylesheet" type="text/css" '. - 'href="'.theme_path('ie'.$ver.'.css').'?version='.LACONICA_VERSION.'" /><![endif]'); - } - } - - common_element('script', array('type' => 'text/javascript', - 'src' => common_path('js/jquery.min.js')), - ' '); - common_element('script', array('type' => 'text/javascript', - 'src' => common_path('js/jquery.form.js')), - ' '); - common_element('script', array('type' => 'text/javascript', - 'src' => common_path('js/xbImportNode.js')), - ' '); - common_element('script', array('type' => 'text/javascript', - 'src' => common_path('js/util.js?version='.LACONICA_VERSION)), - ' '); - common_element('link', array('rel' => 'search', 'type' => 'application/opensearchdescription+xml', - 'href' => common_local_url('opensearch', array('type' => 'people')), - 'title' => common_config('site', 'name').' People Search')); - - common_element('link', array('rel' => 'search', 'type' => 'application/opensearchdescription+xml', - 'href' => common_local_url('opensearch', array('type' => 'notice')), - 'title' => common_config('site', 'name').' Notice Search')); - - if ($callable) { - if ($data) { - call_user_func($callable, $data); - } else { - call_user_func($callable); - } - } - common_element_end('head'); - common_element_start('body', $action); - common_element_start('div', array('id' => 'wrap')); - common_element_start('div', array('id' => 'header')); - common_nav_menu(); - if ((isset($config['site']['logo']) && is_string($config['site']['logo']) && (strlen($config['site']['logo']) > 0)) - || file_exists(theme_file('logo.png'))) - { - common_element_start('a', array('href' => common_local_url('public'))); - common_element('img', array('src' => isset($config['site']['logo']) ? - ($config['site']['logo']) : theme_path('logo.png'), - 'alt' => $config['site']['name'], - 'id' => 'logo')); - common_element_end('a'); - } else { - common_element_start('p', array('id' => 'branding')); - common_element('a', array('href' => common_local_url('public')), - $config['site']['name']); - common_element_end('p'); - } - - common_element('h1', 'pagetitle', $pagetitle); - - if ($headercall) { - if ($data) { - call_user_func($headercall, $data); - } else { - call_user_func($headercall); - } - } - common_element_end('div'); - common_element_start('div', array('id' => 'content')); -} - -function common_start_html($type=NULL, $indent=true) { - - if (!$type) { - $httpaccept = isset($_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : NULL; - - # XXX: allow content negotiation for RDF, RSS, or XRDS - - $type = common_negotiate_type(common_accept_to_prefs($httpaccept), - common_accept_to_prefs(PAGE_TYPE_PREFS)); - - if (!$type) { - common_user_error(_('This page is not available in a media type you accept'), 406); - exit(0); - } - } - - header('Content-Type: '.$type); - - common_start_xml('html', - '-//W3C//DTD XHTML 1.0 Strict//EN', - 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd', $indent); - - # FIXME: correct language for interface - - $language = common_language(); - - common_element_start('html', array('xmlns' => 'http://www.w3.org/1999/xhtml', - 'xml:lang' => $language, - 'lang' => $language)); -} - -function common_show_footer() { - global $xw, $config; - common_element_end('div'); # content div - common_foot_menu(); - common_element_start('div', array('id' => 'footer')); - common_element_start('div', 'laconica'); - 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); + common_start_html(); + + common_element_start('head'); + common_element('title', null, + $pagetitle . " - " . $config['site']['name']); + common_element('link', array('rel' => 'stylesheet', + 'type' => 'text/css', + 'href' => theme_path('display.css') . '?version=' . LACONICA_VERSION, + 'media' => 'screen, projection, tv')); + foreach (array(6,7) as $ver) { + if (file_exists(theme_file('ie'.$ver.'.css'))) { + // Yes, IE people should be put in jail. + $xw->writeComment('[if lte IE '.$ver.']><link rel="stylesheet" type="text/css" '. + 'href="'.theme_path('ie'.$ver.'.css').'?version='.LACONICA_VERSION.'" /><![endif]'); + } + } + + common_element('script', array('type' => 'text/javascript', + 'src' => common_path('js/jquery.min.js')), + ' '); + common_element('script', array('type' => 'text/javascript', + 'src' => common_path('js/jquery.form.js')), + ' '); + common_element('script', array('type' => 'text/javascript', + 'src' => common_path('js/xbImportNode.js')), + ' '); + common_element('script', array('type' => 'text/javascript', + 'src' => common_path('js/util.js?version='.LACONICA_VERSION)), + ' '); + common_element('link', array('rel' => 'search', 'type' => 'application/opensearchdescription+xml', + 'href' => common_local_url('opensearch', array('type' => 'people')), + 'title' => common_config('site', 'name').' People Search')); + + common_element('link', array('rel' => 'search', 'type' => 'application/opensearchdescription+xml', + 'href' => common_local_url('opensearch', array('type' => 'notice')), + 'title' => common_config('site', 'name').' Notice Search')); + + if ($callable) { + if ($data) { + call_user_func($callable, $data); + } else { + call_user_func($callable); + } + } + common_element_end('head'); + common_element_start('body', $action); + common_element_start('div', array('id' => 'wrap')); + common_element_start('div', array('id' => 'header')); + common_nav_menu(); + if ((isset($config['site']['logo']) && is_string($config['site']['logo']) && (strlen($config['site']['logo']) > 0)) + || file_exists(theme_file('logo.png'))) + { + common_element_start('a', array('href' => common_local_url('public'))); + common_element('img', array('src' => isset($config['site']['logo']) ? + ($config['site']['logo']) : theme_path('logo.png'), + 'alt' => $config['site']['name'], + 'id' => 'logo')); + common_element_end('a'); + } else { + common_element_start('p', array('id' => 'branding')); + common_element('a', array('href' => common_local_url('public')), + $config['site']['name']); + common_element_end('p'); + } + + common_element('h1', 'pagetitle', $pagetitle); + + if ($headercall) { + if ($data) { + call_user_func($headercall, $data); + } else { + call_user_func($headercall); + } + } + common_element_end('div'); + common_element_start('div', array('id' => 'content')); +} + +function common_start_html($type=null, $indent=true) +{ + + if (!$type) { + $httpaccept = isset($_SERVER['HTTP_ACCEPT']) ? $_SERVER['HTTP_ACCEPT'] : null; + + // XXX: allow content negotiation for RDF, RSS, or XRDS + + $type = common_negotiate_type(common_accept_to_prefs($httpaccept), + common_accept_to_prefs(PAGE_TYPE_PREFS)); + + if (!$type) { + common_user_error(_('This page is not available in a media type you accept'), 406); + exit(0); + } + } + + header('Content-Type: '.$type); + + common_start_xml('html', + '-//W3C//DTD XHTML 1.0 Strict//EN', + 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd', $indent); + + // FIXME: correct language for interface + + $language = common_language(); + + common_element_start('html', array('xmlns' => 'http://www.w3.org/1999/xhtml', + 'xml:lang' => $language, + 'lang' => $language)); +} + +function common_show_footer() +{ + global $xw, $config; + common_element_end('div'); // content div + common_foot_menu(); + common_element_start('div', array('id' => 'footer')); + common_element_start('div', 'laconica'); + 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); common_raw($output); - common_element_end('div'); - common_element('img', array('id' => 'cc', - 'src' => $config['license']['image'], - 'alt' => $config['license']['title'])); - common_element_start('p'); - common_text(_('Unless otherwise specified, contents of this site are copyright by the contributors and available under the ')); - common_element('a', array('class' => 'license', - 'rel' => 'license', - 'href' => $config['license']['url']), - $config['license']['title']); - common_text(_('. Contributors should be attributed by full name or nickname.')); - common_element_end('p'); - common_element_end('div'); - common_element_end('div'); - common_element_end('body'); - common_element_end('html'); - common_end_xml(); -} - -function common_text($txt) { - global $xw; - $xw->text($txt); -} - -function common_raw($xml) { - global $xw; - $xw->writeRaw($xml); -} - -function common_nav_menu() { - $user = common_current_user(); - common_element_start('ul', array('id' => 'nav')); - if ($user) { - common_menu_item(common_local_url('all', array('nickname' => $user->nickname)), - _('Home')); - } - common_menu_item(common_local_url('peoplesearch'), _('Search')); - if ($user) { - common_menu_item(common_local_url('profilesettings'), - _('Settings')); - common_menu_item(common_local_url('invite'), - _('Invite')); - common_menu_item(common_local_url('logout'), - _('Logout')); - } else { - common_menu_item(common_local_url('login'), _('Login')); - if (!common_config('site', 'closed')) { - common_menu_item(common_local_url('register'), _('Register')); - } - common_menu_item(common_local_url('openidlogin'), _('OpenID')); - } - common_menu_item(common_local_url('doc', array('title' => 'help')), - _('Help')); - common_element_end('ul'); -} - -function common_foot_menu() { - common_element_start('ul', array('id' => 'nav_sub')); - common_menu_item(common_local_url('doc', array('title' => 'help')), - _('Help')); - common_menu_item(common_local_url('doc', array('title' => 'about')), - _('About')); - common_menu_item(common_local_url('doc', array('title' => 'faq')), - _('FAQ')); - common_menu_item(common_local_url('doc', array('title' => 'privacy')), - _('Privacy')); - common_menu_item(common_local_url('doc', array('title' => 'source')), - _('Source')); - common_menu_item(common_local_url('doc', array('title' => 'contact')), - _('Contact')); - common_element_end('ul'); -} - -function common_menu_item($url, $text, $title=NULL, $is_selected=false) { - $lattrs = array(); - if ($is_selected) { - $lattrs['class'] = 'current'; - } - common_element_start('li', $lattrs); - $attrs['href'] = $url; - if ($title) { - $attrs['title'] = $title; - } - common_element('a', $attrs, $text); - common_element_end('li'); -} - -function common_input($id, $label, $value=NULL,$instructions=NULL) { - common_element_start('p'); - common_element('label', array('for' => $id), $label); - $attrs = array('name' => $id, - 'type' => 'text', - 'class' => 'input_text', - 'id' => $id); - if ($value) { - $attrs['value'] = htmlspecialchars($value); - } - common_element('input', $attrs); - if ($instructions) { - common_element('span', 'input_instructions', $instructions); - } - common_element_end('p'); -} - -function common_checkbox($id, $label, $checked=false, $instructions=NULL, $value='true', $disabled=false) -{ - common_element_start('p'); - $attrs = array('name' => $id, - 'type' => 'checkbox', - 'class' => 'checkbox', - 'id' => $id); - if ($value) { - $attrs['value'] = htmlspecialchars($value); - } - if ($checked) { - $attrs['checked'] = 'checked'; - } - if ($disabled) { - $attrs['disabled'] = 'true'; - } - common_element('input', $attrs); - common_text(' '); - common_element('label', array('class' => 'checkbox_label', 'for' => $id), $label); - common_text(' '); - if ($instructions) { - common_element('span', 'input_instructions', $instructions); - } - common_element_end('p'); -} - -function common_dropdown($id, $label, $content, $instructions=NULL, $blank_select=FALSE, $selected=NULL) { - common_element_start('p'); - common_element('label', array('for' => $id), $label); - common_element_start('select', array('id' => $id, 'name' => $id)); - if ($blank_select) { - common_element('option', array('value' => '')); - } - foreach ($content as $value => $option) { - if ($value == $selected) { - common_element('option', array('value' => $value, 'selected' => $value), $option); - } else { - common_element('option', array('value' => $value), $option); - } - } - common_element_end('select'); - if ($instructions) { - common_element('span', 'input_instructions', $instructions); - } - common_element_end('p'); -} -function common_hidden($id, $value) { - common_element('input', array('name' => $id, - 'type' => 'hidden', - 'id' => $id, - 'value' => $value)); -} - -function common_password($id, $label, $instructions=NULL) { - common_element_start('p'); - common_element('label', array('for' => $id), $label); - $attrs = array('name' => $id, - 'type' => 'password', - 'class' => 'password', - 'id' => $id); - common_element('input', $attrs); - if ($instructions) { - common_element('span', 'input_instructions', $instructions); - } - common_element_end('p'); -} - -function common_submit($id, $label, $cls='submit') { - global $xw; - common_element_start('p'); - common_element('input', array('type' => 'submit', - 'id' => $id, - 'name' => $id, - 'class' => $cls, - 'value' => $label)); - common_element_end('p'); -} - -function common_textarea($id, $label, $content=NULL, $instructions=NULL) { - common_element_start('p'); - common_element('label', array('for' => $id), $label); - common_element('textarea', array('rows' => 3, - 'cols' => 40, - 'name' => $id, - 'id' => $id), - ($content) ? $content : ''); - if ($instructions) { - common_element('span', 'input_instructions', $instructions); - } - common_element_end('p'); -} - -function common_timezone() { - if (common_logged_in()) { - $user = common_current_user(); - if ($user->timezone) { - return $user->timezone; - } - } - - global $config; - return $config['site']['timezone']; -} - -function common_language() { - - // If there is a user logged in and they've set a language preference - // then return that one... - if (common_logged_in()) { - $user = common_current_user(); - $user_language = $user->language; - if ($user_language) - return $user_language; + common_element_end('div'); + common_element('img', array('id' => 'cc', + 'src' => $config['license']['image'], + 'alt' => $config['license']['title'])); + common_element_start('p'); + common_text(_('Unless otherwise specified, contents of this site are copyright by the contributors and available under the ')); + common_element('a', array('class' => 'license', + 'rel' => 'license', + 'href' => $config['license']['url']), + $config['license']['title']); + common_text(_('. Contributors should be attributed by full name or nickname.')); + common_element_end('p'); + common_element_end('div'); + common_element_end('div'); + common_element_end('body'); + common_element_end('html'); + common_end_xml(); +} + +function common_text($txt) +{ + global $xw; + $xw->text($txt); +} + +function common_raw($xml) +{ + global $xw; + $xw->writeRaw($xml); +} + +function common_nav_menu() +{ + $user = common_current_user(); + common_element_start('ul', array('id' => 'nav')); + if ($user) { + common_menu_item(common_local_url('all', array('nickname' => $user->nickname)), + _('Home')); + } + common_menu_item(common_local_url('peoplesearch'), _('Search')); + if ($user) { + common_menu_item(common_local_url('profilesettings'), + _('Settings')); + common_menu_item(common_local_url('invite'), + _('Invite')); + common_menu_item(common_local_url('logout'), + _('Logout')); + } else { + common_menu_item(common_local_url('login'), _('Login')); + if (!common_config('site', 'closed')) { + common_menu_item(common_local_url('register'), _('Register')); } + common_menu_item(common_local_url('openidlogin'), _('OpenID')); + } + common_menu_item(common_local_url('doc', array('title' => 'help')), + _('Help')); + common_element_end('ul'); +} - // Otherwise, find the best match for the languages requested by the - // user's browser... - $httplang = isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? $_SERVER['HTTP_ACCEPT_LANGUAGE'] : NULL; - if (!empty($httplang)) { - $language = client_prefered_language($httplang); - if ($language) - return $language; - } +function common_foot_menu() +{ + common_element_start('ul', array('id' => 'nav_sub')); + common_menu_item(common_local_url('doc', array('title' => 'help')), + _('Help')); + common_menu_item(common_local_url('doc', array('title' => 'about')), + _('About')); + common_menu_item(common_local_url('doc', array('title' => 'faq')), + _('FAQ')); + common_menu_item(common_local_url('doc', array('title' => 'privacy')), + _('Privacy')); + common_menu_item(common_local_url('doc', array('title' => 'source')), + _('Source')); + common_menu_item(common_local_url('doc', array('title' => 'contact')), + _('Contact')); + common_element_end('ul'); +} + +function common_menu_item($url, $text, $title=null, $is_selected=false) +{ + $lattrs = array(); + if ($is_selected) { + $lattrs['class'] = 'current'; + } + common_element_start('li', $lattrs); + $attrs['href'] = $url; + if ($title) { + $attrs['title'] = $title; + } + common_element('a', $attrs, $text); + common_element_end('li'); +} - // Finally, if none of the above worked, use the site's default... - return common_config('site', 'language'); +function common_input($id, $label, $value=null,$instructions=null) +{ + common_element_start('p'); + common_element('label', array('for' => $id), $label); + $attrs = array('name' => $id, + 'type' => 'text', + 'class' => 'input_text', + 'id' => $id); + if ($value) { + $attrs['value'] = htmlspecialchars($value); + } + common_element('input', $attrs); + if ($instructions) { + common_element('span', 'input_instructions', $instructions); + } + common_element_end('p'); } -# salted, hashed passwords are stored in the DB -function common_munge_password($password, $id) { - return md5($password . $id); +function common_checkbox($id, $label, $checked=false, $instructions=null, $value='true', $disabled=false) +{ + common_element_start('p'); + $attrs = array('name' => $id, + 'type' => 'checkbox', + 'class' => 'checkbox', + 'id' => $id); + if ($value) { + $attrs['value'] = htmlspecialchars($value); + } + if ($checked) { + $attrs['checked'] = 'checked'; + } + if ($disabled) { + $attrs['disabled'] = 'true'; + } + common_element('input', $attrs); + common_text(' '); + common_element('label', array('class' => 'checkbox_label', 'for' => $id), $label); + common_text(' '); + if ($instructions) { + common_element('span', 'input_instructions', $instructions); + } + common_element_end('p'); } -# check if a username exists and has matching password -function common_check_user($nickname, $password) { - # NEVER allow blank passwords, even if they match the DB - if (mb_strlen($password) == 0) { - return false; - } - $user = User::staticGet('nickname', $nickname); - if (is_null($user)) { - return false; - } else { - if (0 == strcmp(common_munge_password($password, $user->id), - $user->password)) { - return $user; - } else { - return false; - } - } +function common_dropdown($id, $label, $content, $instructions=null, $blank_select=false, $selected=null) +{ + common_element_start('p'); + common_element('label', array('for' => $id), $label); + common_element_start('select', array('id' => $id, 'name' => $id)); + if ($blank_select) { + common_element('option', array('value' => '')); + } + foreach ($content as $value => $option) { + if ($value == $selected) { + common_element('option', array('value' => $value, 'selected' => $value), $option); + } else { + common_element('option', array('value' => $value), $option); + } + } + common_element_end('select'); + if ($instructions) { + common_element('span', 'input_instructions', $instructions); + } + common_element_end('p'); +} +function common_hidden($id, $value) +{ + common_element('input', array('name' => $id, + 'type' => 'hidden', + 'id' => $id, + 'value' => $value)); +} + +function common_password($id, $label, $instructions=null) +{ + common_element_start('p'); + common_element('label', array('for' => $id), $label); + $attrs = array('name' => $id, + 'type' => 'password', + 'class' => 'password', + 'id' => $id); + common_element('input', $attrs); + if ($instructions) { + common_element('span', 'input_instructions', $instructions); + } + common_element_end('p'); +} + +function common_submit($id, $label, $cls='submit') +{ + global $xw; + common_element_start('p'); + common_element('input', array('type' => 'submit', + 'id' => $id, + 'name' => $id, + 'class' => $cls, + 'value' => $label)); + common_element_end('p'); +} + +function common_textarea($id, $label, $content=null, $instructions=null) +{ + common_element_start('p'); + common_element('label', array('for' => $id), $label); + common_element('textarea', array('rows' => 3, + 'cols' => 40, + 'name' => $id, + 'id' => $id), + ($content) ? $content : ''); + if ($instructions) { + common_element('span', 'input_instructions', $instructions); + } + common_element_end('p'); +} + +function common_timezone() +{ + if (common_logged_in()) { + $user = common_current_user(); + if ($user->timezone) { + return $user->timezone; + } + } + + global $config; + return $config['site']['timezone']; +} + +function common_language() +{ + + // If there is a user logged in and they've set a language preference + // then return that one... + if (common_logged_in()) { + $user = common_current_user(); + $user_language = $user->language; + if ($user_language) + return $user_language; + } + + // Otherwise, find the best match for the languages requested by the + // user's browser... + $httplang = isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? $_SERVER['HTTP_ACCEPT_LANGUAGE'] : null; + if (!empty($httplang)) { + $language = client_prefered_language($httplang); + if ($language) + return $language; + } + + // Finally, if none of the above worked, use the site's default... + return common_config('site', 'language'); +} +// salted, hashed passwords are stored in the DB + +function common_munge_password($password, $id) +{ + return md5($password . $id); +} + +// check if a username exists and has matching password +function common_check_user($nickname, $password) +{ + // NEVER allow blank passwords, even if they match the DB + if (mb_strlen($password) == 0) { + return false; + } + $user = User::staticGet('nickname', $nickname); + if (is_null($user)) { + return false; + } else { + if (0 == strcmp(common_munge_password($password, $user->id), + $user->password)) { + return $user; + } else { + return false; + } + } } -# is the current user logged in? -function common_logged_in() { - return (!is_null(common_current_user())); +// is the current user logged in? +function common_logged_in() +{ + return (!is_null(common_current_user())); } -function common_have_session() { - return (0 != strcmp(session_id(), '')); +function common_have_session() +{ + return (0 != strcmp(session_id(), '')); } -function common_ensure_session() { - if (!common_have_session()) { - @session_start(); - } +function common_ensure_session() +{ + if (!common_have_session()) { + @session_start(); + } } -# Three kinds of arguments: -# 1) a user object -# 2) a nickname -# 3) NULL to clear +// Three kinds of arguments: +// 1) a user object +// 2) a nickname +// 3) null to clear -# Initialize to false; set to NULL if none found +// Initialize to false; set to null if none found $_cur = false; -function common_set_user($user) { +function common_set_user($user) +{ global $_cur; - if (is_null($user) && common_have_session()) { - $_cur = NULL; - unset($_SESSION['userid']); - return true; - } else if (is_string($user)) { - $nickname = $user; - $user = User::staticGet('nickname', $nickname); - } else if (!($user instanceof User)) { - return false; - } - - if ($user) { - common_ensure_session(); - $_SESSION['userid'] = $user->id; + if (is_null($user) && common_have_session()) { + $_cur = null; + unset($_SESSION['userid']); + return true; + } else if (is_string($user)) { + $nickname = $user; + $user = User::staticGet('nickname', $nickname); + } else if (!($user instanceof User)) { + return false; + } + + if ($user) { + common_ensure_session(); + $_SESSION['userid'] = $user->id; $_cur = $user; - return $_cur; - } - return false; + return $_cur; + } + return false; } -function common_set_cookie($key, $value, $expiration=0) { - $path = common_config('site', 'path'); - $server = common_config('site', 'server'); +function common_set_cookie($key, $value, $expiration=0) +{ + $path = common_config('site', 'path'); + $server = common_config('site', 'server'); - if ($path && ($path != '/')) { - $cookiepath = '/' . $path . '/'; - } else { - $cookiepath = '/'; - } - return setcookie($key, - $value, - $expiration, - $cookiepath, - $server); + if ($path && ($path != '/')) { + $cookiepath = '/' . $path . '/'; + } else { + $cookiepath = '/'; + } + return setcookie($key, + $value, + $expiration, + $cookiepath, + $server); } define('REMEMBERME', 'rememberme'); -define('REMEMBERME_EXPIRY', 30 * 24 * 60 * 60); # 30 days +define('REMEMBERME_EXPIRY', 30 * 24 * 60 * 60); // 30 days -function common_rememberme($user=NULL) { - if (!$user) { - $user = common_current_user(); - if (!$user) { - common_debug('No current user to remember', __FILE__); - return false; - } - } +function common_rememberme($user=null) +{ + if (!$user) { + $user = common_current_user(); + if (!$user) { + common_debug('No current user to remember', __FILE__); + return false; + } + } - $rm = new Remember_me(); + $rm = new Remember_me(); - $rm->code = common_good_rand(16); - $rm->user_id = $user->id; + $rm->code = common_good_rand(16); + $rm->user_id = $user->id; - # Wrap the insert in some good ol' fashioned transaction code + // Wrap the insert in some good ol' fashioned transaction code $rm->query('BEGIN'); - $result = $rm->insert(); + $result = $rm->insert(); - if (!$result) { - common_log_db_error($rm, 'INSERT', __FILE__); - common_debug('Error adding rememberme record for ' . $user->nickname, __FILE__); - return false; + if (!$result) { + common_log_db_error($rm, 'INSERT', __FILE__); + common_debug('Error adding rememberme record for ' . $user->nickname, __FILE__); + return false; } $rm->query('COMMIT'); - common_debug('Inserted rememberme record (' . $rm->code . ', ' . $rm->user_id . '); result = ' . $result . '.', __FILE__); + common_debug('Inserted rememberme record (' . $rm->code . ', ' . $rm->user_id . '); result = ' . $result . '.', __FILE__); $cookieval = $rm->user_id . ':' . $rm->code; - common_log(LOG_INFO, 'adding rememberme cookie "' . $cookieval . '" for ' . $user->nickname); + common_log(LOG_INFO, 'adding rememberme cookie "' . $cookieval . '" for ' . $user->nickname); - common_set_cookie(REMEMBERME, $cookieval, time() + REMEMBERME_EXPIRY); + common_set_cookie(REMEMBERME, $cookieval, time() + REMEMBERME_EXPIRY); - return true; + return true; } -function common_remembered_user() { +function common_remembered_user() +{ - $user = NULL; + $user = null; - $packed = isset($_COOKIE[REMEMBERME]) ? $_COOKIE[REMEMBERME] : NULL; + $packed = isset($_COOKIE[REMEMBERME]) ? $_COOKIE[REMEMBERME] : null; - if (!$packed) { - return NULL; + if (!$packed) { + return null; } list($id, $code) = explode(':', $packed); @@ -665,7 +699,7 @@ function common_remembered_user() { if (!$id || !$code) { common_log(LOG_WARNING, 'Malformed rememberme cookie: ' . $packed); common_forgetme(); - return NULL; + return null; } $rm = Remember_me::staticGet($code); @@ -673,13 +707,13 @@ function common_remembered_user() { if (!$rm) { common_log(LOG_WARNING, 'No such remember code: ' . $code); common_forgetme(); - return NULL; + return null; } if ($rm->user_id != $id) { common_log(LOG_WARNING, 'Rememberme code for wrong user: ' . $rm->user_id . ' != ' . $id); common_forgetme(); - return NULL; + return null; } $user = User::staticGet($rm->user_id); @@ -687,17 +721,17 @@ function common_remembered_user() { if (!$user) { common_log(LOG_WARNING, 'No such user for rememberme: ' . $rm->user_id); common_forgetme(); - return NULL; + return null; } - # successful! + // successful! $result = $rm->delete(); if (!$result) { common_log_db_error($rm, 'DELETE', __FILE__); common_log(LOG_WARNING, 'Could not delete rememberme: ' . $code); common_forgetme(); - return NULL; + return null; } common_log(LOG_INFO, 'logging in ' . $user->nickname . ' using rememberme code ' . $rm->code); @@ -705,22 +739,24 @@ function common_remembered_user() { common_set_user($user); common_real_login(false); - # We issue a new cookie, so they can log in - # automatically again after this session + // We issue a new cookie, so they can log in + // automatically again after this session common_rememberme($user); - return $user; + return $user; } -# must be called with a valid user! +// must be called with a valid user! -function common_forgetme() { - common_set_cookie(REMEMBERME, '', 0); +function common_forgetme() +{ + common_set_cookie(REMEMBERME, '', 0); } -# who is the current user? -function common_current_user() { +// who is the current user? +function common_current_user() +{ global $_cur; if ($_cur === false) { @@ -734,581 +770,610 @@ function common_current_user() { } } - # that didn't work; try to remember; will init $_cur to NULL on failure + // that didn't work; try to remember; will init $_cur to null on failure $_cur = common_remembered_user(); if ($_cur) { common_debug("Got User " . $_cur->nickname); common_debug("Faking session on remembered user"); - # XXX: Is this necessary? + // XXX: Is this necessary? $_SESSION['userid'] = $_cur->id; } } - return $_cur; + return $_cur; } -# Logins that are 'remembered' aren't 'real' -- they're subject to -# cookie-stealing. So, we don't let them do certain things. New reg, -# OpenID, and password logins _are_ real. +// Logins that are 'remembered' aren't 'real' -- they're subject to +// cookie-stealing. So, we don't let them do certain things. New reg, +// OpenID, and password logins _are_ real. -function common_real_login($real=true) { - common_ensure_session(); - $_SESSION['real_login'] = $real; +function common_real_login($real=true) +{ + common_ensure_session(); + $_SESSION['real_login'] = $real; } -function common_is_real_login() { - return common_logged_in() && $_SESSION['real_login']; +function common_is_real_login() +{ + return common_logged_in() && $_SESSION['real_login']; } -# get canonical version of nickname for comparison -function common_canonical_nickname($nickname) { - # XXX: UTF-8 canonicalization (like combining chars) - return strtolower($nickname); +// get canonical version of nickname for comparison +function common_canonical_nickname($nickname) +{ + // XXX: UTF-8 canonicalization (like combining chars) + return strtolower($nickname); } -# get canonical version of email for comparison -function common_canonical_email($email) { - # XXX: canonicalize UTF-8 - # XXX: lcase the domain part - return $email; +// get canonical version of email for comparison +function common_canonical_email($email) +{ + // XXX: canonicalize UTF-8 + // XXX: lcase the domain part + return $email; } define('URL_REGEX', '^|[ \t\r\n])((ftp|http|https|gopher|mailto|news|nntp|telnet|wais|file|prospero|aim|webcal):(([A-Za-z0-9$_.+!*(),;/?:@&~=-])|%[A-Fa-f0-9]{2}){2,}(#([a-zA-Z0-9][a-zA-Z0-9$_.+!*(),;/?:@&~=%-]*))?([A-Za-z0-9$_+!*();/?:~-]))'); -function common_render_content($text, $notice) { - $r = common_render_text($text); - $id = $notice->profile_id; - $r = preg_replace('/(^|\s+)@([A-Za-z0-9]{1,64})/e', "'\\1@'.common_at_link($id, '\\2')", $r); - $r = preg_replace('/^T ([A-Z0-9]{1,64}) /e', "'T '.common_at_link($id, '\\1').' '", $r); - $r = preg_replace('/(^|\s+)@#([A-Za-z0-9]{1,64})/e', "'\\1@#'.common_at_hash_link($id, '\\2')", $r); - return $r; -} - -function common_render_text($text) { - $r = htmlspecialchars($text); - - $r = preg_replace('/[\x{0}-\x{8}\x{b}-\x{c}\x{e}-\x{19}]/', '', $r); - $r = preg_replace_callback('@https?://[^\]>\s]+@', 'common_render_uri_thingy', $r); - $r = preg_replace('/(^|\s+)#([A-Za-z0-9_\-\.]{1,64})/e', "'\\1#'.common_tag_link('\\2')", $r); - # XXX: machine tags - return $r; -} - -function common_render_uri_thingy($matches) { - $uri = $matches[0]; - $trailer = ''; - - # Some heuristics for extracting URIs from surrounding punctuation - # Strip from trailing text... - if (preg_match('/^(.*)([,.:"\']+)$/', $uri, $matches)) { - $uri = $matches[1]; - $trailer = $matches[2]; - } - - $pairs = array( - ']' => '[', # technically disallowed in URIs, but used in Java docs - ')' => '(', # far too frequent in Wikipedia and MSDN - ); - $final = substr($uri, -1, 1); - if (isset($pairs[$final])) { - $openers = substr_count($uri, $pairs[$final]); - $closers = substr_count($uri, $final); - if ($closers > $openers) { - // Assume the paren was opened outside the URI - $uri = substr($uri, 0, -1); - $trailer = $final . $trailer; - } - } - if ($longurl = common_longurl($uri)) { - $longurl = htmlentities($longurl, ENT_QUOTES, 'UTF-8'); - $title = " title='$longurl'"; - } - else $title = ''; - - return '<a href="' . $uri . '"' . $title . ' class="extlink">' . $uri . '</a>' . $trailer; -} - -function common_longurl($short_url) { +function common_render_content($text, $notice) +{ + $r = common_render_text($text); + $id = $notice->profile_id; + $r = preg_replace('/(^|\s+)@([A-Za-z0-9]{1,64})/e', "'\\1@'.common_at_link($id, '\\2')", $r); + $r = preg_replace('/^T ([A-Z0-9]{1,64}) /e', "'T '.common_at_link($id, '\\1').' '", $r); + $r = preg_replace('/(^|\s+)@#([A-Za-z0-9]{1,64})/e', "'\\1@#'.common_at_hash_link($id, '\\2')", $r); + return $r; +} + +function common_render_text($text) +{ + $r = htmlspecialchars($text); + + $r = preg_replace('/[\x{0}-\x{8}\x{b}-\x{c}\x{e}-\x{19}]/', '', $r); + $r = preg_replace_callback('@https?://[^\]>\s]+@', 'common_render_uri_thingy', $r); + $r = preg_replace('/(^|\s+)#([A-Za-z0-9_\-\.]{1,64})/e', "'\\1#'.common_tag_link('\\2')", $r); + // XXX: machine tags + return $r; +} + +function common_render_uri_thingy($matches) +{ + $uri = $matches[0]; + $trailer = ''; + + // Some heuristics for extracting URIs from surrounding punctuation + // Strip from trailing text... + if (preg_match('/^(.*)([,.:"\']+)$/', $uri, $matches)) { + $uri = $matches[1]; + $trailer = $matches[2]; + } + + $pairs = array( + ']' => '[', // technically disallowed in URIs, but used in Java docs + ')' => '(', // far too frequent in Wikipedia and MSDN + ); + $final = substr($uri, -1, 1); + if (isset($pairs[$final])) { + $openers = substr_count($uri, $pairs[$final]); + $closers = substr_count($uri, $final); + if ($closers > $openers) { + // Assume the paren was opened outside the URI + $uri = substr($uri, 0, -1); + $trailer = $final . $trailer; + } + } + if ($longurl = common_longurl($uri)) { + $longurl = htmlentities($longurl, ENT_QUOTES, 'UTF-8'); + $title = " title='$longurl'"; + } + else $title = ''; + + return '<a href="' . $uri . '"' . $title . ' class="extlink">' . $uri . '</a>' . $trailer; +} + +function common_longurl($short_url) +{ $long_url = common_shorten_link($short_url, true); if ($long_url === $short_url) return false; return $long_url; } -function common_longurl2($uri) { - $uri_e = urlencode($uri); - $longurl = unserialize(file_get_contents("http://api.longurl.org/v1/expand?format=php&url=$uri_e")); - if (empty($longurl['long_url']) || $uri === $longurl['long_url']) return false; - return stripslashes($longurl['long_url']); +function common_longurl2($uri) +{ + $uri_e = urlencode($uri); + $longurl = unserialize(file_get_contents("http://api.longurl.org/v1/expand?format=php&url=$uri_e")); + if (empty($longurl['long_url']) || $uri === $longurl['long_url']) return false; + return stripslashes($longurl['long_url']); } -function common_shorten_links($text) { +function common_shorten_links($text) +{ if (mb_strlen($text) <= 140) return $text; static $cache = array(); if (isset($cache[$text])) return $cache[$text]; // \s = not a horizontal whitespace character (since PHP 5.2.4) - return $cache[$text] = preg_replace('@https?://[^)\]>\s]+@e', "common_shorten_link('\\0')", $text); + return $cache[$text] = preg_replace('@https?://[^)\]>\s]+@e', "common_shorten_link('\\0')", $text); } -function common_shorten_link($url, $reverse = false) { - static $url_cache = array(); +function common_shorten_link($url, $reverse = false) +{ + static $url_cache = array(); if ($reverse) return isset($url_cache[$url]) ? $url_cache[$url] : $url; - $user = common_current_user(); - - $curlh = curl_init(); - curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 20); // # seconds to wait - curl_setopt($curlh, CURLOPT_USERAGENT, 'Laconica'); - curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true); - - switch($user->urlshorteningservice) { - case 'ur1.ca': - $short_url_service = new LilUrl; - $short_url = $short_url_service->shorten($url); - break; - - case '2tu.us': - $short_url_service = new TightUrl; - $short_url = $short_url_service->shorten($url); - break; - - case 'ptiturl.com': - $short_url_service = new PtitUrl; - $short_url = $short_url_service->shorten($url); - break; - - case 'bit.ly': - curl_setopt($curlh, CURLOPT_URL, 'http://bit.ly/api?method=shorten&long_url='.urlencode($url)); - $short_url = current(json_decode(curl_exec($curlh))->results)->hashUrl; - break; - - case 'is.gd': - curl_setopt($curlh, CURLOPT_URL, 'http://is.gd/api.php?longurl='.urlencode($url)); - $short_url = curl_exec($curlh); - break; - case 'snipr.com': - curl_setopt($curlh, CURLOPT_URL, 'http://snipr.com/site/snip?r=simple&link='.urlencode($url)); - $short_url = curl_exec($curlh); - break; - case 'metamark.net': - curl_setopt($curlh, CURLOPT_URL, 'http://metamark.net/api/rest/simple?long_url='.urlencode($url)); - $short_url = curl_exec($curlh); - break; - case 'tinyurl.com': - curl_setopt($curlh, CURLOPT_URL, 'http://tinyurl.com/api-create.php?url='.urlencode($url)); - $short_url = curl_exec($curlh); - break; - default: - $short_url = false; - } - - curl_close($curlh); - - if ($short_url) { + $user = common_current_user(); + + $curlh = curl_init(); + curl_setopt($curlh, CURLOPT_CONNECTTIMEOUT, 20); // # seconds to wait + curl_setopt($curlh, CURLOPT_USERAGENT, 'Laconica'); + curl_setopt($curlh, CURLOPT_RETURNTRANSFER, true); + + switch($user->urlshorteningservice) { + case 'ur1.ca': + $short_url_service = new LilUrl; + $short_url = $short_url_service->shorten($url); + break; + + case '2tu.us': + $short_url_service = new TightUrl; + $short_url = $short_url_service->shorten($url); + break; + + case 'ptiturl.com': + $short_url_service = new PtitUrl; + $short_url = $short_url_service->shorten($url); + break; + + case 'bit.ly': + curl_setopt($curlh, CURLOPT_URL, 'http://bit.ly/api?method=shorten&long_url='.urlencode($url)); + $short_url = current(json_decode(curl_exec($curlh))->results)->hashUrl; + break; + + case 'is.gd': + curl_setopt($curlh, CURLOPT_URL, 'http://is.gd/api.php?longurl='.urlencode($url)); + $short_url = curl_exec($curlh); + break; + case 'snipr.com': + curl_setopt($curlh, CURLOPT_URL, 'http://snipr.com/site/snip?r=simple&link='.urlencode($url)); + $short_url = curl_exec($curlh); + break; + case 'metamark.net': + curl_setopt($curlh, CURLOPT_URL, 'http://metamark.net/api/rest/simple?long_url='.urlencode($url)); + $short_url = curl_exec($curlh); + break; + case 'tinyurl.com': + curl_setopt($curlh, CURLOPT_URL, 'http://tinyurl.com/api-create.php?url='.urlencode($url)); + $short_url = curl_exec($curlh); + break; + default: + $short_url = false; + } + + curl_close($curlh); + + if ($short_url) { $url_cache[(string)$short_url] = $url; - return (string)$short_url; - } - return $url; -} - -function common_xml_safe_str($str) { - $xmlStr = htmlentities(iconv('UTF-8', 'UTF-8//IGNORE', $str), ENT_NOQUOTES, 'UTF-8'); - - // Replace control, formatting, and surrogate characters with '*', ala Twitter - return preg_replace('/[\p{Cc}\p{Cf}\p{Cs}]/u', '*', $str); -} - -function common_tag_link($tag) { - $canonical = common_canonical_tag($tag); - $url = common_local_url('tag', array('tag' => $canonical)); - return '<a href="' . htmlspecialchars($url) . '" rel="tag" class="hashlink">' . htmlspecialchars($tag) . '</a>'; -} - -function common_canonical_tag($tag) { - return strtolower(str_replace(array('-', '_', '.'), '', $tag)); -} - -function common_valid_profile_tag($str) { - return preg_match('/^[A-Za-z0-9_\-\.]{1,64}$/', $str); -} - -function common_at_link($sender_id, $nickname) { - $sender = Profile::staticGet($sender_id); - $recipient = common_relative_profile($sender, common_canonical_nickname($nickname)); - if ($recipient) { - return '<a href="'.htmlspecialchars($recipient->profileurl).'" class="atlink">'.$nickname.'</a>'; - } else { - return $nickname; - } -} - -function common_at_hash_link($sender_id, $tag) { - $user = User::staticGet($sender_id); - if (!$user) { - return $tag; - } - $tagged = Profile_tag::getTagged($user->id, common_canonical_tag($tag)); - if ($tagged) { - $url = common_local_url('subscriptions', - array('nickname' => $user->nickname, - 'tag' => $tag)); - return '<a href="'.htmlspecialchars($url).'" class="atlink">'.$tag.'</a>'; - } else { - return $tag; - } -} - -function common_relative_profile($sender, $nickname, $dt=NULL) { - # Try to find profiles this profile is subscribed to that have this nickname - $recipient = new Profile(); - # XXX: use a join instead of a subquery - $recipient->whereAdd('EXISTS (SELECT subscribed from subscription where subscriber = '.$sender->id.' and subscribed = id)', 'AND'); - $recipient->whereAdd('nickname = "' . trim($nickname) . '"', 'AND'); - if ($recipient->find(TRUE)) { - # XXX: should probably differentiate between profiles with - # the same name by date of most recent update - return $recipient; - } - # Try to find profiles that listen to this profile and that have this nickname - $recipient = new Profile(); - # XXX: use a join instead of a subquery - $recipient->whereAdd('EXISTS (SELECT subscriber from subscription where subscribed = '.$sender->id.' and subscriber = id)', 'AND'); - $recipient->whereAdd('nickname = "' . trim($nickname) . '"', 'AND'); - if ($recipient->find(TRUE)) { - # XXX: should probably differentiate between profiles with - # the same name by date of most recent update - return $recipient; - } - # If this is a local user, try to find a local user with that nickname. - $sender = User::staticGet($sender->id); - if ($sender) { - $recipient_user = User::staticGet('nickname', $nickname); - if ($recipient_user) { - return $recipient_user->getProfile(); - } - } - # Otherwise, no links. @messages from local users to remote users, - # or from remote users to other remote users, are just - # outside our ability to make intelligent guesses about - return NULL; + return (string)$short_url; + } + return $url; +} + +function common_xml_safe_str($str) +{ + $xmlStr = htmlentities(iconv('UTF-8', 'UTF-8//IGNORE', $str), ENT_NOQUOTES, 'UTF-8'); + + // Replace control, formatting, and surrogate characters with '*', ala Twitter + return preg_replace('/[\p{Cc}\p{Cf}\p{Cs}]/u', '*', $str); +} + +function common_tag_link($tag) +{ + $canonical = common_canonical_tag($tag); + $url = common_local_url('tag', array('tag' => $canonical)); + return '<a href="' . htmlspecialchars($url) . '" rel="tag" class="hashlink">' . htmlspecialchars($tag) . '</a>'; +} + +function common_canonical_tag($tag) +{ + return strtolower(str_replace(array('-', '_', '.'), '', $tag)); +} + +function common_valid_profile_tag($str) +{ + return preg_match('/^[A-Za-z0-9_\-\.]{1,64}$/', $str); +} + +function common_at_link($sender_id, $nickname) +{ + $sender = Profile::staticGet($sender_id); + $recipient = common_relative_profile($sender, common_canonical_nickname($nickname)); + if ($recipient) { + return '<a href="'.htmlspecialchars($recipient->profileurl).'" class="atlink">'.$nickname.'</a>'; + } else { + return $nickname; + } +} + +function common_at_hash_link($sender_id, $tag) +{ + $user = User::staticGet($sender_id); + if (!$user) { + return $tag; + } + $tagged = Profile_tag::getTagged($user->id, common_canonical_tag($tag)); + if ($tagged) { + $url = common_local_url('subscriptions', + array('nickname' => $user->nickname, + 'tag' => $tag)); + return '<a href="'.htmlspecialchars($url).'" class="atlink">'.$tag.'</a>'; + } else { + return $tag; + } +} + +function common_relative_profile($sender, $nickname, $dt=null) +{ + // Try to find profiles this profile is subscribed to that have this nickname + $recipient = new Profile(); + // XXX: use a join instead of a subquery + $recipient->whereAdd('EXISTS (SELECT subscribed from subscription where subscriber = '.$sender->id.' and subscribed = id)', 'AND'); + $recipient->whereAdd('nickname = "' . trim($nickname) . '"', 'AND'); + if ($recipient->find(true)) { + // XXX: should probably differentiate between profiles with + // the same name by date of most recent update + return $recipient; + } + // Try to find profiles that listen to this profile and that have this nickname + $recipient = new Profile(); + // XXX: use a join instead of a subquery + $recipient->whereAdd('EXISTS (SELECT subscriber from subscription where subscribed = '.$sender->id.' and subscriber = id)', 'AND'); + $recipient->whereAdd('nickname = "' . trim($nickname) . '"', 'AND'); + if ($recipient->find(true)) { + // XXX: should probably differentiate between profiles with + // the same name by date of most recent update + return $recipient; + } + // If this is a local user, try to find a local user with that nickname. + $sender = User::staticGet($sender->id); + if ($sender) { + $recipient_user = User::staticGet('nickname', $nickname); + if ($recipient_user) { + return $recipient_user->getProfile(); + } + } + // Otherwise, no links. @messages from local users to remote users, + // or from remote users to other remote users, are just + // outside our ability to make intelligent guesses about + return null; } // where should the avatar go for this user? -function common_avatar_filename($id, $extension, $size=NULL, $extra=NULL) { - global $config; - - if ($size) { - return $id . '-' . $size . (($extra) ? ('-' . $extra) : '') . $extension; - } else { - return $id . '-original' . (($extra) ? ('-' . $extra) : '') . $extension; - } -} - -function common_avatar_path($filename) { - global $config; - return INSTALLDIR . '/avatar/' . $filename; -} - -function common_avatar_url($filename) { - return common_path('avatar/'.$filename); -} - -function common_avatar_display_url($avatar) { - $server = common_config('avatar', 'server'); - if ($server) { - return 'http://'.$server.'/'.$avatar->filename; - } else { - return $avatar->url; - } -} - -function common_default_avatar($size) { - static $sizenames = array(AVATAR_PROFILE_SIZE => 'profile', - AVATAR_STREAM_SIZE => 'stream', - AVATAR_MINI_SIZE => 'mini'); - return theme_path('default-avatar-'.$sizenames[$size].'.png'); -} - -function common_local_url($action, $args=NULL, $fragment=NULL) { - $url = NULL; - if (common_config('site','fancy')) { - $url = common_fancy_url($action, $args); - } else { - $url = common_simple_url($action, $args); - } - if (!is_null($fragment)) { - $url .= '#'.$fragment; - } - return $url; -} - -function common_fancy_url($action, $args=NULL) { - switch (strtolower($action)) { - case 'public': - if ($args && isset($args['page'])) { - return common_path('?page=' . $args['page']); - } else { - return common_path(''); - } - case 'featured': - if ($args && isset($args['page'])) { - return common_path('featured?page=' . $args['page']); - } else { - return common_path('featured'); - } - case 'favorited': - if ($args && isset($args['page'])) { - return common_path('favorited?page=' . $args['page']); - } else { - return common_path('favorited'); - } - case 'publicrss': - return common_path('rss'); - case 'publicatom': - return common_path("api/statuses/public_timeline.atom"); - case 'publicxrds': - return common_path('xrds'); - case 'featuredrss': - return common_path('featuredrss'); - case 'favoritedrss': - return common_path('favoritedrss'); - case 'opensearch': - if ($args && $args['type']) { - return common_path('opensearch/'.$args['type']); - } else { - return common_path('opensearch/people'); - } - case 'doc': - return common_path('doc/'.$args['title']); +function common_avatar_filename($id, $extension, $size=null, $extra=null) +{ + global $config; + + if ($size) { + return $id . '-' . $size . (($extra) ? ('-' . $extra) : '') . $extension; + } else { + return $id . '-original' . (($extra) ? ('-' . $extra) : '') . $extension; + } +} + +function common_avatar_path($filename) +{ + global $config; + return INSTALLDIR . '/avatar/' . $filename; +} + +function common_avatar_url($filename) +{ + return common_path('avatar/'.$filename); +} + +function common_avatar_display_url($avatar) +{ + $server = common_config('avatar', 'server'); + if ($server) { + return 'http://'.$server.'/'.$avatar->filename; + } else { + return $avatar->url; + } +} + +function common_default_avatar($size) +{ + static $sizenames = array(AVATAR_PROFILE_SIZE => 'profile', + AVATAR_STREAM_SIZE => 'stream', + AVATAR_MINI_SIZE => 'mini'); + return theme_path('default-avatar-'.$sizenames[$size].'.png'); +} + +function common_local_url($action, $args=null, $fragment=null) +{ + $url = null; + if (common_config('site','fancy')) { + $url = common_fancy_url($action, $args); + } else { + $url = common_simple_url($action, $args); + } + if (!is_null($fragment)) { + $url .= '#'.$fragment; + } + return $url; +} + +function common_fancy_url($action, $args=null) +{ + switch (strtolower($action)) { + case 'public': + if ($args && isset($args['page'])) { + return common_path('?page=' . $args['page']); + } else { + return common_path(''); + } + case 'featured': + if ($args && isset($args['page'])) { + return common_path('featured?page=' . $args['page']); + } else { + return common_path('featured'); + } + case 'favorited': + if ($args && isset($args['page'])) { + return common_path('favorited?page=' . $args['page']); + } else { + return common_path('favorited'); + } + case 'publicrss': + return common_path('rss'); + case 'publicatom': + return common_path("api/statuses/public_timeline.atom"); + case 'publicxrds': + return common_path('xrds'); + case 'featuredrss': + return common_path('featuredrss'); + case 'favoritedrss': + return common_path('favoritedrss'); + case 'opensearch': + if ($args && $args['type']) { + return common_path('opensearch/'.$args['type']); + } else { + return common_path('opensearch/people'); + } + case 'doc': + return common_path('doc/'.$args['title']); case 'block': - case 'login': - case 'logout': - case 'subscribe': - case 'unsubscribe': - case 'invite': - return common_path('main/'.$action); - case 'tagother': - return common_path('main/tagother?id='.$args['id']); - case 'register': - if ($args && $args['code']) { - return common_path('main/register/'.$args['code']); - } else { - return common_path('main/register'); - } - case 'remotesubscribe': - if ($args && $args['nickname']) { - return common_path('main/remote?nickname=' . $args['nickname']); - } else { - return common_path('main/remote'); - } - case 'nudge': - return common_path($args['nickname'].'/nudge'); - case 'openidlogin': - return common_path('main/openid'); - case 'profilesettings': - return common_path('settings/profile'); - case 'emailsettings': - return common_path('settings/email'); - case 'openidsettings': - return common_path('settings/openid'); - case 'smssettings': - return common_path('settings/sms'); - case 'twittersettings': - return common_path('settings/twitter'); - case 'othersettings': - return common_path('settings/other'); + case 'login': + case 'logout': + case 'subscribe': + case 'unsubscribe': + case 'invite': + return common_path('main/'.$action); + case 'tagother': + return common_path('main/tagother?id='.$args['id']); + case 'register': + if ($args && $args['code']) { + return common_path('main/register/'.$args['code']); + } else { + return common_path('main/register'); + } + case 'remotesubscribe': + if ($args && $args['nickname']) { + return common_path('main/remote?nickname=' . $args['nickname']); + } else { + return common_path('main/remote'); + } + case 'nudge': + return common_path($args['nickname'].'/nudge'); + case 'openidlogin': + return common_path('main/openid'); + case 'profilesettings': + return common_path('settings/profile'); + case 'emailsettings': + return common_path('settings/email'); + case 'openidsettings': + return common_path('settings/openid'); + case 'smssettings': + return common_path('settings/sms'); + case 'twittersettings': + return common_path('settings/twitter'); + case 'othersettings': + return common_path('settings/other'); case 'deleteprofile': return common_path('settings/delete'); - case 'newnotice': - if ($args && $args['replyto']) { - return common_path('notice/new?replyto='.$args['replyto']); - } else { - return common_path('notice/new'); - } - case 'shownotice': - return common_path('notice/'.$args['notice']); - case 'deletenotice': - if ($args && $args['notice']) { - return common_path('notice/delete/'.$args['notice']); - } else { - return common_path('notice/delete'); - } - case 'microsummary': - case 'xrds': - case 'foaf': - return common_path($args['nickname'].'/'.$action); - case 'all': - case 'replies': - case 'inbox': - case 'outbox': - if ($args && isset($args['page'])) { - return common_path($args['nickname'].'/'.$action.'?page=' . $args['page']); - } else { - return common_path($args['nickname'].'/'.$action); - } - case 'subscriptions': - case 'subscribers': - $nickname = $args['nickname']; - unset($args['nickname']); - if (isset($args['tag'])) { - $tag = $args['tag']; - unset($args['tag']); - } - $params = http_build_query($args); - if ($params) { - return common_path($nickname.'/'.$action . (($tag) ? '/' . $tag : '') . '?' . $params); - } else { - return common_path($nickname.'/'.$action . (($tag) ? '/' . $tag : '')); - } - case 'allrss': - return common_path($args['nickname'].'/all/rss'); - case 'repliesrss': - return common_path($args['nickname'].'/replies/rss'); - case 'userrss': + case 'newnotice': + if ($args && $args['replyto']) { + return common_path('notice/new?replyto='.$args['replyto']); + } else { + return common_path('notice/new'); + } + case 'shownotice': + return common_path('notice/'.$args['notice']); + case 'deletenotice': + if ($args && $args['notice']) { + return common_path('notice/delete/'.$args['notice']); + } else { + return common_path('notice/delete'); + } + case 'microsummary': + case 'xrds': + case 'foaf': + return common_path($args['nickname'].'/'.$action); + case 'all': + case 'replies': + case 'inbox': + case 'outbox': + if ($args && isset($args['page'])) { + return common_path($args['nickname'].'/'.$action.'?page=' . $args['page']); + } else { + return common_path($args['nickname'].'/'.$action); + } + case 'subscriptions': + case 'subscribers': + $nickname = $args['nickname']; + unset($args['nickname']); + if (isset($args['tag'])) { + $tag = $args['tag']; + unset($args['tag']); + } + $params = http_build_query($args); + if ($params) { + return common_path($nickname.'/'.$action . (($tag) ? '/' . $tag : '') . '?' . $params); + } else { + return common_path($nickname.'/'.$action . (($tag) ? '/' . $tag : '')); + } + case 'allrss': + return common_path($args['nickname'].'/all/rss'); + case 'repliesrss': + return common_path($args['nickname'].'/replies/rss'); + case 'userrss': if (isset($args['limit'])) - return common_path($args['nickname'].'/rss?limit=' . $args['limit']); - return common_path($args['nickname'].'/rss'); - case 'showstream': - if ($args && isset($args['page'])) { - return common_path($args['nickname'].'?page=' . $args['page']); - } else { - return common_path($args['nickname']); - } - - case 'usertimeline': - return common_path("api/statuses/user_timeline/".$args['nickname'].".atom"); - case 'confirmaddress': - return common_path('main/confirmaddress/'.$args['code']); - case 'userbyid': - return common_path('user/'.$args['id']); - case 'recoverpassword': - $path = 'main/recoverpassword'; - if ($args['code']) { - $path .= '/' . $args['code']; - } - return common_path($path); - case 'imsettings': - return common_path('settings/im'); - case 'peoplesearch': - return common_path('search/people' . (($args) ? ('?' . http_build_query($args)) : '')); - case 'noticesearch': - return common_path('search/notice' . (($args) ? ('?' . http_build_query($args)) : '')); - case 'noticesearchrss': - return common_path('search/notice/rss' . (($args) ? ('?' . http_build_query($args)) : '')); - case 'avatarbynickname': - return common_path($args['nickname'].'/avatar/'.$args['size']); - case 'tag': - if (isset($args['tag']) && $args['tag']) { - $path = 'tag/' . $args['tag']; - unset($args['tag']); - } else { - $path = 'tags'; - } - return common_path($path . (($args) ? ('?' . http_build_query($args)) : '')); - case 'peopletag': - $path = 'peopletag/' . $args['tag']; - unset($args['tag']); - return common_path($path . (($args) ? ('?' . http_build_query($args)) : '')); - case 'tags': - return common_path('tags' . (($args) ? ('?' . http_build_query($args)) : '')); - case 'favor': - return common_path('main/favor'); - case 'disfavor': - return common_path('main/disfavor'); - case 'showfavorites': - if ($args && isset($args['page'])) { - return common_path($args['nickname'].'/favorites?page=' . $args['page']); - } else { - return common_path($args['nickname'].'/favorites'); - } - case 'favoritesrss': - return common_path($args['nickname'].'/favorites/rss'); - case 'showmessage': - return common_path('message/' . $args['message']); - case 'newmessage': - return common_path('message/new' . (($args) ? ('?' . http_build_query($args)) : '')); - case 'api': - # XXX: do fancy URLs for all the API methods - switch (strtolower($args['apiaction'])) { - case 'statuses': - switch (strtolower($args['method'])) { - case 'user_timeline.rss': - return common_path('api/statuses/user_timeline/'.$args['argument'].'.rss'); - case 'user_timeline.atom': - return common_path('api/statuses/user_timeline/'.$args['argument'].'.atom'); - case 'user_timeline.json': - return common_path('api/statuses/user_timeline/'.$args['argument'].'.json'); - case 'user_timeline.xml': - return common_path('api/statuses/user_timeline/'.$args['argument'].'.xml'); - default: return common_simple_url($action, $args); - } - default: return common_simple_url($action, $args); - } - case 'sup': - if ($args && isset($args['seconds'])) { - return common_path('main/sup?seconds='.$args['seconds']); - } else { - return common_path('main/sup'); - } - default: - return common_simple_url($action, $args); - } -} - -function common_simple_url($action, $args=NULL) { - global $config; - /* XXX: pretty URLs */ - $extra = ''; - if ($args) { - foreach ($args as $key => $value) { - $extra .= "&${key}=${value}"; - } - } - return common_path("index.php?action=${action}${extra}"); -} - -function common_path($relative) { - global $config; - $pathpart = ($config['site']['path']) ? $config['site']['path']."/" : ''; - return "http://".$config['site']['server'].'/'.$pathpart.$relative; -} - -function common_date_string($dt) { - // XXX: do some sexy date formatting - // return date(DATE_RFC822, $dt); - $t = strtotime($dt); - $now = time(); - $diff = $now - $t; - - if ($now < $t) { # that shouldn't happen! - return common_exact_date($dt); - } else if ($diff < 60) { - return _('a few seconds ago'); - } else if ($diff < 92) { - return _('about a minute ago'); - } else if ($diff < 3300) { - return sprintf(_('about %d minutes ago'), round($diff/60)); - } else if ($diff < 5400) { - return _('about an hour ago'); - } else if ($diff < 22 * 3600) { - return sprintf(_('about %d hours ago'), round($diff/3600)); - } else if ($diff < 37 * 3600) { - return _('about a day ago'); - } else if ($diff < 24 * 24 * 3600) { - return sprintf(_('about %d days ago'), round($diff/(24*3600))); - } else if ($diff < 46 * 24 * 3600) { - return _('about a month ago'); - } else if ($diff < 330 * 24 * 3600) { - return sprintf(_('about %d months ago'), round($diff/(30*24*3600))); - } else if ($diff < 480 * 24 * 3600) { - return _('about a year ago'); - } else { - return common_exact_date($dt); - } -} - -function common_exact_date($dt) { + return common_path($args['nickname'].'/rss?limit=' . $args['limit']); + return common_path($args['nickname'].'/rss'); + case 'showstream': + if ($args && isset($args['page'])) { + return common_path($args['nickname'].'?page=' . $args['page']); + } else { + return common_path($args['nickname']); + } + + case 'usertimeline': + return common_path("api/statuses/user_timeline/".$args['nickname'].".atom"); + case 'confirmaddress': + return common_path('main/confirmaddress/'.$args['code']); + case 'userbyid': + return common_path('user/'.$args['id']); + case 'recoverpassword': + $path = 'main/recoverpassword'; + if ($args['code']) { + $path .= '/' . $args['code']; + } + return common_path($path); + case 'imsettings': + return common_path('settings/im'); + case 'peoplesearch': + return common_path('search/people' . (($args) ? ('?' . http_build_query($args)) : '')); + case 'noticesearch': + return common_path('search/notice' . (($args) ? ('?' . http_build_query($args)) : '')); + case 'noticesearchrss': + return common_path('search/notice/rss' . (($args) ? ('?' . http_build_query($args)) : '')); + case 'avatarbynickname': + return common_path($args['nickname'].'/avatar/'.$args['size']); + case 'tag': + if (isset($args['tag']) && $args['tag']) { + $path = 'tag/' . $args['tag']; + unset($args['tag']); + } else { + $path = 'tags'; + } + return common_path($path . (($args) ? ('?' . http_build_query($args)) : '')); + case 'peopletag': + $path = 'peopletag/' . $args['tag']; + unset($args['tag']); + return common_path($path . (($args) ? ('?' . http_build_query($args)) : '')); + case 'tags': + return common_path('tags' . (($args) ? ('?' . http_build_query($args)) : '')); + case 'favor': + return common_path('main/favor'); + case 'disfavor': + return common_path('main/disfavor'); + case 'showfavorites': + if ($args && isset($args['page'])) { + return common_path($args['nickname'].'/favorites?page=' . $args['page']); + } else { + return common_path($args['nickname'].'/favorites'); + } + case 'favoritesrss': + return common_path($args['nickname'].'/favorites/rss'); + case 'showmessage': + return common_path('message/' . $args['message']); + case 'newmessage': + return common_path('message/new' . (($args) ? ('?' . http_build_query($args)) : '')); + case 'api': + // XXX: do fancy URLs for all the API methods + switch (strtolower($args['apiaction'])) { + case 'statuses': + switch (strtolower($args['method'])) { + case 'user_timeline.rss': + return common_path('api/statuses/user_timeline/'.$args['argument'].'.rss'); + case 'user_timeline.atom': + return common_path('api/statuses/user_timeline/'.$args['argument'].'.atom'); + case 'user_timeline.json': + return common_path('api/statuses/user_timeline/'.$args['argument'].'.json'); + case 'user_timeline.xml': + return common_path('api/statuses/user_timeline/'.$args['argument'].'.xml'); + default: return common_simple_url($action, $args); + } + default: return common_simple_url($action, $args); + } + case 'sup': + if ($args && isset($args['seconds'])) { + return common_path('main/sup?seconds='.$args['seconds']); + } else { + return common_path('main/sup'); + } + default: + return common_simple_url($action, $args); + } +} + +function common_simple_url($action, $args=null) +{ + global $config; + /* XXX: pretty URLs */ + $extra = ''; + if ($args) { + foreach ($args as $key => $value) { + $extra .= "&${key}=${value}"; + } + } + return common_path("index.php?action=${action}${extra}"); +} + +function common_path($relative) +{ + global $config; + $pathpart = ($config['site']['path']) ? $config['site']['path']."/" : ''; + return "http://".$config['site']['server'].'/'.$pathpart.$relative; +} + +function common_date_string($dt) +{ + // XXX: do some sexy date formatting + // return date(DATE_RFC822, $dt); + $t = strtotime($dt); + $now = time(); + $diff = $now - $t; + + if ($now < $t) { // that shouldn't happen! + return common_exact_date($dt); + } else if ($diff < 60) { + return _('a few seconds ago'); + } else if ($diff < 92) { + return _('about a minute ago'); + } else if ($diff < 3300) { + return sprintf(_('about %d minutes ago'), round($diff/60)); + } else if ($diff < 5400) { + return _('about an hour ago'); + } else if ($diff < 22 * 3600) { + return sprintf(_('about %d hours ago'), round($diff/3600)); + } else if ($diff < 37 * 3600) { + return _('about a day ago'); + } else if ($diff < 24 * 24 * 3600) { + return sprintf(_('about %d days ago'), round($diff/(24*3600))); + } else if ($diff < 46 * 24 * 3600) { + return _('about a month ago'); + } else if ($diff < 330 * 24 * 3600) { + return sprintf(_('about %d months ago'), round($diff/(30*24*3600))); + } else if ($diff < 480 * 24 * 3600) { + return _('about a year ago'); + } else { + return common_exact_date($dt); + } +} + +function common_exact_date($dt) +{ static $_utc; static $_siteTz; @@ -1317,888 +1382,947 @@ function common_exact_date($dt) { $_siteTz = new DateTimeZone(common_timezone()); } - $dateStr = date('d F Y H:i:s', strtotime($dt)); - $d = new DateTime($dateStr, $_utc); - $d->setTimezone($_siteTz); - return $d->format(DATE_RFC850); + $dateStr = date('d F Y H:i:s', strtotime($dt)); + $d = new DateTime($dateStr, $_utc); + $d->setTimezone($_siteTz); + return $d->format(DATE_RFC850); } -function common_date_w3dtf($dt) { - $dateStr = date('d F Y H:i:s', strtotime($dt)); - $d = new DateTime($dateStr, new DateTimeZone('UTC')); - $d->setTimezone(new DateTimeZone(common_timezone())); - return $d->format(DATE_W3C); +function common_date_w3dtf($dt) +{ + $dateStr = date('d F Y H:i:s', strtotime($dt)); + $d = new DateTime($dateStr, new DateTimeZone('UTC')); + $d->setTimezone(new DateTimeZone(common_timezone())); + return $d->format(DATE_W3C); } -function common_date_rfc2822($dt) { - $dateStr = date('d F Y H:i:s', strtotime($dt)); - $d = new DateTime($dateStr, new DateTimeZone('UTC')); - $d->setTimezone(new DateTimeZone(common_timezone())); - return $d->format('r'); +function common_date_rfc2822($dt) +{ + $dateStr = date('d F Y H:i:s', strtotime($dt)); + $d = new DateTime($dateStr, new DateTimeZone('UTC')); + $d->setTimezone(new DateTimeZone(common_timezone())); + return $d->format('r'); } -function common_date_iso8601($dt) { - $dateStr = date('d F Y H:i:s', strtotime($dt)); - $d = new DateTime($dateStr, new DateTimeZone('UTC')); - $d->setTimezone(new DateTimeZone(common_timezone())); - return $d->format('c'); +function common_date_iso8601($dt) +{ + $dateStr = date('d F Y H:i:s', strtotime($dt)); + $d = new DateTime($dateStr, new DateTimeZone('UTC')); + $d->setTimezone(new DateTimeZone(common_timezone())); + return $d->format('c'); } -function common_sql_now() { - return strftime('%Y-%m-%d %H:%M:%S', time()); +function common_sql_now() +{ + return strftime('%Y-%m-%d %H:%M:%S', time()); } -function common_redirect($url, $code=307) { - static $status = array(301 => "Moved Permanently", - 302 => "Found", - 303 => "See Other", - 307 => "Temporary Redirect"); - header("Status: ${code} $status[$code]"); - header("Location: $url"); - - common_start_xml('a', - '-//W3C//DTD XHTML 1.0 Strict//EN', - 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'); - common_element('a', array('href' => $url), $url); - common_end_xml(); +function common_redirect($url, $code=307) +{ + static $status = array(301 => "Moved Permanently", + 302 => "Found", + 303 => "See Other", + 307 => "Temporary Redirect"); + header("Status: ${code} $status[$code]"); + header("Location: $url"); + + common_start_xml('a', + '-//W3C//DTD XHTML 1.0 Strict//EN', + 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'); + common_element('a', array('href' => $url), $url); + common_end_xml(); exit; } -function common_save_replies($notice) { - # Alternative reply format - $tname = false; - if (preg_match('/^T ([A-Z0-9]{1,64}) /', $notice->content, $match)) { - $tname = $match[1]; - } - # extract all @messages - $cnt = preg_match_all('/(?:^|\s)@([a-z0-9]{1,64})/', $notice->content, $match); - - $names = array(); - - if ($cnt || $tname) { - # XXX: is there another way to make an array copy? - $names = ($tname) ? array_unique(array_merge(array(strtolower($tname)), $match[1])) : array_unique($match[1]); - } - - $sender = Profile::staticGet($notice->profile_id); - - $replied = array(); - - # store replied only for first @ (what user/notice what the reply directed, - # we assume first @ is it) - - for ($i=0; $i<count($names); $i++) { - $nickname = $names[$i]; - $recipient = common_relative_profile($sender, $nickname, $notice->created); - if (!$recipient) { - continue; - } - if ($i == 0 && ($recipient->id != $sender->id) && !$notice->reply_to) { # Don't save reply to self - $reply_for = $recipient; - $recipient_notice = $reply_for->getCurrentNotice(); - if ($recipient_notice) { - $orig = clone($notice); - $notice->reply_to = $recipient_notice->id; - $notice->update($orig); - } - } - # Don't save replies from blocked profile to local user +function common_save_replies($notice) +{ + // Alternative reply format + $tname = false; + if (preg_match('/^T ([A-Z0-9]{1,64}) /', $notice->content, $match)) { + $tname = $match[1]; + } + // extract all @messages + $cnt = preg_match_all('/(?:^|\s)@([a-z0-9]{1,64})/', $notice->content, $match); + + $names = array(); + + if ($cnt || $tname) { + // XXX: is there another way to make an array copy? + $names = ($tname) ? array_unique(array_merge(array(strtolower($tname)), $match[1])) : array_unique($match[1]); + } + + $sender = Profile::staticGet($notice->profile_id); + + $replied = array(); + + // store replied only for first @ (what user/notice what the reply directed, + // we assume first @ is it) + + for ($i=0; $i<count($names); $i++) { + $nickname = $names[$i]; + $recipient = common_relative_profile($sender, $nickname, $notice->created); + if (!$recipient) { + continue; + } + if ($i == 0 && ($recipient->id != $sender->id) && !$notice->reply_to) { // Don't save reply to self + $reply_for = $recipient; + $recipient_notice = $reply_for->getCurrentNotice(); + if ($recipient_notice) { + $orig = clone($notice); + $notice->reply_to = $recipient_notice->id; + $notice->update($orig); + } + } + // Don't save replies from blocked profile to local user $recipient_user = User::staticGet('id', $recipient->id); if ($recipient_user && $recipient_user->hasBlocked($sender)) { continue; } - $reply = new Reply(); - $reply->notice_id = $notice->id; - $reply->profile_id = $recipient->id; - $id = $reply->insert(); - if (!$id) { - $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError'); - common_log(LOG_ERR, 'DB error inserting reply: ' . $last_error->message); - common_server_error(sprintf(_('DB error inserting reply: %s'), $last_error->message)); - return; - } else { - $replied[$recipient->id] = 1; - } - } - - # Hash format replies, too - $cnt = preg_match_all('/(?:^|\s)@#([a-z0-9]{1,64})/', $notice->content, $match); - if ($cnt) { - foreach ($match[1] as $tag) { - $tagged = Profile_tag::getTagged($sender->id, $tag); - foreach ($tagged as $t) { - if (!$replied[$t->id]) { - # Don't save replies from blocked profile to local user + $reply = new Reply(); + $reply->notice_id = $notice->id; + $reply->profile_id = $recipient->id; + $id = $reply->insert(); + if (!$id) { + $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError'); + common_log(LOG_ERR, 'DB error inserting reply: ' . $last_error->message); + common_server_error(sprintf(_('DB error inserting reply: %s'), $last_error->message)); + return; + } else { + $replied[$recipient->id] = 1; + } + } + + // Hash format replies, too + $cnt = preg_match_all('/(?:^|\s)@#([a-z0-9]{1,64})/', $notice->content, $match); + if ($cnt) { + foreach ($match[1] as $tag) { + $tagged = Profile_tag::getTagged($sender->id, $tag); + foreach ($tagged as $t) { + if (!$replied[$t->id]) { + // Don't save replies from blocked profile to local user $t_user = User::staticGet('id', $t->id); if ($t_user && $t_user->hasBlocked($sender)) { continue; } - $reply = new Reply(); - $reply->notice_id = $notice->id; - $reply->profile_id = $t->id; - $id = $reply->insert(); - if (!$id) { - common_log_db_error($reply, 'INSERT', __FILE__); - return; - } - } - } - } - } -} - -function common_broadcast_notice($notice, $remote=false) { - - // Check to see if notice should go to Twitter - $flink = Foreign_link::getByUserID($notice->profile_id, 1); // 1 == Twitter - if (($flink->noticesync & FOREIGN_NOTICE_SEND) == FOREIGN_NOTICE_SEND) { - - // If it's not a Twitter-style reply, or if the user WANTS to send replies... - - if (!preg_match('/^@[a-zA-Z0-9_]{1,15}\b/u', $notice->content) || - (($flink->noticesync & FOREIGN_NOTICE_SEND_REPLY) == FOREIGN_NOTICE_SEND_REPLY)) { - - $result = common_twitter_broadcast($notice, $flink); - - if (!$result) { - common_debug('Unable to send notice: ' . $notice->id . ' to Twitter.', __FILE__); - } - } - } - - if (common_config('queue', 'enabled')) { - # Do it later! - return common_enqueue_notice($notice); - } else { - return common_real_broadcast($notice, $remote); - } -} - -function common_twitter_broadcast($notice, $flink) { - global $config; - $success = true; - $fuser = $flink->getForeignUser(); - $twitter_user = $fuser->nickname; - $twitter_password = $flink->credentials; - $uri = 'http://www.twitter.com/statuses/update.json'; - - // XXX: Hack to get around PHP cURL's use of @ being a a meta character - $statustxt = preg_replace('/^@/', ' @', $notice->content); - - $options = array( - CURLOPT_USERPWD => "$twitter_user:$twitter_password", - CURLOPT_POST => true, - CURLOPT_POSTFIELDS => array( - 'status' => $statustxt, - 'source' => $config['integration']['source'] - ), - CURLOPT_RETURNTRANSFER => true, - CURLOPT_FAILONERROR => true, - CURLOPT_HEADER => false, - CURLOPT_FOLLOWLOCATION => true, - CURLOPT_USERAGENT => "Laconica", - CURLOPT_CONNECTTIMEOUT => 120, // XXX: Scary!!!! How long should this be? - CURLOPT_TIMEOUT => 120, - - # Twitter is strict about accepting invalid "Expect" headers - CURLOPT_HTTPHEADER => array('Expect:') - ); - - $ch = curl_init($uri); + $reply = new Reply(); + $reply->notice_id = $notice->id; + $reply->profile_id = $t->id; + $id = $reply->insert(); + if (!$id) { + common_log_db_error($reply, 'INSERT', __FILE__); + return; + } + } + } + } + } +} + +function common_broadcast_notice($notice, $remote=false) +{ + + // Check to see if notice should go to Twitter + $flink = Foreign_link::getByUserID($notice->profile_id, 1); // 1 == Twitter + if (($flink->noticesync & FOREIGN_NOTICE_SEND) == FOREIGN_NOTICE_SEND) { + + // If it's not a Twitter-style reply, or if the user WANTS to send replies... + + if (!preg_match('/^@[a-zA-Z0-9_]{1,15}\b/u', $notice->content) || + (($flink->noticesync & FOREIGN_NOTICE_SEND_REPLY) == FOREIGN_NOTICE_SEND_REPLY)) { + + $result = common_twitter_broadcast($notice, $flink); + + if (!$result) { + common_debug('Unable to send notice: ' . $notice->id . ' to Twitter.', __FILE__); + } + } + } + + if (common_config('queue', 'enabled')) { + // Do it later! + return common_enqueue_notice($notice); + } else { + return common_real_broadcast($notice, $remote); + } +} + +function common_twitter_broadcast($notice, $flink) +{ + global $config; + $success = true; + $fuser = $flink->getForeignUser(); + $twitter_user = $fuser->nickname; + $twitter_password = $flink->credentials; + $uri = 'http://www.twitter.com/statuses/update.json'; + + // XXX: Hack to get around PHP cURL's use of @ being a a meta character + $statustxt = preg_replace('/^@/', ' @', $notice->content); + + $options = array( + CURLOPT_USERPWD => "$twitter_user:$twitter_password", + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => array( + 'status' => $statustxt, + 'source' => $config['integration']['source'] + ), + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FAILONERROR => true, + CURLOPT_HEADER => false, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_USERAGENT => "Laconica", + CURLOPT_CONNECTTIMEOUT => 120, // XXX: Scary!!!! How long should this be? + CURLOPT_TIMEOUT => 120, + + # Twitter is strict about accepting invalid "Expect" headers + CURLOPT_HTTPHEADER => array('Expect:') + ); + + $ch = curl_init($uri); curl_setopt_array($ch, $options); $data = curl_exec($ch); $errmsg = curl_error($ch); - if ($errmsg) { - common_debug("cURL error: $errmsg - trying to send notice for $twitter_user.", - __FILE__); - $success = false; - } + if ($errmsg) { + common_debug("cURL error: $errmsg - trying to send notice for $twitter_user.", + __FILE__); + $success = false; + } - curl_close($ch); + curl_close($ch); - if (!$data) { - common_debug("No data returned by Twitter's API trying to send update for $twitter_user", - __FILE__); - $success = false; - } + if (!$data) { + common_debug("No data returned by Twitter's API trying to send update for $twitter_user", + __FILE__); + $success = false; + } - // Twitter should return a status - $status = json_decode($data); + // Twitter should return a status + $status = json_decode($data); - if (!$status->id) { - common_debug("Unexpected data returned by Twitter API trying to send update for $twitter_user", - __FILE__); - $success = false; - } + if (!$status->id) { + common_debug("Unexpected data returned by Twitter API trying to send update for $twitter_user", + __FILE__); + $success = false; + } - return $success; + return $success; } -# Stick the notice on the queue +// Stick the notice on the queue -function common_enqueue_notice($notice) { - foreach (array('jabber', 'omb', 'sms', 'public') as $transport) { - $qi = new Queue_item(); - $qi->notice_id = $notice->id; - $qi->transport = $transport; - $qi->created = $notice->created; +function common_enqueue_notice($notice) +{ + foreach (array('jabber', 'omb', 'sms', 'public') as $transport) { + $qi = new Queue_item(); + $qi->notice_id = $notice->id; + $qi->transport = $transport; + $qi->created = $notice->created; $result = $qi->insert(); - if (!$result) { - $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError'); - common_log(LOG_ERR, 'DB error inserting queue item: ' . $last_error->message); - return false; - } - common_log(LOG_DEBUG, 'complete queueing notice ID = ' . $notice->id . ' for ' . $transport); - } - return $result; -} - -function common_dequeue_notice($notice) { - $qi = Queue_item::staticGet($notice->id); - if ($qi) { - $result = $qi->delete(); - if (!$result) { - $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError'); - common_log(LOG_ERR, 'DB error deleting queue item: ' . $last_error->message); - return false; - } - common_log(LOG_DEBUG, 'complete dequeueing notice ID = ' . $notice->id); - return $result; - } else { + if (!$result) { + $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError'); + common_log(LOG_ERR, 'DB error inserting queue item: ' . $last_error->message); + return false; + } + common_log(LOG_DEBUG, 'complete queueing notice ID = ' . $notice->id . ' for ' . $transport); + } + return $result; +} + +function common_dequeue_notice($notice) +{ + $qi = Queue_item::staticGet($notice->id); + if ($qi) { + $result = $qi->delete(); + if (!$result) { + $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError'); + common_log(LOG_ERR, 'DB error deleting queue item: ' . $last_error->message); return false; } + common_log(LOG_DEBUG, 'complete dequeueing notice ID = ' . $notice->id); + return $result; + } else { + return false; + } +} + +function common_real_broadcast($notice, $remote=false) +{ + $success = true; + if (!$remote) { + // Make sure we have the OMB stuff + require_once(INSTALLDIR.'/lib/omb.php'); + $success = omb_broadcast_remote_subscribers($notice); + if (!$success) { + common_log(LOG_ERR, 'Error in OMB broadcast for notice ' . $notice->id); + } + } + if ($success) { + require_once(INSTALLDIR.'/lib/jabber.php'); + $success = jabber_broadcast_notice($notice); + if (!$success) { + common_log(LOG_ERR, 'Error in jabber broadcast for notice ' . $notice->id); + } + } + if ($success) { + require_once(INSTALLDIR.'/lib/mail.php'); + $success = mail_broadcast_notice_sms($notice); + if (!$success) { + common_log(LOG_ERR, 'Error in sms broadcast for notice ' . $notice->id); + } + } + if ($success) { + $success = jabber_public_notice($notice); + if (!$success) { + common_log(LOG_ERR, 'Error in public broadcast for notice ' . $notice->id); + } + } + // XXX: broadcast notices to other IM + return $success; } -function common_real_broadcast($notice, $remote=false) { - $success = true; - if (!$remote) { - # Make sure we have the OMB stuff - require_once(INSTALLDIR.'/lib/omb.php'); - $success = omb_broadcast_remote_subscribers($notice); - if (!$success) { - common_log(LOG_ERR, 'Error in OMB broadcast for notice ' . $notice->id); - } - } - if ($success) { - require_once(INSTALLDIR.'/lib/jabber.php'); - $success = jabber_broadcast_notice($notice); - if (!$success) { - common_log(LOG_ERR, 'Error in jabber broadcast for notice ' . $notice->id); - } - } - if ($success) { - require_once(INSTALLDIR.'/lib/mail.php'); - $success = mail_broadcast_notice_sms($notice); - if (!$success) { - common_log(LOG_ERR, 'Error in sms broadcast for notice ' . $notice->id); - } - } - if ($success) { - $success = jabber_public_notice($notice); - if (!$success) { - common_log(LOG_ERR, 'Error in public broadcast for notice ' . $notice->id); - } - } - // XXX: broadcast notices to other IM - return $success; -} - -function common_broadcast_profile($profile) { - // XXX: optionally use a queue system like http://code.google.com/p/microapps/wiki/NQDQ - require_once(INSTALLDIR.'/lib/omb.php'); - omb_broadcast_profile($profile); - // XXX: Other broadcasts...? - return true; -} - -function common_profile_url($nickname) { - return common_local_url('showstream', array('nickname' => $nickname)); -} - -# Don't call if nobody's logged in - -function common_notice_form($action=NULL, $content=NULL) { - $user = common_current_user(); - assert(!is_null($user)); - common_element_start('form', array('id' => 'status_form', - 'method' => 'post', - 'action' => common_local_url('newnotice'))); - common_element_start('p'); - common_element('label', array('for' => 'status_textarea', - 'id' => 'status_label'), - sprintf(_('What\'s up, %s?'), $user->nickname)); +function common_broadcast_profile($profile) +{ + // XXX: optionally use a queue system like http://code.google.com/p/microapps/wiki/NQDQ + require_once(INSTALLDIR.'/lib/omb.php'); + omb_broadcast_profile($profile); + // XXX: Other broadcasts...? + return true; +} + +function common_profile_url($nickname) +{ + return common_local_url('showstream', array('nickname' => $nickname)); +} + +// Don't call if nobody's logged in + +function common_notice_form($action=null, $content=null) +{ + $user = common_current_user(); + assert(!is_null($user)); + common_element_start('form', array('id' => 'status_form', + 'method' => 'post', + 'action' => common_local_url('newnotice'))); + common_element_start('p'); + common_element('label', array('for' => 'status_textarea', + 'id' => 'status_label'), + sprintf(_('What\'s up, %s?'), $user->nickname)); common_element('span', array('id' => 'counter', 'class' => 'counter'), '140'); - common_element('textarea', array('id' => 'status_textarea', - 'cols' => 60, - 'rows' => 3, - 'name' => 'status_textarea'), - ($content) ? $content : ''); - common_hidden('token', common_session_token()); - if ($action) { - common_hidden('returnto', $action); - } - # set by JavaScript - common_hidden('inreplyto', 'false'); - common_element('input', array('id' => 'status_submit', - 'name' => 'status_submit', - 'type' => 'submit', - 'value' => _('Send'))); - common_element_end('p'); - common_element_end('form'); -} - -# Should make up a reasonable root URL - -function common_root_url() { - return common_path(''); -} - -# returns $bytes bytes of random data as a hexadecimal string -# "good" here is a goal and not a guarantee - -function common_good_rand($bytes) { - # XXX: use random.org...? - if (file_exists('/dev/urandom')) { - return common_urandom($bytes); - } else { # FIXME: this is probably not good enough - return common_mtrand($bytes); - } -} - -function common_urandom($bytes) { - $h = fopen('/dev/urandom', 'rb'); - # should not block - $src = fread($h, $bytes); - fclose($h); - $enc = ''; - for ($i = 0; $i < $bytes; $i++) { - $enc .= sprintf("%02x", (ord($src[$i]))); - } - return $enc; -} - -function common_mtrand($bytes) { - $enc = ''; - for ($i = 0; $i < $bytes; $i++) { - $enc .= sprintf("%02x", mt_rand(0, 255)); - } - return $enc; -} - -function common_set_returnto($url) { - common_ensure_session(); - $_SESSION['returnto'] = $url; -} - -function common_get_returnto() { - common_ensure_session(); - return $_SESSION['returnto']; -} - -function common_timestamp() { - return date('YmdHis'); -} - -function common_ensure_syslog() { - static $initialized = false; - if (!$initialized) { - global $config; - openlog($config['syslog']['appname'], 0, LOG_USER); - $initialized = true; - } -} - -function common_log($priority, $msg, $filename=NULL) { - $logfile = common_config('site', 'logfile'); - if ($logfile) { - $log = fopen($logfile, "a"); - if ($log) { - static $syslog_priorities = array('LOG_EMERG', 'LOG_ALERT', 'LOG_CRIT', 'LOG_ERR', - 'LOG_WARNING', 'LOG_NOTICE', 'LOG_INFO', 'LOG_DEBUG'); - $output = date('Y-m-d H:i:s') . ' ' . $syslog_priorities[$priority] . ': ' . $msg . "\n"; - fwrite($log, $output); - fclose($log); - } - } else { - common_ensure_syslog(); - syslog($priority, $msg); - } -} - -function common_debug($msg, $filename=NULL) { - if ($filename) { - common_log(LOG_DEBUG, basename($filename).' - '.$msg); - } else { - common_log(LOG_DEBUG, $msg); - } -} - -function common_log_db_error(&$object, $verb, $filename=NULL) { - $objstr = common_log_objstring($object); - $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError'); - common_log(LOG_ERR, $last_error->message . '(' . $verb . ' on ' . $objstr . ')', $filename); + common_element('textarea', array('id' => 'status_textarea', + 'cols' => 60, + 'rows' => 3, + 'name' => 'status_textarea'), + ($content) ? $content : ''); + common_hidden('token', common_session_token()); + if ($action) { + common_hidden('returnto', $action); + } + // set by JavaScript + common_hidden('inreplyto', 'false'); + common_element('input', array('id' => 'status_submit', + 'name' => 'status_submit', + 'type' => 'submit', + 'value' => _('Send'))); + common_element_end('p'); + common_element_end('form'); } -function common_log_objstring(&$object) { - if (is_null($object)) { - return "NULL"; - } - $arr = $object->toArray(); - $fields = array(); - foreach ($arr as $k => $v) { - $fields[] = "$k='$v'"; - } - $objstring = $object->tableName() . '[' . implode(',', $fields) . ']'; - return $objstring; +// Should make up a reasonable root URL + +function common_root_url() +{ + return common_path(''); } -function common_valid_http_url($url) { - return Validate::uri($url, array('allowed_schemes' => array('http', 'https'))); -} +// returns $bytes bytes of random data as a hexadecimal string +// "good" here is a goal and not a guarantee -function common_valid_tag($tag) { - if (preg_match('/^tag:(.*?),(\d{4}(-\d{2}(-\d{2})?)?):(.*)$/', $tag, $matches)) { - return (Validate::email($matches[1]) || - preg_match('/^([\w-\.]+)$/', $matches[1])); - } - return false; +function common_good_rand($bytes) +{ + // XXX: use random.org...? + if (file_exists('/dev/urandom')) { + return common_urandom($bytes); + } else { // FIXME: this is probably not good enough + return common_mtrand($bytes); + } } -# Does a little before-after block for next/prev page - -function common_pagination($have_before, $have_after, $page, $action, $args=NULL) { +function common_urandom($bytes) +{ + $h = fopen('/dev/urandom', 'rb'); + // should not block + $src = fread($h, $bytes); + fclose($h); + $enc = ''; + for ($i = 0; $i < $bytes; $i++) { + $enc .= sprintf("%02x", (ord($src[$i]))); + } + return $enc; +} + +function common_mtrand($bytes) +{ + $enc = ''; + for ($i = 0; $i < $bytes; $i++) { + $enc .= sprintf("%02x", mt_rand(0, 255)); + } + return $enc; +} + +function common_set_returnto($url) +{ + common_ensure_session(); + $_SESSION['returnto'] = $url; +} + +function common_get_returnto() +{ + common_ensure_session(); + return $_SESSION['returnto']; +} + +function common_timestamp() +{ + return date('YmdHis'); +} + +function common_ensure_syslog() +{ + static $initialized = false; + if (!$initialized) { + global $config; + openlog($config['syslog']['appname'], 0, LOG_USER); + $initialized = true; + } +} - if ($have_before || $have_after) { - common_element_start('div', array('id' => 'pagination')); - common_element_start('ul', array('id' => 'nav_pagination')); - } +function common_log($priority, $msg, $filename=null) +{ + $logfile = common_config('site', 'logfile'); + if ($logfile) { + $log = fopen($logfile, "a"); + if ($log) { + static $syslog_priorities = array('LOG_EMERG', 'LOG_ALERT', 'LOG_CRIT', 'LOG_ERR', + 'LOG_WARNING', 'LOG_NOTICE', 'LOG_INFO', 'LOG_DEBUG'); + $output = date('Y-m-d H:i:s') . ' ' . $syslog_priorities[$priority] . ': ' . $msg . "\n"; + fwrite($log, $output); + fclose($log); + } + } else { + common_ensure_syslog(); + syslog($priority, $msg); + } +} - if ($have_before) { - $pargs = array('page' => $page-1); - $newargs = ($args) ? array_merge($args,$pargs) : $pargs; +function common_debug($msg, $filename=null) +{ + if ($filename) { + common_log(LOG_DEBUG, basename($filename).' - '.$msg); + } else { + common_log(LOG_DEBUG, $msg); + } +} - common_element_start('li', 'before'); - common_element('a', array('href' => common_local_url($action, $newargs), 'rel' => 'prev'), - _('« After')); - common_element_end('li'); - } +function common_log_db_error(&$object, $verb, $filename=null) +{ + $objstr = common_log_objstring($object); + $last_error = &PEAR::getStaticProperty('DB_DataObject','lastError'); + common_log(LOG_ERR, $last_error->message . '(' . $verb . ' on ' . $objstr . ')', $filename); +} - if ($have_after) { - $pargs = array('page' => $page+1); - $newargs = ($args) ? array_merge($args,$pargs) : $pargs; - common_element_start('li', 'after'); - common_element('a', array('href' => common_local_url($action, $newargs), 'rel' => 'next'), - _('Before »')); - common_element_end('li'); - } +function common_log_objstring(&$object) +{ + if (is_null($object)) { + return "null"; + } + $arr = $object->toArray(); + $fields = array(); + foreach ($arr as $k => $v) { + $fields[] = "$k='$v'"; + } + $objstring = $object->tableName() . '[' . implode(',', $fields) . ']'; + return $objstring; +} - if ($have_before || $have_after) { - common_element_end('ul'); - common_element_end('div'); - } +function common_valid_http_url($url) +{ + return Validate::uri($url, array('allowed_schemes' => array('http', 'https'))); +} + +function common_valid_tag($tag) +{ + if (preg_match('/^tag:(.*?),(\d{4}(-\d{2}(-\d{2})?)?):(.*)$/', $tag, $matches)) { + return (Validate::email($matches[1]) || + preg_match('/^([\w-\.]+)$/', $matches[1])); + } + return false; +} + +// Does a little before-after block for next/prev page + +function common_pagination($have_before, $have_after, $page, $action, $args=null) +{ + + if ($have_before || $have_after) { + common_element_start('div', array('id' => 'pagination')); + common_element_start('ul', array('id' => 'nav_pagination')); + } + + if ($have_before) { + $pargs = array('page' => $page-1); + $newargs = ($args) ? array_merge($args,$pargs) : $pargs; + + common_element_start('li', 'before'); + common_element('a', array('href' => common_local_url($action, $newargs), 'rel' => 'prev'), + _('« After')); + common_element_end('li'); + } + + if ($have_after) { + $pargs = array('page' => $page+1); + $newargs = ($args) ? array_merge($args,$pargs) : $pargs; + common_element_start('li', 'after'); + common_element('a', array('href' => common_local_url($action, $newargs), 'rel' => 'next'), + _('Before »')); + common_element_end('li'); + } + + if ($have_before || $have_after) { + common_element_end('ul'); + common_element_end('div'); + } } /* Following functions are copied from MediaWiki GlobalFunctions.php * and written by Evan Prodromou. */ -function common_accept_to_prefs($accept, $def = '*/*') { - # No arg means accept anything (per HTTP spec) - if(!$accept) { - return array($def => 1); - } - - $prefs = array(); - - $parts = explode(',', $accept); - - foreach($parts as $part) { - # FIXME: doesn't deal with params like 'text/html; level=1' - @list($value, $qpart) = explode(';', $part); - $match = array(); - if(!isset($qpart)) { - $prefs[$value] = 1; - } elseif(preg_match('/q\s*=\s*(\d*\.\d+)/', $qpart, $match)) { - $prefs[$value] = $match[1]; - } - } - - return $prefs; -} - -function common_mime_type_match($type, $avail) { - if(array_key_exists($type, $avail)) { - return $type; - } else { - $parts = explode('/', $type); - if(array_key_exists($parts[0] . '/*', $avail)) { - return $parts[0] . '/*'; - } elseif(array_key_exists('*/*', $avail)) { - return '*/*'; - } else { - return NULL; - } - } -} - -function common_negotiate_type($cprefs, $sprefs) { - $combine = array(); - - foreach(array_keys($sprefs) as $type) { - $parts = explode('/', $type); - if($parts[1] != '*') { - $ckey = common_mime_type_match($type, $cprefs); - if($ckey) { - $combine[$type] = $sprefs[$type] * $cprefs[$ckey]; - } - } - } - - foreach(array_keys($cprefs) as $type) { - $parts = explode('/', $type); - if($parts[1] != '*' && !array_key_exists($type, $sprefs)) { - $skey = common_mime_type_match($type, $sprefs); - if($skey) { - $combine[$type] = $sprefs[$skey] * $cprefs[$type]; - } - } - } - - $bestq = 0; - $besttype = "text/html"; - - foreach(array_keys($combine) as $type) { - if($combine[$type] > $bestq) { - $besttype = $type; - $bestq = $combine[$type]; - } - } - - return $besttype; -} - -function common_config($main, $sub) { - global $config; - return isset($config[$main][$sub]) ? $config[$main][$sub] : false; -} - -function common_copy_args($from) { - $to = array(); - $strip = get_magic_quotes_gpc(); - foreach ($from as $k => $v) { - $to[$k] = ($strip) ? stripslashes($v) : $v; - } - return $to; +function common_accept_to_prefs($accept, $def = '*/*') +{ + // No arg means accept anything (per HTTP spec) + if(!$accept) { + return array($def => 1); + } + + $prefs = array(); + + $parts = explode(',', $accept); + + foreach($parts as $part) { + // FIXME: doesn't deal with params like 'text/html; level=1' + @list($value, $qpart) = explode(';', $part); + $match = array(); + if(!isset($qpart)) { + $prefs[$value] = 1; + } elseif(preg_match('/q\s*=\s*(\d*\.\d+)/', $qpart, $match)) { + $prefs[$value] = $match[1]; + } + } + + return $prefs; +} + +function common_mime_type_match($type, $avail) +{ + if(array_key_exists($type, $avail)) { + return $type; + } else { + $parts = explode('/', $type); + if(array_key_exists($parts[0] . '/*', $avail)) { + return $parts[0] . '/*'; + } elseif(array_key_exists('*/*', $avail)) { + return '*/*'; + } else { + return null; + } + } +} + +function common_negotiate_type($cprefs, $sprefs) +{ + $combine = array(); + + foreach(array_keys($sprefs) as $type) { + $parts = explode('/', $type); + if($parts[1] != '*') { + $ckey = common_mime_type_match($type, $cprefs); + if($ckey) { + $combine[$type] = $sprefs[$type] * $cprefs[$ckey]; + } + } + } + + foreach(array_keys($cprefs) as $type) { + $parts = explode('/', $type); + if($parts[1] != '*' && !array_key_exists($type, $sprefs)) { + $skey = common_mime_type_match($type, $sprefs); + if($skey) { + $combine[$type] = $sprefs[$skey] * $cprefs[$type]; + } + } + } + + $bestq = 0; + $besttype = "text/html"; + + foreach(array_keys($combine) as $type) { + if($combine[$type] > $bestq) { + $besttype = $type; + $bestq = $combine[$type]; + } + } + + return $besttype; +} + +function common_config($main, $sub) +{ + global $config; + return isset($config[$main][$sub]) ? $config[$main][$sub] : false; +} + +function common_copy_args($from) +{ + $to = array(); + $strip = get_magic_quotes_gpc(); + foreach ($from as $k => $v) { + $to[$k] = ($strip) ? stripslashes($v) : $v; + } + return $to; } // Neutralise the evil effects of magic_quotes_gpc in the current request. // This is used before handing a request off to OAuthRequest::from_request. -function common_remove_magic_from_request() { - if(get_magic_quotes_gpc()) { - $_POST=array_map('stripslashes',$_POST); - $_GET=array_map('stripslashes',$_GET); - } +function common_remove_magic_from_request() +{ + if(get_magic_quotes_gpc()) { + $_POST=array_map('stripslashes',$_POST); + $_GET=array_map('stripslashes',$_GET); + } } -function common_user_uri(&$user) { - return common_local_url('userbyid', array('id' => $user->id)); +function common_user_uri(&$user) +{ + return common_local_url('userbyid', array('id' => $user->id)); } -function common_notice_uri(&$notice) { - return common_local_url('shownotice', - array('notice' => $notice->id)); +function common_notice_uri(&$notice) +{ + return common_local_url('shownotice', + array('notice' => $notice->id)); } -# 36 alphanums - lookalikes (0, O, 1, I) = 32 chars = 5 bits +// 36 alphanums - lookalikes (0, O, 1, I) = 32 chars = 5 bits -function common_confirmation_code($bits) { - # 36 alphanums - lookalikes (0, O, 1, I) = 32 chars = 5 bits - static $codechars = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ'; - $chars = ceil($bits/5); - $code = ''; - for ($i = 0; $i < $chars; $i++) { - # XXX: convert to string and back - $num = hexdec(common_good_rand(1)); - # XXX: randomness is too precious to throw away almost - # 40% of the bits we get! - $code .= $codechars[$num%32]; - } - return $code; +function common_confirmation_code($bits) +{ + // 36 alphanums - lookalikes (0, O, 1, I) = 32 chars = 5 bits + static $codechars = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ'; + $chars = ceil($bits/5); + $code = ''; + for ($i = 0; $i < $chars; $i++) { + // XXX: convert to string and back + $num = hexdec(common_good_rand(1)); + // XXX: randomness is too precious to throw away almost + // 40% of the bits we get! + $code .= $codechars[$num%32]; + } + return $code; } -# convert markup to HTML +// convert markup to HTML -function common_markup_to_html($c) { - $c = preg_replace('/%%action.(\w+)%%/e', "common_local_url('\\1')", $c); - $c = preg_replace('/%%doc.(\w+)%%/e', "common_local_url('doc', array('title'=>'\\1'))", $c); - $c = preg_replace('/%%(\w+).(\w+)%%/e', 'common_config(\'\\1\', \'\\2\')', $c); - return Markdown($c); +function common_markup_to_html($c) +{ + $c = preg_replace('/%%action.(\w+)%%/e', "common_local_url('\\1')", $c); + $c = preg_replace('/%%doc.(\w+)%%/e', "common_local_url('doc', array('title'=>'\\1'))", $c); + $c = preg_replace('/%%(\w+).(\w+)%%/e', 'common_config(\'\\1\', \'\\2\')', $c); + return Markdown($c); } -function common_profile_avatar_url($profile, $size=AVATAR_PROFILE_SIZE) { - $avatar = $profile->getAvatar($size); - if ($avatar) { - return common_avatar_display_url($avatar); - } else { - return common_default_avatar($size); - } +function common_profile_avatar_url($profile, $size=AVATAR_PROFILE_SIZE) +{ + $avatar = $profile->getAvatar($size); + if ($avatar) { + return common_avatar_display_url($avatar); + } else { + return common_default_avatar($size); + } } -function common_profile_uri($profile) { - if (!$profile) { - return NULL; - } - $user = User::staticGet($profile->id); - if ($user) { - return $user->uri; - } +function common_profile_uri($profile) +{ + if (!$profile) { + return null; + } + $user = User::staticGet($profile->id); + if ($user) { + return $user->uri; + } - $remote = Remote_profile::staticGet($profile->id); - if ($remote) { - return $remote->uri; - } - # XXX: this is a very bad profile! - return NULL; + $remote = Remote_profile::staticGet($profile->id); + if ($remote) { + return $remote->uri; + } + // XXX: this is a very bad profile! + return null; } -function common_canonical_sms($sms) { - # strip non-digits - preg_replace('/\D/', '', $sms); - return $sms; +function common_canonical_sms($sms) +{ + // strip non-digits + preg_replace('/\D/', '', $sms); + return $sms; } -function common_error_handler($errno, $errstr, $errfile, $errline, $errcontext) { +function common_error_handler($errno, $errstr, $errfile, $errline, $errcontext) +{ switch ($errno) { case E_USER_ERROR: - common_log(LOG_ERR, "[$errno] $errstr ($errfile:$errline)"); - exit(1); - break; + common_log(LOG_ERR, "[$errno] $errstr ($errfile:$errline)"); + exit(1); + break; - case E_USER_WARNING: - common_log(LOG_WARNING, "[$errno] $errstr ($errfile:$errline)"); - break; + case E_USER_WARNING: + common_log(LOG_WARNING, "[$errno] $errstr ($errfile:$errline)"); + break; case E_USER_NOTICE: - common_log(LOG_NOTICE, "[$errno] $errstr ($errfile:$errline)"); - break; + common_log(LOG_NOTICE, "[$errno] $errstr ($errfile:$errline)"); + break; } - # FIXME: show error page if we're on the Web + // FIXME: show error page if we're on the Web /* Don't execute PHP internal error handler */ return true; } -function common_session_token() { - common_ensure_session(); - if (!array_key_exists('token', $_SESSION)) { - $_SESSION['token'] = common_good_rand(64); - } - return $_SESSION['token']; -} - -function common_disfavor_form($notice) { - common_element_start('form', array('id' => 'disfavor-' . $notice->id, - 'method' => 'post', - 'class' => 'disfavor', - 'action' => common_local_url('disfavor'))); - - common_element('input', array('type' => 'hidden', - 'name' => 'token-'. $notice->id, - 'id' => 'token-'. $notice->id, - 'class' => 'token', - 'value' => common_session_token())); - - common_element('input', array('type' => 'hidden', - 'name' => 'notice', - 'id' => 'notice-n'. $notice->id, - 'class' => 'notice', - 'value' => $notice->id)); - - common_element('input', array('type' => 'submit', - 'id' => 'disfavor-submit-' . $notice->id, - 'name' => 'disfavor-submit-' . $notice->id, - 'class' => 'disfavor', - 'value' => 'Disfavor favorite', - 'title' => 'Remove this message from favorites')); - common_element_end('form'); -} - -function common_favor_form($notice) { - common_element_start('form', array('id' => 'favor-' . $notice->id, - 'method' => 'post', - 'class' => 'favor', - 'action' => common_local_url('favor'))); - - common_element('input', array('type' => 'hidden', - 'name' => 'token-'. $notice->id, - 'id' => 'token-'. $notice->id, - 'class' => 'token', - 'value' => common_session_token())); - - common_element('input', array('type' => 'hidden', - 'name' => 'notice', - 'id' => 'notice-n'. $notice->id, - 'class' => 'notice', - 'value' => $notice->id)); - - common_element('input', array('type' => 'submit', - 'id' => 'favor-submit-' . $notice->id, - 'name' => 'favor-submit-' . $notice->id, - 'class' => 'favor', - 'value' => 'Add to favorites', - 'title' => 'Add this message to favorites')); - common_element_end('form'); -} - -function common_nudge_form($profile) { - common_element_start('form', array('id' => 'nudge', 'method' => 'post', - 'action' => common_local_url('nudge', array('nickname' => $profile->nickname)))); - common_hidden('token', common_session_token()); - common_element('input', array('type' => 'submit', - 'class' => 'submit', - 'value' => _('Send a nudge'))); - common_element_end('form'); -} -function common_nudge_response() { - common_element('p', array('id' => 'nudge_response'), _('Nudge sent!')); -} - -function common_subscribe_form($profile) { - common_element_start('form', array('id' => 'subscribe-' . $profile->id, - 'method' => 'post', - 'class' => 'subscribe', - 'action' => common_local_url('subscribe'))); - common_hidden('token', common_session_token()); - common_element('input', array('id' => 'subscribeto-' . $profile->id, - 'name' => 'subscribeto', - 'type' => 'hidden', - 'value' => $profile->id)); - common_element('input', array('type' => 'submit', - 'class' => 'submit', - 'value' => _('Subscribe'))); - common_element_end('form'); -} - -function common_unsubscribe_form($profile) { - common_element_start('form', array('id' => 'unsubscribe-' . $profile->id, - 'method' => 'post', - 'class' => 'unsubscribe', - 'action' => common_local_url('unsubscribe'))); - common_hidden('token', common_session_token()); - common_element('input', array('id' => 'unsubscribeto-' . $profile->id, - 'name' => 'unsubscribeto', - 'type' => 'hidden', - 'value' => $profile->id)); - common_element('input', array('type' => 'submit', - 'class' => 'submit', - 'value' => _('Unsubscribe'))); - common_element_end('form'); +function common_session_token() +{ + common_ensure_session(); + if (!array_key_exists('token', $_SESSION)) { + $_SESSION['token'] = common_good_rand(64); + } + return $_SESSION['token']; +} + +function common_disfavor_form($notice) +{ + common_element_start('form', array('id' => 'disfavor-' . $notice->id, + 'method' => 'post', + 'class' => 'disfavor', + 'action' => common_local_url('disfavor'))); + + common_element('input', array('type' => 'hidden', + 'name' => 'token-'. $notice->id, + 'id' => 'token-'. $notice->id, + 'class' => 'token', + 'value' => common_session_token())); + + common_element('input', array('type' => 'hidden', + 'name' => 'notice', + 'id' => 'notice-n'. $notice->id, + 'class' => 'notice', + 'value' => $notice->id)); + + common_element('input', array('type' => 'submit', + 'id' => 'disfavor-submit-' . $notice->id, + 'name' => 'disfavor-submit-' . $notice->id, + 'class' => 'disfavor', + 'value' => 'Disfavor favorite', + 'title' => 'Remove this message from favorites')); + common_element_end('form'); +} + +function common_favor_form($notice) +{ + common_element_start('form', array('id' => 'favor-' . $notice->id, + 'method' => 'post', + 'class' => 'favor', + 'action' => common_local_url('favor'))); + + common_element('input', array('type' => 'hidden', + 'name' => 'token-'. $notice->id, + 'id' => 'token-'. $notice->id, + 'class' => 'token', + 'value' => common_session_token())); + + common_element('input', array('type' => 'hidden', + 'name' => 'notice', + 'id' => 'notice-n'. $notice->id, + 'class' => 'notice', + 'value' => $notice->id)); + + common_element('input', array('type' => 'submit', + 'id' => 'favor-submit-' . $notice->id, + 'name' => 'favor-submit-' . $notice->id, + 'class' => 'favor', + 'value' => 'Add to favorites', + 'title' => 'Add this message to favorites')); + common_element_end('form'); +} + +function common_nudge_form($profile) +{ + common_element_start('form', array('id' => 'nudge', 'method' => 'post', + 'action' => common_local_url('nudge', array('nickname' => $profile->nickname)))); + common_hidden('token', common_session_token()); + common_element('input', array('type' => 'submit', + 'class' => 'submit', + 'value' => _('Send a nudge'))); + common_element_end('form'); +} +function common_nudge_response() +{ + common_element('p', array('id' => 'nudge_response'), _('Nudge sent!')); +} + +function common_subscribe_form($profile) +{ + common_element_start('form', array('id' => 'subscribe-' . $profile->id, + 'method' => 'post', + 'class' => 'subscribe', + 'action' => common_local_url('subscribe'))); + common_hidden('token', common_session_token()); + common_element('input', array('id' => 'subscribeto-' . $profile->id, + 'name' => 'subscribeto', + 'type' => 'hidden', + 'value' => $profile->id)); + common_element('input', array('type' => 'submit', + 'class' => 'submit', + 'value' => _('Subscribe'))); + common_element_end('form'); +} + +function common_unsubscribe_form($profile) +{ + common_element_start('form', array('id' => 'unsubscribe-' . $profile->id, + 'method' => 'post', + 'class' => 'unsubscribe', + 'action' => common_local_url('unsubscribe'))); + common_hidden('token', common_session_token()); + common_element('input', array('id' => 'unsubscribeto-' . $profile->id, + 'name' => 'unsubscribeto', + 'type' => 'hidden', + 'value' => $profile->id)); + common_element('input', array('type' => 'submit', + 'class' => 'submit', + 'value' => _('Unsubscribe'))); + common_element_end('form'); } // XXX: Refactor this code -function common_profile_new_message_nudge ($cur, $profile) { - $user = User::staticGet('id', $profile->id); +function common_profile_new_message_nudge ($cur, $profile) +{ + $user = User::staticGet('id', $profile->id); - if ($cur && $cur->id != $user->id && $cur->mutuallySubscribed($user)) { + if ($cur && $cur->id != $user->id && $cur->mutuallySubscribed($user)) { common_element_start('li', array('id' => 'profile_send_a_new_message')); - common_element('a', array('href' => common_local_url('newmessage', array('to' => $user->id))), - _('Send a message')); + common_element('a', array('href' => common_local_url('newmessage', array('to' => $user->id))), + _('Send a message')); common_element_end('li'); - if ($user->email && $user->emailnotifynudge) { + if ($user->email && $user->emailnotifynudge) { common_element_start('li', array('id' => 'profile_nudge')); common_nudge_form($user); common_element_end('li'); } - } + } } -function common_cache_key($extra) { - return 'laconica:' . common_keyize(common_config('site', 'name')) . ':' . $extra; +function common_cache_key($extra) +{ + return 'laconica:' . common_keyize(common_config('site', 'name')) . ':' . $extra; } -function common_keyize($str) { - $str = strtolower($str); - $str = preg_replace('/\s/', '_', $str); - return $str; +function common_keyize($str) +{ + $str = strtolower($str); + $str = preg_replace('/\s/', '_', $str); + return $str; } -function common_message_form($content, $user, $to) { +function common_message_form($content, $user, $to) +{ - common_element_start('form', array('id' => 'message_form', - 'method' => 'post', - 'action' => common_local_url('newmessage'))); + common_element_start('form', array('id' => 'message_form', + 'method' => 'post', + 'action' => common_local_url('newmessage'))); - $mutual_users = $user->mutuallySubscribedUsers(); + $mutual_users = $user->mutuallySubscribedUsers(); - $mutual = array(); + $mutual = array(); - while ($mutual_users->fetch()) { - if ($mutual_users->id != $user->id) { - $mutual[$mutual_users->id] = $mutual_users->nickname; - } - } + while ($mutual_users->fetch()) { + if ($mutual_users->id != $user->id) { + $mutual[$mutual_users->id] = $mutual_users->nickname; + } + } - $mutual_users->free(); - unset($mutual_users); + $mutual_users->free(); + unset($mutual_users); - common_dropdown('to', _('To'), $mutual, NULL, FALSE, $to->id); + common_dropdown('to', _('To'), $mutual, null, false, $to->id); - common_element_start('p'); + common_element_start('p'); - common_element('textarea', array('id' => 'message_content', - 'cols' => 60, - 'rows' => 3, - 'name' => 'content'), - ($content) ? $content : ''); + common_element('textarea', array('id' => 'message_content', + 'cols' => 60, + 'rows' => 3, + 'name' => 'content'), + ($content) ? $content : ''); - common_element('input', array('id' => 'message_send', - 'name' => 'message_send', - 'type' => 'submit', - 'value' => _('Send'))); + common_element('input', array('id' => 'message_send', + 'name' => 'message_send', + 'type' => 'submit', + 'value' => _('Send'))); - common_hidden('token', common_session_token()); + common_hidden('token', common_session_token()); - common_element_end('p'); - common_element_end('form'); + common_element_end('p'); + common_element_end('form'); } -function common_memcache() { - static $cache = NULL; - if (!common_config('memcached', 'enabled')) { - return NULL; - } else { - if (!$cache) { - $cache = new Memcache(); - $servers = common_config('memcached', 'server'); - if (is_array($servers)) { - foreach($servers as $server) { - $cache->addServer($server); - } - } else { - $cache->addServer($servers); - } - } - return $cache; - } +function common_memcache() +{ + static $cache = null; + if (!common_config('memcached', 'enabled')) { + return null; + } else { + if (!$cache) { + $cache = new Memcache(); + $servers = common_config('memcached', 'server'); + if (is_array($servers)) { + foreach($servers as $server) { + $cache->addServer($server); + } + } else { + $cache->addServer($servers); + } + } + return $cache; + } } -function common_compatible_license($from, $to) { - # XXX: better compatibility check needed here! - return ($from == $to); +function common_compatible_license($from, $to) +{ + // XXX: better compatibility check needed here! + return ($from == $to); } /* These are almost identical, so we use a helper function */ -function common_block_form($profile, $args=NULL) { +function common_block_form($profile, $args=null) +{ common_blocking_form('block', _('Block'), $profile, $args); } -function common_unblock_form($profile, $args=NULL) { +function common_unblock_form($profile, $args=null) +{ common_blocking_form('unblock', _('Unblock'), $profile, $args); } -function common_blocking_form($type, $label, $profile, $args=NULL) { +function common_blocking_form($type, $label, $profile, $args=null) +{ common_element_start('form', array('id' => $type . '-' . $profile->id, 'method' => 'post', 'class' => $type, @@ -2220,4 +2344,3 @@ function common_blocking_form($type, $label, $profile, $args=NULL) { common_element_end('form'); return; } - diff --git a/lib/xmppqueuehandler.php b/lib/xmppqueuehandler.php index cfc9642e4..91015fd45 100644 --- a/lib/xmppqueuehandler.php +++ b/lib/xmppqueuehandler.php @@ -28,64 +28,71 @@ require_once(INSTALLDIR.'/lib/queuehandler.php'); * superclass. */ -class XmppQueueHandler extends QueueHandler { - - function start() { - # Low priority; we don't want to receive messages - $this->log(LOG_INFO, "INITIALIZE"); - $this->conn = jabber_connect($this->_id); - if ($this->conn) { - $this->conn->addEventHandler('message', 'forward_message', $this); - $this->conn->addEventHandler('reconnect', 'handle_reconnect', $this); - $this->conn->setReconnectTimeout(600); - jabber_send_presence("Send me a message to post a notice", 'available', NULL, 'available', -1); - } - return !is_null($this->conn); - } - - function handle_reconnect(&$pl) { - $this->conn->processUntil('session_start'); - $this->conn->presence(NULL, 'available', NULL, 'available', -1); - } +class XmppQueueHandler extends QueueHandler +{ + + function start() + { + # Low priority; we don't want to receive messages + $this->log(LOG_INFO, "INITIALIZE"); + $this->conn = jabber_connect($this->_id); + if ($this->conn) { + $this->conn->addEventHandler('message', 'forward_message', $this); + $this->conn->addEventHandler('reconnect', 'handle_reconnect', $this); + $this->conn->setReconnectTimeout(600); + jabber_send_presence("Send me a message to post a notice", 'available', null, 'available', -1); + } + return !is_null($this->conn); + } + + function handle_reconnect(&$pl) + { + $this->conn->processUntil('session_start'); + $this->conn->presence(null, 'available', null, 'available', -1); + } - function idle($timeout=0) { - # Process the queue for as long as needed - try { - if ($this->conn) { - $this->conn->processTime($timeout); - } - } catch (XMPPHP_Exception $e) { - $this->log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage()); - die($e->getMessage()); - } - } - - function forward_message(&$pl) { - if ($pl['type'] != 'chat') { - $this->log(LOG_DEBUG, 'Ignoring message of type ' . $pl['type'] . ' from ' . $pl['from']); - return; - } - $listener = $this->listener(); - if (strtolower($listener) == strtolower($pl['from'])) { - $this->log(LOG_WARNING, 'Ignoring loop message.'); - return; - } - $this->log(LOG_INFO, 'Forwarding message from ' . $pl['from'] . ' to ' . $listener); - $this->conn->message($this->listener(), $pl['body'], 'chat', NULL, $this->ofrom($pl['from'])); - } + function idle($timeout=0) + { + # Process the queue for as long as needed + try { + if ($this->conn) { + $this->conn->processTime($timeout); + } + } catch (XMPPHP_Exception $e) { + $this->log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage()); + die($e->getMessage()); + } + } + + function forward_message(&$pl) + { + if ($pl['type'] != 'chat') { + $this->log(LOG_DEBUG, 'Ignoring message of type ' . $pl['type'] . ' from ' . $pl['from']); + return; + } + $listener = $this->listener(); + if (strtolower($listener) == strtolower($pl['from'])) { + $this->log(LOG_WARNING, 'Ignoring loop message.'); + return; + } + $this->log(LOG_INFO, 'Forwarding message from ' . $pl['from'] . ' to ' . $listener); + $this->conn->message($this->listener(), $pl['body'], 'chat', null, $this->ofrom($pl['from'])); + } - function ofrom($from) { - $address = "<addresses xmlns='http://jabber.org/protocol/address'>\n"; - $address .= "<address type='ofrom' jid='$from' />\n"; - $address .= "</addresses>\n"; - return $address; - } + function ofrom($from) + { + $address = "<addresses xmlns='http://jabber.org/protocol/address'>\n"; + $address .= "<address type='ofrom' jid='$from' />\n"; + $address .= "</addresses>\n"; + return $address; + } - function listener() { - if (common_config('xmpp', 'listener')) { - return common_config('xmpp', 'listener'); - } else { - return jabber_daemon_address() . '/' . common_config('xmpp','resource') . '-listener'; - } - } + function listener() + { + if (common_config('xmpp', 'listener')) { + return common_config('xmpp', 'listener'); + } else { + return jabber_daemon_address() . '/' . common_config('xmpp','resource') . '-listener'; + } + } } diff --git a/scripts/enjitqueuehandler.php b/scripts/enjitqueuehandler.php index 8538ae09a..3a4f8315d 100755 --- a/scripts/enjitqueuehandler.php +++ b/scripts/enjitqueuehandler.php @@ -20,8 +20,8 @@ # Abort if called from a web server if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { - print "This script must be run from the command line\n"; - exit(); + print "This script must be run from the command line\n"; + exit(); } define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); @@ -33,21 +33,25 @@ require_once(INSTALLDIR . '/lib/queuehandler.php'); set_error_handler('common_error_handler'); -class EnjitQueueHandler extends QueueHandler { - - function transport() { - return 'enjit'; - } +class EnjitQueueHandler extends QueueHandler +{ + + function transport() + { + return 'enjit'; + } - function start() { + function start() + { $this->log(LOG_INFO, "Starting EnjitQueueHandler"); $this->log(LOG_INFO, "Broadcasting to ".common_config('enjit', 'apiurl')); - return true; - } + return true; + } - function handle_notice($notice) { + function handle_notice($notice) + { - $profile = Profile::staticGet($notice->profile_id); + $profile = Profile::staticGet($notice->profile_id); $this->log(LOG_INFO, "Posting Notice ".$notice->id." from ".$profile->nickname); @@ -60,25 +64,25 @@ class EnjitQueueHandler extends QueueHandler { # # Build an Atom message from the notice # - $noticeurl = common_local_url('shownotice', array('notice' => $notice->id)); - $msg = $profile->nickname . ': ' . $notice->content; - - $atom = "<entry xmlns='http://www.w3.org/2005/Atom'>\n"; - $atom .= "<apisource>".common_config('enjit','source')."</apisource>\n"; - $atom .= "<source>\n"; - $atom .= "<title>" . $profile->nickname . " - " . common_config('site', 'name') . "</title>\n"; - $atom .= "<link href='" . $profile->profileurl . "'/>\n"; - $atom .= "<link rel='self' type='application/rss+xml' href='" . common_local_url('userrss', array('nickname' => $profile->nickname)) . "'/>\n"; - $atom .= "<author><name>" . $profile->nickname . "</name></author>\n"; - $atom .= "<icon>" . common_profile_avatar_url($profile, AVATAR_PROFILE_SIZE) . "</icon>\n"; - $atom .= "</source>\n"; - $atom .= "<title>" . htmlspecialchars($msg) . "</title>\n"; - $atom .= "<summary>" . htmlspecialchars($msg) . "</summary>\n"; - $atom .= "<link rel='alternate' href='" . $noticeurl . "' />\n"; - $atom .= "<id>". $notice->uri . "</id>\n"; - $atom .= "<published>".common_date_w3dtf($notice->created)."</published>\n"; - $atom .= "<updated>".common_date_w3dtf($notice->modified)."</updated>\n"; - $atom .= "</entry>\n"; + $noticeurl = common_local_url('shownotice', array('notice' => $notice->id)); + $msg = $profile->nickname . ': ' . $notice->content; + + $atom = "<entry xmlns='http://www.w3.org/2005/Atom'>\n"; + $atom .= "<apisource>".common_config('enjit','source')."</apisource>\n"; + $atom .= "<source>\n"; + $atom .= "<title>" . $profile->nickname . " - " . common_config('site', 'name') . "</title>\n"; + $atom .= "<link href='" . $profile->profileurl . "'/>\n"; + $atom .= "<link rel='self' type='application/rss+xml' href='" . common_local_url('userrss', array('nickname' => $profile->nickname)) . "'/>\n"; + $atom .= "<author><name>" . $profile->nickname . "</name></author>\n"; + $atom .= "<icon>" . common_profile_avatar_url($profile, AVATAR_PROFILE_SIZE) . "</icon>\n"; + $atom .= "</source>\n"; + $atom .= "<title>" . htmlspecialchars($msg) . "</title>\n"; + $atom .= "<summary>" . htmlspecialchars($msg) . "</summary>\n"; + $atom .= "<link rel='alternate' href='" . $noticeurl . "' />\n"; + $atom .= "<id>". $notice->uri . "</id>\n"; + $atom .= "<published>".common_date_w3dtf($notice->created)."</published>\n"; + $atom .= "<updated>".common_date_w3dtf($notice->modified)."</updated>\n"; + $atom .= "</entry>\n"; $url = common_config('enjit', 'apiurl') . "/submit/". common_config('enjit','apikey'); $data = "msg=$atom"; @@ -86,43 +90,43 @@ class EnjitQueueHandler extends QueueHandler { # # POST the message to $config['enjit']['apiurl'] # - $ch = curl_init(); + $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); + curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HEADER, 1); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_POST, 1) ; - curl_setopt($ch, CURLOPT_POSTFIELDS, $data); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); + curl_setopt($ch, CURLOPT_POST, 1) ; + curl_setopt($ch, CURLOPT_POSTFIELDS, $data); # SSL and Debugging options # - # curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); - # curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); + # curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); + # curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); # curl_setopt($ch, CURLOPT_VERBOSE, 1); - $result = curl_exec($ch); + $result = curl_exec($ch); - $code = curl_getinfo($ch, CURLINFO_HTTP_CODE ); + $code = curl_getinfo($ch, CURLINFO_HTTP_CODE ); $this->log(LOG_INFO, "Response Code: $code"); - curl_close($ch); + curl_close($ch); return $code; - } - + } + } mb_internal_encoding('UTF-8'); -$id = ($argc > 1) ? $argv[1] : NULL; +$id = ($argc > 1) ? $argv[1] : null; $handler = new EnjitQueueHandler($id); if ($handler->start()) { - $handler->handle_queue(); + $handler->handle_queue(); } $handler->finish(); diff --git a/scripts/fixup_hashtags.php b/scripts/fixup_hashtags.php index 88f385798..6f65c78a1 100755 --- a/scripts/fixup_hashtags.php +++ b/scripts/fixup_hashtags.php @@ -37,10 +37,10 @@ $cnt = $notice->find(); while ($notice->fetch()) { common_log(LOG_INFO, 'Getting tags for notice #' . $notice->id); $notice->saveTags(); - $original = clone($notice); - $notice->rendered = common_render_content($notice->content, $notice); - $result = $notice->update($original); - if (!$result) { - common_log_db_error($notice, 'UPDATE', __FILE__); - } + $original = clone($notice); + $notice->rendered = common_render_content($notice->content, $notice); + $result = $notice->update($original); + if (!$result) { + common_log_db_error($notice, 'UPDATE', __FILE__); + } } diff --git a/scripts/fixup_inboxes.php b/scripts/fixup_inboxes.php index 1715b0bc1..a5c8a0a5a 100755 --- a/scripts/fixup_inboxes.php +++ b/scripts/fixup_inboxes.php @@ -34,14 +34,14 @@ define('LACONICA', true); require_once(INSTALLDIR . '/lib/common.php'); -$start_at = ($argc > 1) ? $argv[1] : NULL; +$start_at = ($argc > 1) ? $argv[1] : null; common_log(LOG_INFO, 'Updating user inboxes.'); $user = new User(); if ($start_at) { - $user->whereAdd('id >= ' . $start_at); + $user->whereAdd('id >= ' . $start_at); } $cnt = $user->find(); @@ -49,32 +49,32 @@ $cache = common_memcache(); while ($user->fetch()) { common_log(LOG_INFO, 'Updating inbox for user ' . $user->id); - $user->query('BEGIN'); - $inbox = new Notice_inbox(); - $result = $inbox->query('INSERT LOW_PRIORITY INTO notice_inbox (user_id, notice_id, created) ' . - 'SELECT ' . $user->id . ', notice.id, notice.created ' . - 'FROM subscription JOIN notice ON subscription.subscribed = notice.profile_id ' . - 'WHERE subscription.subscriber = ' . $user->id . ' ' . - 'AND notice.created >= subscription.created ' . - 'AND NOT EXISTS (SELECT user_id, notice_id ' . - 'FROM notice_inbox ' . - 'WHERE user_id = ' . $user->id . ' ' . - 'AND notice_id = notice.id)'); - if (is_null($result) || $result === false) { - common_log_db_error($inbox, 'INSERT', __FILE__); - continue; - } - $orig = clone($user); - $user->inboxed = 1; - $result = $user->update($orig); - if (!$result) { - common_log_db_error($user, 'UPDATE', __FILE__); - continue; - } - $user->query('COMMIT'); - $inbox->free(); - unset($inbox); - if ($cache) { - $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id)); - } + $user->query('BEGIN'); + $inbox = new Notice_inbox(); + $result = $inbox->query('INSERT LOW_PRIORITY INTO notice_inbox (user_id, notice_id, created) ' . + 'SELECT ' . $user->id . ', notice.id, notice.created ' . + 'FROM subscription JOIN notice ON subscription.subscribed = notice.profile_id ' . + 'WHERE subscription.subscriber = ' . $user->id . ' ' . + 'AND notice.created >= subscription.created ' . + 'AND NOT EXISTS (SELECT user_id, notice_id ' . + 'FROM notice_inbox ' . + 'WHERE user_id = ' . $user->id . ' ' . + 'AND notice_id = notice.id)'); + if (is_null($result) || $result === false) { + common_log_db_error($inbox, 'INSERT', __FILE__); + continue; + } + $orig = clone($user); + $user->inboxed = 1; + $result = $user->update($orig); + if (!$result) { + common_log_db_error($user, 'UPDATE', __FILE__); + continue; + } + $user->query('COMMIT'); + $inbox->free(); + unset($inbox); + if ($cache) { + $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id)); + } } diff --git a/scripts/fixup_notices_rendered.php b/scripts/fixup_notices_rendered.php index c6c925729..c27185546 100755 --- a/scripts/fixup_notices_rendered.php +++ b/scripts/fixup_notices_rendered.php @@ -20,8 +20,8 @@ # Abort if called from a web server if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { - print "This script must be run from the command line\n"; - exit(); + print "This script must be run from the command line\n"; + exit(); } define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); @@ -31,20 +31,20 @@ require_once(INSTALLDIR . '/lib/common.php'); common_log(LOG_INFO, 'Starting to render old notices.'); -$start_at = ($argc > 1) ? $argv[1] : NULL; +$start_at = ($argc > 1) ? $argv[1] : null; $notice = new Notice(); if ($start_at) { - $notice->whereAdd('id >= ' . $start_at); + $notice->whereAdd('id >= ' . $start_at); } $cnt = $notice->find(); while ($notice->fetch()) { - common_log(LOG_INFO, 'Pre-rendering notice #' . $notice->id); - $original = clone($notice); - $notice->rendered = common_render_content($notice->content, $notice); - $result = $notice->update($original); - if (!$result) { - common_log_db_error($notice, 'UPDATE', __FILE__); - } + common_log(LOG_INFO, 'Pre-rendering notice #' . $notice->id); + $original = clone($notice); + $notice->rendered = common_render_content($notice->content, $notice); + $result = $notice->update($original); + if (!$result) { + common_log_db_error($notice, 'UPDATE', __FILE__); + } } diff --git a/scripts/getpiddir.php b/scripts/getpiddir.php index b4dda2254..4f5704249 100755 --- a/scripts/getpiddir.php +++ b/scripts/getpiddir.php @@ -20,8 +20,8 @@ # Abort if called from a web server if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { - print "This script must be run from the command line\n"; - exit(); + print "This script must be run from the command line\n"; + exit(); } define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); diff --git a/scripts/inbox_users.php b/scripts/inbox_users.php index 0543abb2a..f907e1584 100755 --- a/scripts/inbox_users.php +++ b/scripts/inbox_users.php @@ -42,68 +42,68 @@ common_log(LOG_INFO, 'Updating user inboxes.'); $ids = file($id_file); foreach ($ids as $id) { - - $user = User::staticGet('id', $id); + + $user = User::staticGet('id', $id); - if (!$user) { - common_log(LOG_WARNING, 'No such user: ' . $id); - continue; - } - - if ($user->inboxed) { - common_log(LOG_WARNING, 'Already inboxed: ' . $id); - continue; - } - + if (!$user) { + common_log(LOG_WARNING, 'No such user: ' . $id); + continue; + } + + if ($user->inboxed) { + common_log(LOG_WARNING, 'Already inboxed: ' . $id); + continue; + } + common_log(LOG_INFO, 'Updating inbox for user ' . $user->id); - - $user->query('BEGIN'); - - $old_inbox = new Notice_inbox(); - $old_inbox->user_id = $user->id; - - $result = $old_inbox->delete(); - - if (is_null($result) || $result === false) { - common_log_db_error($old_inbox, 'DELETE', __FILE__); - continue; - } + + $user->query('BEGIN'); + + $old_inbox = new Notice_inbox(); + $old_inbox->user_id = $user->id; + + $result = $old_inbox->delete(); + + if (is_null($result) || $result === false) { + common_log_db_error($old_inbox, 'DELETE', __FILE__); + continue; + } - $old_inbox->free(); - - $inbox = new Notice_inbox(); - - $result = $inbox->query('INSERT INTO notice_inbox (user_id, notice_id, created) ' . - 'SELECT ' . $user->id . ', notice.id, notice.created ' . - 'FROM subscription JOIN notice ON subscription.subscribed = notice.profile_id ' . - 'WHERE subscription.subscriber = ' . $user->id . ' ' . - 'AND notice.created >= subscription.created ' . - 'AND now() - notice.created < ' . (7 * 24 * 3600) . ' ' . - 'AND NOT EXISTS (SELECT user_id, notice_id ' . - 'FROM notice_inbox ' . - 'WHERE user_id = ' . $user->id . ' ' . - 'AND notice_id = notice.id)'); - - if (is_null($result) || $result === false) { - common_log_db_error($inbox, 'INSERT', __FILE__); - continue; - } - - $orig = clone($user); - $user->inboxed = 1; - $result = $user->update($orig); - - if (!$result) { - common_log_db_error($user, 'UPDATE', __FILE__); - continue; - } - - $user->query('COMMIT'); - - $inbox->free(); - unset($inbox); - - if ($cache) { - $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id)); - } + $old_inbox->free(); + + $inbox = new Notice_inbox(); + + $result = $inbox->query('INSERT INTO notice_inbox (user_id, notice_id, created) ' . + 'SELECT ' . $user->id . ', notice.id, notice.created ' . + 'FROM subscription JOIN notice ON subscription.subscribed = notice.profile_id ' . + 'WHERE subscription.subscriber = ' . $user->id . ' ' . + 'AND notice.created >= subscription.created ' . + 'AND now() - notice.created < ' . (7 * 24 * 3600) . ' ' . + 'AND NOT EXISTS (SELECT user_id, notice_id ' . + 'FROM notice_inbox ' . + 'WHERE user_id = ' . $user->id . ' ' . + 'AND notice_id = notice.id)'); + + if (is_null($result) || $result === false) { + common_log_db_error($inbox, 'INSERT', __FILE__); + continue; + } + + $orig = clone($user); + $user->inboxed = 1; + $result = $user->update($orig); + + if (!$result) { + common_log_db_error($user, 'UPDATE', __FILE__); + continue; + } + + $user->query('COMMIT'); + + $inbox->free(); + unset($inbox); + + if ($cache) { + $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id)); + } } diff --git a/scripts/jabberqueuehandler.php b/scripts/jabberqueuehandler.php index 59cdb94ad..924fc4545 100755 --- a/scripts/jabberqueuehandler.php +++ b/scripts/jabberqueuehandler.php @@ -20,8 +20,8 @@ # Abort if called from a web server if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { - print "This script must be run from the command line\n"; - exit(); + print "This script must be run from the command line\n"; + exit(); } define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); @@ -33,22 +33,25 @@ require_once(INSTALLDIR . '/lib/xmppqueuehandler.php'); set_error_handler('common_error_handler'); -class JabberQueueHandler extends XmppQueueHandler { +class JabberQueueHandler extends XmppQueueHandler +{ - var $conn = NULL; + var $conn = null; - function transport() { - return 'jabber'; - } + function transport() + { + return 'jabber'; + } - function handle_notice($notice) { - try { - return jabber_broadcast_notice($notice); - } catch (XMPPHP_Exception $e) { - $this->log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage()); - exit(1); - } - } + function handle_notice($notice) + { + try { + return jabber_broadcast_notice($notice); + } catch (XMPPHP_Exception $e) { + $this->log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage()); + exit(1); + } + } } ini_set("max_execution_time", "0"); diff --git a/scripts/maildaemon.php b/scripts/maildaemon.php index 8b809f646..b9facec1a 100755 --- a/scripts/maildaemon.php +++ b/scripts/maildaemon.php @@ -20,8 +20,8 @@ # Abort if called from a web server if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { - print "This script must be run from the command line\n"; - exit(); + print "This script must be run from the command line\n"; + exit(); } define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); @@ -34,181 +34,194 @@ require_once('Mail/mimeDecode.php'); # FIXME: we use both Mail_mimeDecode and mailparse # Need to move everything to mailparse -class MailerDaemon { - - function __construct() { - } - - function handle_message($fname='php://stdin') { - list($from, $to, $msg) = $this->parse_message($fname); - if (!$from || !$to || !$msg) { - $this->error(NULL, _('Could not parse message.')); - } - common_log(LOG_INFO, "Mail from $from to $to: " .substr($msg, 0, 20)); - $user = $this->user_from($from); - if (!$user) { - $this->error($from, _('Not a registered user.')); - return false; - } - if (!$this->user_match_to($user, $to)) { - $this->error($from, _('Sorry, that is not your incoming email address.')); - return false; - } - if (!$user->emailpost) { - $this->error($from, _('Sorry, no incoming email allowed.')); - return false; - } - $response = $this->handle_command($user, $from, $msg); - if ($response) { - return true; - } - $msg = $this->cleanup_msg($msg); - $this->add_notice($user, $msg); - } - - function error($from, $msg) { - file_put_contents("php://stderr", $msg . "\n"); - exit(1); - } - - function user_from($from_hdr) { - $froms = mailparse_rfc822_parse_addresses($from_hdr); - if (!$froms) { - return NULL; - } - $from = $froms[0]; - $addr = common_canonical_email($from['address']); - $user = User::staticGet('email', $addr); - if (!$user) { - $user = User::staticGet('smsemail', $addr); - } - return $user; - } - - function user_match_to($user, $to_hdr) { - $incoming = $user->incomingemail; - $tos = mailparse_rfc822_parse_addresses($to_hdr); - foreach ($tos as $to) { - if (strcasecmp($incoming, $to['address']) == 0) { - return true; - } - } - return false; - } - - function handle_command($user, $from, $msg) { - $inter = new CommandInterpreter(); - $cmd = $inter->handle_command($user, $msg); - if ($cmd) { - $cmd->execute(new MailChannel($from)); - return true; - } - return false; - } - - function respond($from, $to, $response) { - - $headers['From'] = $to; - $headers['To'] = $from; - $headers['Subject'] = "Command complete"; - - return mail_send(array($from), $headers, $response); - } - - function log($level, $msg) { - common_log($level, 'MailDaemon: '.$msg); - } - - function add_notice($user, $msg) { +class MailerDaemon +{ + + function __construct() + { + } + + function handle_message($fname='php://stdin') + { + list($from, $to, $msg) = $this->parse_message($fname); + if (!$from || !$to || !$msg) { + $this->error(null, _('Could not parse message.')); + } + common_log(LOG_INFO, "Mail from $from to $to: " .substr($msg, 0, 20)); + $user = $this->user_from($from); + if (!$user) { + $this->error($from, _('Not a registered user.')); + return false; + } + if (!$this->user_match_to($user, $to)) { + $this->error($from, _('Sorry, that is not your incoming email address.')); + return false; + } + if (!$user->emailpost) { + $this->error($from, _('Sorry, no incoming email allowed.')); + return false; + } + $response = $this->handle_command($user, $from, $msg); + if ($response) { + return true; + } + $msg = $this->cleanup_msg($msg); + $this->add_notice($user, $msg); + } + + function error($from, $msg) + { + file_put_contents("php://stderr", $msg . "\n"); + exit(1); + } + + function user_from($from_hdr) + { + $froms = mailparse_rfc822_parse_addresses($from_hdr); + if (!$froms) { + return null; + } + $from = $froms[0]; + $addr = common_canonical_email($from['address']); + $user = User::staticGet('email', $addr); + if (!$user) { + $user = User::staticGet('smsemail', $addr); + } + return $user; + } + + function user_match_to($user, $to_hdr) + { + $incoming = $user->incomingemail; + $tos = mailparse_rfc822_parse_addresses($to_hdr); + foreach ($tos as $to) { + if (strcasecmp($incoming, $to['address']) == 0) { + return true; + } + } + return false; + } + + function handle_command($user, $from, $msg) + { + $inter = new CommandInterpreter(); + $cmd = $inter->handle_command($user, $msg); + if ($cmd) { + $cmd->execute(new MailChannel($from)); + return true; + } + return false; + } + + function respond($from, $to, $response) + { + + $headers['From'] = $to; + $headers['To'] = $from; + $headers['Subject'] = "Command complete"; + + return mail_send(array($from), $headers, $response); + } + + function log($level, $msg) + { + common_log($level, 'MailDaemon: '.$msg); + } + + function add_notice($user, $msg) + { // should test // $msg_shortened = common_shorten_links($msg); // if (mb_strlen($msg_shortened) > 140) ERROR and STOP - $notice = Notice::saveNew($user->id, $msg, 'mail'); - if (is_string($notice)) { - $this->log(LOG_ERR, $notice); - return; - } - common_broadcast_notice($notice); - $this->log(LOG_INFO, - 'Added notice ' . $notice->id . ' from user ' . $user->nickname); - } - - function parse_message($fname) { - $contents = file_get_contents($fname); - $parsed = Mail_mimeDecode::decode(array('input' => $contents, - 'include_bodies' => true, - 'decode_headers' => true, - 'decode_bodies' => true)); - if (!$parsed) { - return NULL; - } - - $from = $parsed->headers['from']; - - $to = $parsed->headers['to']; - - $type = $parsed->ctype_primary . '/' . $parsed->ctype_secondary; - - if ($parsed->ctype_primary == 'multipart') { - foreach ($parsed->parts as $part) { - if ($part->ctype_primary == 'text' && - $part->ctype_secondary == 'plain') { - $msg = $part->body; - break; - } - } - } else if ($type == 'text/plain') { - $msg = $parsed->body; - } else { - $this->unsupported_type($type); - } - - return array($from, $to, $msg); - } - - function unsupported_type($type) { - $this->error(NULL, "Unsupported message type: " . $type); - } - - function cleanup_msg($msg) { - $lines = explode("\n", $msg); - - $output = ''; - - foreach ($lines as $line) { - // skip quotes - if (preg_match('/^\s*>.*$/', $line)) { - continue; - } - // skip start of quote - if (preg_match('/^\s*On.*wrote:\s*$/', $line)) { - continue; - } - // probably interesting to someone, not us - if (preg_match('/^\s*Sent via/', $line)) { - continue; - } - // skip everything after a sig - if (preg_match('/^\s*--+\s*$/', $line) || - preg_match('/^\s*__+\s*$/', $line)) - { - break; - } - // skip everything after Outlook quote - if (preg_match('/^\s*-+\s*Original Message\s*-+\s*$/', $line)) { - break; - } - // skip everything after weird forward - if (preg_match('/^\s*Begin\s+forward/', $line)) { - break; - } - - $output .= ' ' . $line; - } - - preg_replace('/\s+/', ' ', $output); - return trim($output); - } + $notice = Notice::saveNew($user->id, $msg, 'mail'); + if (is_string($notice)) { + $this->log(LOG_ERR, $notice); + return; + } + common_broadcast_notice($notice); + $this->log(LOG_INFO, + 'Added notice ' . $notice->id . ' from user ' . $user->nickname); + } + + function parse_message($fname) + { + $contents = file_get_contents($fname); + $parsed = Mail_mimeDecode::decode(array('input' => $contents, + 'include_bodies' => true, + 'decode_headers' => true, + 'decode_bodies' => true)); + if (!$parsed) { + return null; + } + + $from = $parsed->headers['from']; + + $to = $parsed->headers['to']; + + $type = $parsed->ctype_primary . '/' . $parsed->ctype_secondary; + + if ($parsed->ctype_primary == 'multipart') { + foreach ($parsed->parts as $part) { + if ($part->ctype_primary == 'text' && + $part->ctype_secondary == 'plain') { + $msg = $part->body; + break; + } + } + } else if ($type == 'text/plain') { + $msg = $parsed->body; + } else { + $this->unsupported_type($type); + } + + return array($from, $to, $msg); + } + + function unsupported_type($type) + { + $this->error(null, "Unsupported message type: " . $type); + } + + function cleanup_msg($msg) + { + $lines = explode("\n", $msg); + + $output = ''; + + foreach ($lines as $line) { + // skip quotes + if (preg_match('/^\s*>.*$/', $line)) { + continue; + } + // skip start of quote + if (preg_match('/^\s*On.*wrote:\s*$/', $line)) { + continue; + } + // probably interesting to someone, not us + if (preg_match('/^\s*Sent via/', $line)) { + continue; + } + // skip everything after a sig + if (preg_match('/^\s*--+\s*$/', $line) || + preg_match('/^\s*__+\s*$/', $line)) + { + break; + } + // skip everything after Outlook quote + if (preg_match('/^\s*-+\s*Original Message\s*-+\s*$/', $line)) { + break; + } + // skip everything after weird forward + if (preg_match('/^\s*Begin\s+forward/', $line)) { + break; + } + + $output .= ' ' . $line; + } + + preg_replace('/\s+/', ' ', $output); + return trim($output); + } } $md = new MailerDaemon(); diff --git a/scripts/ombqueuehandler.php b/scripts/ombqueuehandler.php index 1df816d14..cdcea51dc 100755 --- a/scripts/ombqueuehandler.php +++ b/scripts/ombqueuehandler.php @@ -20,8 +20,8 @@ # Abort if called from a web server if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { - print "This script must be run from the command line\n"; - exit(); + print "This script must be run from the command line\n"; + exit(); } define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); @@ -33,33 +33,39 @@ require_once(INSTALLDIR . '/lib/queuehandler.php'); set_error_handler('common_error_handler'); -class OmbQueueHandler extends QueueHandler { - - function transport() { - return 'omb'; - } - - function start() { - $this->log(LOG_INFO, "INITIALIZE"); - return true; - } +class OmbQueueHandler extends QueueHandler +{ + + function transport() + { + return 'omb'; + } + + function start() + { + $this->log(LOG_INFO, "INITIALIZE"); + return true; + } - function handle_notice($notice) { - if ($this->is_remote($notice)) { - $this->log(LOG_DEBUG, 'Ignoring remote notice ' . $notice->id); - return true; - } else { - return omb_broadcast_remote_subscribers($notice); - } - } - - function finish() { - } + function handle_notice($notice) + { + if ($this->is_remote($notice)) { + $this->log(LOG_DEBUG, 'Ignoring remote notice ' . $notice->id); + return true; + } else { + return omb_broadcast_remote_subscribers($notice); + } + } + + function finish() + { + } - function is_remote($notice) { - $user = User::staticGet($notice->profile_id); - return is_null($user); - } + function is_remote($notice) + { + $user = User::staticGet($notice->profile_id); + return is_null($user); + } } ini_set("max_execution_time", "0"); @@ -67,7 +73,7 @@ ini_set("max_input_time", "0"); set_time_limit(0); mb_internal_encoding('UTF-8'); -$id = ($argc > 1) ? $argv[1] : NULL; +$id = ($argc > 1) ? $argv[1] : null; $handler = new OmbQueueHandler($id); diff --git a/scripts/publicqueuehandler.php b/scripts/publicqueuehandler.php index b1ae1d581..5075c12df 100755 --- a/scripts/publicqueuehandler.php +++ b/scripts/publicqueuehandler.php @@ -20,8 +20,8 @@ # Abort if called from a web server if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { - print "This script must be run from the command line\n"; - exit(); + print "This script must be run from the command line\n"; + exit(); } define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); @@ -33,20 +33,23 @@ require_once(INSTALLDIR . '/lib/xmppqueuehandler.php'); set_error_handler('common_error_handler'); -class PublicQueueHandler extends XmppQueueHandler { - - function transport() { - return 'public'; - } - - function handle_notice($notice) { - try { - return jabber_public_notice($notice); - } catch (XMPPHP_Exception $e) { - $this->log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage()); - die($e->getMessage()); - } - } +class PublicQueueHandler extends XmppQueueHandler +{ + + function transport() + { + return 'public'; + } + + function handle_notice($notice) + { + try { + return jabber_public_notice($notice); + } catch (XMPPHP_Exception $e) { + $this->log(LOG_ERR, "Got an XMPPHP_Exception: " . $e->getMessage()); + die($e->getMessage()); + } + } } ini_set("max_execution_time", "0"); diff --git a/scripts/sitemap.php b/scripts/sitemap.php index 6b845beae..51a9bbd75 100644 --- a/scripts/sitemap.php +++ b/scripts/sitemap.php @@ -18,189 +18,193 @@ index_map(); # ------------------------------------------------------------------------------ # Generate index sitemap of all other sitemaps. -function index_map() { - global $output_paths; - $output_dir = $output_paths['output_dir']; - $output_url = $output_paths['output_url']; - - foreach (glob("$output_dir*.xml") as $file_name) { - - # Just the file name please. - $file_name = preg_replace("|$output_dir|", '', $file_name); - - $index_urls .= sitemap( - array( - 'url' => $output_url . $file_name, - 'changefreq' => 'daily' - ) - ); - } - - write_file($output_paths['index_file'], sitemapindex($index_urls)); +function index_map() +{ + global $output_paths; + $output_dir = $output_paths['output_dir']; + $output_url = $output_paths['output_url']; + + foreach (glob("$output_dir*.xml") as $file_name) { + + # Just the file name please. + $file_name = preg_replace("|$output_dir|", '', $file_name); + + $index_urls .= sitemap( + array( + 'url' => $output_url . $file_name, + 'changefreq' => 'daily' + ) + ); + } + + write_file($output_paths['index_file'], sitemapindex($index_urls)); } # Generate sitemap of standard site elements. -function standard_map() { - global $output_paths; - - $standard_map_urls .= url( - array( - 'url' => common_local_url('public'), - 'changefreq' => 'daily', - 'priority' => '1', - ) - ); - - $standard_map_urls .= url( - array( - 'url' => common_local_url('publicrss'), - 'changefreq' => 'daily', - 'priority' => '0.3', - ) - ); - - $docs = array('about', 'faq', 'contact', 'im', 'openid', 'openmublog', 'privacy', 'source'); - - foreach($docs as $title) { - $standard_map_urls .= url( - array( - 'url' => common_local_url('doc', array('title' => $title)), - 'changefreq' => 'monthly', - 'priority' => '0.2', - ) - ); - } - - $urlset_path = $output_paths['output_dir'] . 'standard.xml'; - - write_file($urlset_path, urlset($standard_map_urls)); +function standard_map() +{ + global $output_paths; + + $standard_map_urls .= url( + array( + 'url' => common_local_url('public'), + 'changefreq' => 'daily', + 'priority' => '1', + ) + ); + + $standard_map_urls .= url( + array( + 'url' => common_local_url('publicrss'), + 'changefreq' => 'daily', + 'priority' => '0.3', + ) + ); + + $docs = array('about', 'faq', 'contact', 'im', 'openid', 'openmublog', 'privacy', 'source'); + + foreach($docs as $title) { + $standard_map_urls .= url( + array( + 'url' => common_local_url('doc', array('title' => $title)), + 'changefreq' => 'monthly', + 'priority' => '0.2', + ) + ); + } + + $urlset_path = $output_paths['output_dir'] . 'standard.xml'; + + write_file($urlset_path, urlset($standard_map_urls)); } # Generate sitemaps of all notices. -function notices_map() { - global $output_paths; - - $notices = DB_DataObject::factory('notice'); - - $notices->query('SELECT id, uri, url, modified FROM notice where is_local = 1'); - - $notice_count = 0; - $map_count = 1; - - while ($notices->fetch()) { - - # Maximum 50,000 URLs per sitemap file. - if ($notice_count == 50000) { - $notice_count = 0; - $map_count++; - } - - # remote notices have an URL - - if (!$notices->url && $notices->uri) { - $notice = array( - 'url' => ($notices->uri) ? $notices->uri : common_local_url('shownotice', array('notice' => $notices->id)), - 'lastmod' => common_date_w3dtf($notices->modified), - 'changefreq' => 'never', - 'priority' => '1', - ); - - $notice_list[$map_count] .= url($notice); - $notice_count++; - } - } - - # Make full sitemaps from the lists and save them. - array_to_map($notice_list, 'notice'); +function notices_map() +{ + global $output_paths; + + $notices = DB_DataObject::factory('notice'); + + $notices->query('SELECT id, uri, url, modified FROM notice where is_local = 1'); + + $notice_count = 0; + $map_count = 1; + + while ($notices->fetch()) { + + # Maximum 50,000 URLs per sitemap file. + if ($notice_count == 50000) { + $notice_count = 0; + $map_count++; + } + + # remote notices have an URL + + if (!$notices->url && $notices->uri) { + $notice = array( + 'url' => ($notices->uri) ? $notices->uri : common_local_url('shownotice', array('notice' => $notices->id)), + 'lastmod' => common_date_w3dtf($notices->modified), + 'changefreq' => 'never', + 'priority' => '1', + ); + + $notice_list[$map_count] .= url($notice); + $notice_count++; + } + } + + # Make full sitemaps from the lists and save them. + array_to_map($notice_list, 'notice'); } # Generate sitemaps of all users. -function user_map() { - global $output_paths; - - $users = DB_DataObject::factory('user'); - - $users->query('SELECT id, nickname FROM user'); - - $user_count = 0; - $map_count = 1; - - while ($users->fetch()) { - - # Maximum 50,000 URLs per sitemap file. - if ($user_count == 50000) { - $user_count = 0; - $map_count++; - } - - $user_args = array('nickname' => $users->nickname); - - # Define parameters for generating <url></url> elements. - $user = array( - 'url' => common_local_url('showstream', $user_args), - 'changefreq' => 'daily', - 'priority' => '1', - ); - - $user_rss = array( - 'url' => common_local_url('userrss', $user_args), - 'changefreq' => 'daily', - 'priority' => '0.3', - ); - - $all = array( - 'url' => common_local_url('all', $user_args), - 'changefreq' => 'daily', - 'priority' => '1', - ); - - $all_rss = array( - 'url' => common_local_url('allrss', $user_args), - 'changefreq' => 'daily', - 'priority' => '0.3', - ); - - $replies = array( - 'url' => common_local_url('replies', $user_args), - 'changefreq' => 'daily', - 'priority' => '1', - ); - - $replies_rss = array( - 'url' => common_local_url('repliesrss', $user_args), - 'changefreq' => 'daily', - 'priority' => '0.3', - ); - - $foaf = array( - 'url' => common_local_url('foaf', $user_args), - 'changefreq' => 'weekly', - 'priority' => '0.5', - ); - - # Construct a <url></url> element for each user facet and add it - # to our existing list of those. - $user_list[$map_count] .= url($user); - $user_rss_list[$map_count] .= url($user_rss); - $all_list[$map_count] .= url($all); - $all_rss_list[$map_count] .= url($all_rss); - $replies_list[$map_count] .= url($replies); - $replies_rss_list[$map_count] .= url($replies_rss); - $foaf_list[$map_count] .= url($foaf); - - $user_count++; - } - - # Make full sitemaps from the lists and save them. - # Possible factoring: put all the lists into a master array, thus allowing - # calling with single argument (i.e., array_to_map('user')). - array_to_map($user_list, 'user'); - array_to_map($user_rss_list, 'user_rss'); - array_to_map($all_list, 'all'); - array_to_map($all_rss_list, 'all_rss'); - array_to_map($replies_list, 'replies'); - array_to_map($replies_rss_list, 'replies_rss'); - array_to_map($foaf_list, 'foaf'); +function user_map() +{ + global $output_paths; + + $users = DB_DataObject::factory('user'); + + $users->query('SELECT id, nickname FROM user'); + + $user_count = 0; + $map_count = 1; + + while ($users->fetch()) { + + # Maximum 50,000 URLs per sitemap file. + if ($user_count == 50000) { + $user_count = 0; + $map_count++; + } + + $user_args = array('nickname' => $users->nickname); + + # Define parameters for generating <url></url> elements. + $user = array( + 'url' => common_local_url('showstream', $user_args), + 'changefreq' => 'daily', + 'priority' => '1', + ); + + $user_rss = array( + 'url' => common_local_url('userrss', $user_args), + 'changefreq' => 'daily', + 'priority' => '0.3', + ); + + $all = array( + 'url' => common_local_url('all', $user_args), + 'changefreq' => 'daily', + 'priority' => '1', + ); + + $all_rss = array( + 'url' => common_local_url('allrss', $user_args), + 'changefreq' => 'daily', + 'priority' => '0.3', + ); + + $replies = array( + 'url' => common_local_url('replies', $user_args), + 'changefreq' => 'daily', + 'priority' => '1', + ); + + $replies_rss = array( + 'url' => common_local_url('repliesrss', $user_args), + 'changefreq' => 'daily', + 'priority' => '0.3', + ); + + $foaf = array( + 'url' => common_local_url('foaf', $user_args), + 'changefreq' => 'weekly', + 'priority' => '0.5', + ); + + # Construct a <url></url> element for each user facet and add it + # to our existing list of those. + $user_list[$map_count] .= url($user); + $user_rss_list[$map_count] .= url($user_rss); + $all_list[$map_count] .= url($all); + $all_rss_list[$map_count] .= url($all_rss); + $replies_list[$map_count] .= url($replies); + $replies_rss_list[$map_count] .= url($replies_rss); + $foaf_list[$map_count] .= url($foaf); + + $user_count++; + } + + # Make full sitemaps from the lists and save them. + # Possible factoring: put all the lists into a master array, thus allowing + # calling with single argument (i.e., array_to_map('user')). + array_to_map($user_list, 'user'); + array_to_map($user_rss_list, 'user_rss'); + array_to_map($all_list, 'all'); + array_to_map($all_rss_list, 'all_rss'); + array_to_map($replies_list, 'replies'); + array_to_map($replies_rss_list, 'replies_rss'); + array_to_map($foaf_list, 'foaf'); } # ------------------------------------------------------------------------------ @@ -208,88 +212,93 @@ function user_map() { # ------------------------------------------------------------------------------ # Generate a <url></url> element. -function url($url_args) { - $url = preg_replace('/&/', '&', $url_args['url']); # escape ampersands for XML - $lastmod = $url_args['lastmod']; - $changefreq = $url_args['changefreq']; - $priority = $url_args['priority']; +function url($url_args) +{ + $url = preg_replace('/&/', '&', $url_args['url']); # escape ampersands for XML + $lastmod = $url_args['lastmod']; + $changefreq = $url_args['changefreq']; + $priority = $url_args['priority']; - if (is_null($url)) { - error("url() arguments require 'url' value."); - } + if (is_null($url)) { + error("url() arguments require 'url' value."); + } - $url_out = "\t<url>\n"; - $url_out .= "\t\t<loc>$url</loc>\n"; + $url_out = "\t<url>\n"; + $url_out .= "\t\t<loc>$url</loc>\n"; - if ($changefreq) { - $url_out .= "\t\t<changefreq>$changefreq</changefreq>\n"; - } + if ($changefreq) { + $url_out .= "\t\t<changefreq>$changefreq</changefreq>\n"; + } - if ($lastmod) { - $url_out .= "\t\t<lastmod>$lastmod</lastmod>\n"; - } + if ($lastmod) { + $url_out .= "\t\t<lastmod>$lastmod</lastmod>\n"; + } - if ($priority) { - $url_out .= "\t\t<priority>$priority</priority>\n"; - } + if ($priority) { + $url_out .= "\t\t<priority>$priority</priority>\n"; + } - $url_out .= "\t</url>\n"; + $url_out .= "\t</url>\n"; - return $url_out; + return $url_out; } -function sitemap($sitemap_args) { - $url = preg_replace('/&/', '&', $sitemap_args['url']); # escape ampersands for XML - $lastmod = $sitemap_args['lastmod']; +function sitemap($sitemap_args) +{ + $url = preg_replace('/&/', '&', $sitemap_args['url']); # escape ampersands for XML + $lastmod = $sitemap_args['lastmod']; - if (is_null($url)) { - error("url() arguments require 'url' value."); - } + if (is_null($url)) { + error("url() arguments require 'url' value."); + } - $sitemap_out = "\t<sitemap>\n"; - $sitemap_out .= "\t\t<loc>$url</loc>\n"; + $sitemap_out = "\t<sitemap>\n"; + $sitemap_out .= "\t\t<loc>$url</loc>\n"; - if ($lastmod) { - $sitemap_out .= "\t\t<lastmod>$lastmod</lastmod>\n"; - } + if ($lastmod) { + $sitemap_out .= "\t\t<lastmod>$lastmod</lastmod>\n"; + } - $sitemap_out .= "\t</sitemap>\n"; + $sitemap_out .= "\t</sitemap>\n"; - return $sitemap_out; + return $sitemap_out; } # Generate a <urlset></urlset> element. -function urlset($urlset_text) { - $urlset = '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . - '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n" . - $urlset_text . - '</urlset>'; - - return $urlset; +function urlset($urlset_text) +{ + $urlset = '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . + '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n" . + $urlset_text . + '</urlset>'; + + return $urlset; } # Generate a <urlset></urlset> element. -function sitemapindex($sitemapindex_text) { - $sitemapindex = '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . - '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n" . - $sitemapindex_text . - '</sitemapindex>'; - - return $sitemapindex; +function sitemapindex($sitemapindex_text) +{ + $sitemapindex = '<?xml version="1.0" encoding="UTF-8"?>' . "\n" . + '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' . "\n" . + $sitemapindex_text . + '</sitemapindex>'; + + return $sitemapindex; } # Generate a sitemap from an array containing <url></url> elements and write it to a file. -function array_to_map($url_list, $filename_prefix) { - global $output_paths; - - if ($url_list) { - # $map_urls is a long string containing concatenated <url></url> elements. - while (list($map_idx, $map_urls) = each($url_list)) { - $urlset_path = $output_paths['output_dir'] . "$filename_prefix-$map_idx.xml"; - - write_file($urlset_path, urlset($map_urls)); - } - } +function array_to_map($url_list, $filename_prefix) +{ + global $output_paths; + + if ($url_list) { + # $map_urls is a long string containing concatenated <url></url> elements. + while (list($map_idx, $map_urls) = each($url_list)) { + $urlset_path = $output_paths['output_dir'] . "$filename_prefix-$map_idx.xml"; + + write_file($urlset_path, urlset($map_urls)); + } + } } # ------------------------------------------------------------------------------ @@ -297,80 +306,84 @@ function array_to_map($url_list, $filename_prefix) { # ------------------------------------------------------------------------------ # Parse command line arguments. -function parse_args() { - $args = getopt('f:d:u:'); - - if (is_null($args[f]) && is_null($args[d]) && is_null($args[u])) { - error('Mandatory arguments: -f <index file path> -d <output directory path> -u <URL of sitemaps directory>'); - } - - if (is_null($args[f])) { - error('You must specify an index file name with the -f option.'); - } - - if (is_null($args[d])) { - error('You must specify a directory for the output file with the -d option.'); - } - - if (is_null($args[u])) { - error('You must specify a URL for the directory where the sitemaps will be kept with the -u option.'); - } - - $index_file = $args[f]; - $output_dir = $args[d]; - $output_url = $args[u]; - - if (file_exists($output_dir)) { - if (is_writable($output_dir) === FALSE) { - error("$output_dir is not writable."); - } - } else { - error("output directory $output_dir does not exist."); - } - - $paths = array( - 'index_file' => $index_file, - 'output_dir' => trailing_slash($output_dir), - 'output_url' => trailing_slash($output_url), - ); - - return $paths; +function parse_args() +{ + $args = getopt('f:d:u:'); + + if (is_null($args[f]) && is_null($args[d]) && is_null($args[u])) { + error('Mandatory arguments: -f <index file path> -d <output directory path> -u <URL of sitemaps directory>'); + } + + if (is_null($args[f])) { + error('You must specify an index file name with the -f option.'); + } + + if (is_null($args[d])) { + error('You must specify a directory for the output file with the -d option.'); + } + + if (is_null($args[u])) { + error('You must specify a URL for the directory where the sitemaps will be kept with the -u option.'); + } + + $index_file = $args[f]; + $output_dir = $args[d]; + $output_url = $args[u]; + + if (file_exists($output_dir)) { + if (is_writable($output_dir) === false) { + error("$output_dir is not writable."); + } + } else { + error("output directory $output_dir does not exist."); + } + + $paths = array( + 'index_file' => $index_file, + 'output_dir' => trailing_slash($output_dir), + 'output_url' => trailing_slash($output_url), + ); + + return $paths; } # Ensure paths end with a "/". -function trailing_slash($path) { - if (preg_match('/\/$/', $path) == 0) { - $path .= '/'; - } +function trailing_slash($path) +{ + if (preg_match('/\/$/', $path) == 0) { + $path .= '/'; + } - return $path; + return $path; } # Write data to disk. -function write_file($path, $data) { - if (is_null($path)) { - error('No path specified for writing to.'); - } elseif (is_null($data)) { - error('No data specified for writing.'); - } - - if (($fh_out = fopen($path,'w')) === FALSE) { - error("couldn't open $path for writing."); - } - - if (fwrite($fh_out, $data) === FALSE) { - error("couldn't write to $path."); - } +function write_file($path, $data) +{ + if (is_null($path)) { + error('No path specified for writing to.'); + } elseif (is_null($data)) { + error('No data specified for writing.'); + } + + if (($fh_out = fopen($path,'w')) === false) { + error("couldn't open $path for writing."); + } + + if (fwrite($fh_out, $data) === false) { + error("couldn't write to $path."); + } } # Display an error message and exit. -function error ($error_msg) { - if (is_null($error_msg)) { - $error_msg = 'error() was called without any explanation!'; - } - - echo "Error: $error_msg\n"; - exit(1); +function error ($error_msg) +{ + if (is_null($error_msg)) { + $error_msg = 'error() was called without any explanation!'; + } + + echo "Error: $error_msg\n"; + exit(1); } ?>
\ No newline at end of file diff --git a/scripts/smsqueuehandler.php b/scripts/smsqueuehandler.php index 8f0d02d9b..38f2f11fe 100755 --- a/scripts/smsqueuehandler.php +++ b/scripts/smsqueuehandler.php @@ -20,8 +20,8 @@ # Abort if called from a web server if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { - print "This script must be run from the command line\n"; - exit(); + print "This script must be run from the command line\n"; + exit(); } define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); @@ -33,23 +33,28 @@ require_once(INSTALLDIR . '/lib/queuehandler.php'); set_error_handler('common_error_handler'); -class SmsQueueHandler extends QueueHandler { - - function transport() { - return 'sms'; - } +class SmsQueueHandler extends QueueHandler +{ + + function transport() + { + return 'sms'; + } - function start() { - $this->log(LOG_INFO, "INITIALIZE"); - return true; - } + function start() + { + $this->log(LOG_INFO, "INITIALIZE"); + return true; + } - function handle_notice($notice) { - return mail_broadcast_notice_sms($notice); - } - - function finish() { - } + function handle_notice($notice) + { + return mail_broadcast_notice_sms($notice); + } + + function finish() + { + } } ini_set("max_execution_time", "0"); @@ -57,7 +62,7 @@ ini_set("max_input_time", "0"); set_time_limit(0); mb_internal_encoding('UTF-8'); -$id = ($argc > 1) ? $argv[1] : NULL; +$id = ($argc > 1) ? $argv[1] : null; $handler = new SmsQueueHandler($id); diff --git a/scripts/synctwitterfriends.php b/scripts/synctwitterfriends.php index 070eb9bbb..0ce34c2ae 100755 --- a/scripts/synctwitterfriends.php +++ b/scripts/synctwitterfriends.php @@ -11,17 +11,17 @@ * * 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 + * 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/>. + * along with this program. If not, see <http://www.gnu.org/licenses/>. */ # Abort if called from a web server if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { - print "This script must be run from the command line\n"; - exit(); + print "This script must be run from the command line\n"; + exit(); } define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); @@ -35,21 +35,21 @@ $flink->find(); while ($flink->fetch()) { - if (($flink->friendsync & FOREIGN_FRIEND_RECV) == FOREIGN_FRIEND_RECV) { + if (($flink->friendsync & FOREIGN_FRIEND_RECV) == FOREIGN_FRIEND_RECV) { - $user = User::staticGet($flink->user_id); + $user = User::staticGet($flink->user_id); - print "Updating Twitter friends for user $user->nickname ($user->id)\n"; + print "Updating Twitter friends for user $user->nickname ($user->id)\n"; - $fuser = $flink->getForeignUser(); + $fuser = $flink->getForeignUser(); - $result = save_twitter_friends($user, $fuser->id, $fuser->nickname, $flink->credentials); + $result = save_twitter_friends($user, $fuser->id, $fuser->nickname, $flink->credentials); - if ($result == false) { - print "Problems updating Twitter friends! Check the log.\n"; - exit(1); - } - } + if ($result == false) { + print "Problems updating Twitter friends! Check the log.\n"; + exit(1); + } + } } diff --git a/scripts/update_facebook.php b/scripts/update_facebook.php new file mode 100755 index 000000000..15c0e49f8 --- /dev/null +++ b/scripts/update_facebook.php @@ -0,0 +1,112 @@ +#!/usr/bin/env php +<?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/>. + */ + +# Abort if called from a web server +if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { + print "This script must be run from the command line\n"; + exit(); +} + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); +define('LACONICA', true); + +require_once(INSTALLDIR . '/lib/common.php'); +require_once(INSTALLDIR . '/lib/facebookutil.php'); + +// For storing the last run date-time +$last_updated_file = "/home/zach/laconica/scripts/facebook_last_updated"; + +// Lock file name +$tmp_file = "/tmp/update_facebook.lock"; + +// Make sure only one copy of the script is running at a time +if (!($tmp_file = @fopen($tmp_file, "w"))) +{ + die("Can't open lock file. Script already running?"); +} + +$facebook = get_facebook(); + +$current_time = time(); + +$notice = get_facebook_notices(get_last_updated()); + +print date('r', $current_time) . " Looking for notices to send to Facebook...\n"; + +$cnt = 0; + +while($notice->fetch()) { + + $flink = Foreign_link::getByUserID($notice->profile_id, 2); + $fbuid = $flink->foreign_id; + $content = $notice->content; + + if (($flink->noticesync & FOREIGN_NOTICE_SEND) == FOREIGN_NOTICE_SEND) { + + // If it's not a reply, or if the user WANTS to send replies... + if (!preg_match('/@[a-zA-Z0-9_]{1,15}\b/u', $content) || + (($flink->noticesync & FOREIGN_NOTICE_SEND_REPLY) == FOREIGN_NOTICE_SEND_REPLY)) { + update_status($fbuid, $content); + $cnt++; + } + } +} + +update_last_updated($current_time); + +print "Sent $cnt notices to Facebook.\n"; + +exit(0); + + + +function update_status($fbuid, $content) { + global $facebook; + + try { + $result = $facebook->api_client->users_setStatus($content, $fbuid, false, true); + } catch(FacebookRestClientException $e){ + print_r($e); + } +} + +function get_last_updated(){ + global $last_updated_file, $current_time; + + $file = fopen($last_updated_file, 'r'); + + if ($file) { + $last = fgets($file); + } else { + print "Unable to read $last_updated_file. Using current time.\n"; + return $current_time; + } + + fclose($file); + + return $last; +} + +function update_last_updated($time){ + global $last_updated_file; + $file = fopen($last_updated_file, 'w') or die("Can't open $last_updated_file for writing!"); + fwrite($file, $time); + fclose($file); +} diff --git a/scripts/update_translations.php b/scripts/update_translations.php index 3eb7b3401..8ad07ccca 100644 --- a/scripts/update_translations.php +++ b/scripts/update_translations.php @@ -5,62 +5,62 @@ chdir(dirname(__FILE__) . '/..'); /* Languages to pull */ $languages = array( - 'da_DK' => 'http://laconi.ca/translate/download.php?file_id=23', - 'nl_NL' => 'http://laconi.ca/translate/download.php?file_id=39', - 'en_NZ' => 'http://laconi.ca/translate/download.php?file_id=15', - 'eo' => 'http://laconi.ca/translate/download.php?file_id=10', - 'fr_FR' => 'http://laconi.ca/translate/download.php?file_id=19', - 'de_DE' => 'http://laconi.ca/translate/download.php?file_id=18', - 'it_IT' => 'http://laconi.ca/translate/download.php?file_id=21', - 'ko' => 'http://laconi.ca/translate/download.php?file_id=33', - 'no_NB' => 'http://laconi.ca/translate/download.php?file_id=31', - 'pt' => 'http://laconi.ca/translate/download.php?file_id=8', - 'pt_BR' => 'http://laconi.ca/translate/download.php?file_id=72', - 'ru_RU' => 'http://laconi.ca/translate/download.php?file_id=26', - 'es' => 'http://laconi.ca/translate/download.php?file_id=9', - 'tr_TR' => 'http://laconi.ca/translate/download.php?file_id=37', - 'uk_UA' => 'http://laconi.ca/translate/download.php?file_id=44', - 'he_IL' => 'http://laconi.ca/translate/download.php?file_id=71', - 'mk_MK' => 'http://laconi.ca/translate/download.php?file_id=67', - 'ja_JP' => 'http://laconi.ca/translate/download.php?file_id=43', - 'cs_CZ' => 'http://laconi.ca/translate/download.php?file_id=63', - 'ca_ES' => 'http://laconi.ca/translate/download.php?file_id=49', - 'pl_PL' => 'http://laconi.ca/translate/download.php?file_id=51', - 'sv_SE' => 'http://laconi.ca/translate/download.php?file_id=55' + 'da_DK' => 'http://laconi.ca/translate/download.php?file_id=23', + 'nl_NL' => 'http://laconi.ca/translate/download.php?file_id=39', + 'en_NZ' => 'http://laconi.ca/translate/download.php?file_id=15', + 'eo' => 'http://laconi.ca/translate/download.php?file_id=10', + 'fr_FR' => 'http://laconi.ca/translate/download.php?file_id=19', + 'de_DE' => 'http://laconi.ca/translate/download.php?file_id=18', + 'it_IT' => 'http://laconi.ca/translate/download.php?file_id=21', + 'ko' => 'http://laconi.ca/translate/download.php?file_id=33', + 'no_NB' => 'http://laconi.ca/translate/download.php?file_id=31', + 'pt' => 'http://laconi.ca/translate/download.php?file_id=8', + 'pt_BR' => 'http://laconi.ca/translate/download.php?file_id=72', + 'ru_RU' => 'http://laconi.ca/translate/download.php?file_id=26', + 'es' => 'http://laconi.ca/translate/download.php?file_id=9', + 'tr_TR' => 'http://laconi.ca/translate/download.php?file_id=37', + 'uk_UA' => 'http://laconi.ca/translate/download.php?file_id=44', + 'he_IL' => 'http://laconi.ca/translate/download.php?file_id=71', + 'mk_MK' => 'http://laconi.ca/translate/download.php?file_id=67', + 'ja_JP' => 'http://laconi.ca/translate/download.php?file_id=43', + 'cs_CZ' => 'http://laconi.ca/translate/download.php?file_id=63', + 'ca_ES' => 'http://laconi.ca/translate/download.php?file_id=49', + 'pl_PL' => 'http://laconi.ca/translate/download.php?file_id=51', + 'sv_SE' => 'http://laconi.ca/translate/download.php?file_id=55' ); /* Update the languages */ foreach ($languages as $code => $file) { - $lcdir='locale/'.$code; - $msgdir=$lcdir.'/LC_MESSAGES'; - $pofile=$msgdir.'/laconica.po'; - $mofile=$msgdir.'/laconica.mo'; + $lcdir='locale/'.$code; + $msgdir=$lcdir.'/LC_MESSAGES'; + $pofile=$msgdir.'/laconica.po'; + $mofile=$msgdir.'/laconica.mo'; - /* Check for an existing */ - if (!is_dir($msgdir)) { - mkdir($lcdir); - mkdir($msgdir); - $existingSHA1 = ''; - } else { - $existingSHA1 = file_exists($pofile) ? sha1_file($pofile) : ''; - } + /* Check for an existing */ + if (!is_dir($msgdir)) { + mkdir($lcdir); + mkdir($msgdir); + $existingSHA1 = ''; + } else { + $existingSHA1 = file_exists($pofile) ? sha1_file($pofile) : ''; + } - /* Get the remote one */ - $newFile = file_get_contents($file); + /* Get the remote one */ + $newFile = file_get_contents($file); - // Update if the local .po file is different to the one downloaded, or - // if the .mo file is not present. - if(sha1($newFile)!=$existingSHA1 || !file_exists($mofile)) { - echo "Updating ".$code."\n"; - file_put_contents($pofile, $newFile); - $prevdir = getcwd(); - chdir($msgdir); - system('msgmerge -U laconica.po ../../laconica.pot'); - system('msgfmt -f -o laconica.mo laconica.po'); - chdir($prevdir); - } else { - echo "Unchanged - ".$code."\n"; - } + // Update if the local .po file is different to the one downloaded, or + // if the .mo file is not present. + if(sha1($newFile)!=$existingSHA1 || !file_exists($mofile)) { + echo "Updating ".$code."\n"; + file_put_contents($pofile, $newFile); + $prevdir = getcwd(); + chdir($msgdir); + system('msgmerge -U laconica.po ../../laconica.pot'); + system('msgfmt -f -o laconica.mo laconica.po'); + chdir($prevdir); + } else { + echo "Unchanged - ".$code."\n"; + } } echo "Finished\n"; diff --git a/scripts/xmppconfirmhandler.php b/scripts/xmppconfirmhandler.php index 8961b0b6e..2b8b085ce 100755 --- a/scripts/xmppconfirmhandler.php +++ b/scripts/xmppconfirmhandler.php @@ -20,8 +20,8 @@ # Abort if called from a web server if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { - print "This script must be run from the command line\n"; - exit(); + print "This script must be run from the command line\n"; + exit(); } define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); @@ -35,104 +35,109 @@ set_error_handler('common_error_handler'); define('CLAIM_TIMEOUT', 1200); -class XmppConfirmHandler extends XmppQueueHandler { +class XmppConfirmHandler extends XmppQueueHandler +{ - var $_id = 'confirm'; - - function class_name() { - return 'XmppConfirmHandler'; - } - - function run() { - if (!$this->start()) { - return false; - } - $this->log(LOG_INFO, 'checking for queued confirmations'); - do { - $confirm = $this->next_confirm(); - if ($confirm) { - $this->log(LOG_INFO, 'Sending confirmation for ' . $confirm->address); - $user = User::staticGet($confirm->user_id); - if (!$user) { - $this->log(LOG_WARNING, 'Confirmation for unknown user ' . $confirm->user_id); - continue; - } - $success = jabber_confirm_address($confirm->code, - $user->nickname, - $confirm->address); - if (!$success) { - $this->log(LOG_ERR, 'Confirmation failed for ' . $confirm->address); - # Just let the claim age out; hopefully things work then - continue; - } else { - $this->log(LOG_INFO, 'Confirmation sent for ' . $confirm->address); - # Mark confirmation sent; need a dupe so we don't have the WHERE clause - $dupe = Confirm_address::staticGet('code', $confirm->code); - if (!$dupe) { - common_log(LOG_WARNING, 'Could not refetch confirm', __FILE__); - continue; - } - $orig = clone($dupe); - $dupe->sent = $dupe->claimed; - $result = $dupe->update($orig); - if (!$result) { - common_log_db_error($dupe, 'UPDATE', __FILE__); - # Just let the claim age out; hopefully things work then - continue; - } - $dupe->free(); - unset($dupe); - } - $user->free(); - unset($user); - $confirm->free(); - unset($confirm); - $this->idle(0); - } else { -# $this->clear_old_confirm_claims(); - $this->idle(10); - } - } while (true); - if (!$this->finish()) { - return false; - } - return true; - } + var $_id = 'confirm'; + + function class_name() + { + return 'XmppConfirmHandler'; + } + + function run() + { + if (!$this->start()) { + return false; + } + $this->log(LOG_INFO, 'checking for queued confirmations'); + do { + $confirm = $this->next_confirm(); + if ($confirm) { + $this->log(LOG_INFO, 'Sending confirmation for ' . $confirm->address); + $user = User::staticGet($confirm->user_id); + if (!$user) { + $this->log(LOG_WARNING, 'Confirmation for unknown user ' . $confirm->user_id); + continue; + } + $success = jabber_confirm_address($confirm->code, + $user->nickname, + $confirm->address); + if (!$success) { + $this->log(LOG_ERR, 'Confirmation failed for ' . $confirm->address); + # Just let the claim age out; hopefully things work then + continue; + } else { + $this->log(LOG_INFO, 'Confirmation sent for ' . $confirm->address); + # Mark confirmation sent; need a dupe so we don't have the WHERE clause + $dupe = Confirm_address::staticGet('code', $confirm->code); + if (!$dupe) { + common_log(LOG_WARNING, 'Could not refetch confirm', __FILE__); + continue; + } + $orig = clone($dupe); + $dupe->sent = $dupe->claimed; + $result = $dupe->update($orig); + if (!$result) { + common_log_db_error($dupe, 'UPDATE', __FILE__); + # Just let the claim age out; hopefully things work then + continue; + } + $dupe->free(); + unset($dupe); + } + $user->free(); + unset($user); + $confirm->free(); + unset($confirm); + $this->idle(0); + } else { +# $this->clear_old_confirm_claims(); + $this->idle(10); + } + } while (true); + if (!$this->finish()) { + return false; + } + return true; + } - function next_confirm() { - $confirm = new Confirm_address(); - $confirm->whereAdd('claimed IS NULL'); - $confirm->whereAdd('sent IS NULL'); - # XXX: eventually we could do other confirmations in the queue, too - $confirm->address_type = 'jabber'; - $confirm->orderBy('modified DESC'); - $confirm->limit(1); - if ($confirm->find(TRUE)) { - $this->log(LOG_INFO, 'Claiming confirmation for ' . $confirm->address); - # working around some weird DB_DataObject behaviour - $confirm->whereAdd(''); # clears where stuff - $original = clone($confirm); - $confirm->claimed = common_sql_now(); - $result = $confirm->update($original); - if ($result) { - $this->log(LOG_INFO, 'Succeeded in claim! '. $result); - return $confirm; - } else { - $this->log(LOG_INFO, 'Failed in claim!'); - return false; - } - } - return NULL; - } + function next_confirm() + { + $confirm = new Confirm_address(); + $confirm->whereAdd('claimed IS null'); + $confirm->whereAdd('sent IS null'); + # XXX: eventually we could do other confirmations in the queue, too + $confirm->address_type = 'jabber'; + $confirm->orderBy('modified DESC'); + $confirm->limit(1); + if ($confirm->find(true)) { + $this->log(LOG_INFO, 'Claiming confirmation for ' . $confirm->address); + # working around some weird DB_DataObject behaviour + $confirm->whereAdd(''); # clears where stuff + $original = clone($confirm); + $confirm->claimed = common_sql_now(); + $result = $confirm->update($original); + if ($result) { + $this->log(LOG_INFO, 'Succeeded in claim! '. $result); + return $confirm; + } else { + $this->log(LOG_INFO, 'Failed in claim!'); + return false; + } + } + return null; + } - function clear_old_confirm_claims() { - $confirm = new Confirm(); - $confirm->claimed = NULL; - $confirm->whereAdd('now() - claimed > '.CLAIM_TIMEOUT); - $confirm->update(DB_DATAOBJECT_WHEREADD_ONLY); - $confirm->free(); - unset($confirm); - } + function clear_old_confirm_claims() + { + $confirm = new Confirm(); + $confirm->claimed = null; + $confirm->whereAdd('now() - claimed > '.CLAIM_TIMEOUT); + $confirm->update(DB_DATAOBJECT_WHEREADD_ONLY); + $confirm->free(); + unset($confirm); + } } ini_set("max_execution_time", "0"); diff --git a/scripts/xmppdaemon.php b/scripts/xmppdaemon.php index 9a60970a6..01fe8914f 100755 --- a/scripts/xmppdaemon.php +++ b/scripts/xmppdaemon.php @@ -20,8 +20,8 @@ # Abort if called from a web server if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) { - print "This script must be run from the command line\n"; - exit(); + print "This script must be run from the command line\n"; + exit(); } define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); @@ -37,205 +37,221 @@ set_error_handler('common_error_handler'); # in jabber.php, which create a new XMPP class. A more elegant (?) solution # might be to use make this a subclass of XMPP. -class XMPPDaemon extends Daemon { - - function XMPPDaemon($resource=NULL) { - static $attrs = array('server', 'port', 'user', 'password', 'host'); - - foreach ($attrs as $attr) - { - $this->$attr = common_config('xmpp', $attr); - } - - if ($resource) { - $this->resource = $resource; - } else { - $this->resource = common_config('xmpp', 'resource') . 'daemon'; - } - - $this->log(LOG_INFO, "INITIALIZE XMPPDaemon {$this->user}@{$this->server}/{$this->resource}"); - } - - function connect() { - - $connect_to = ($this->host) ? $this->host : $this->server; - - $this->log(LOG_INFO, "Connecting to $connect_to on port $this->port"); - - $this->conn = jabber_connect($this->resource); - - if (!$this->conn) { - return false; - } - - $this->conn->setReconnectTimeout(600); - - jabber_send_presence("Send me a message to post a notice", 'available', - NULL, 'available', 100); - return !$this->conn->isDisconnected(); - } - - function name() { - return strtolower('xmppdaemon.'.$this->resource); - } - - function run() { - if ($this->connect()) { - - $this->conn->addEventHandler('message', 'handle_message', $this); - $this->conn->addEventHandler('presence', 'handle_presence', $this); - $this->conn->addEventHandler('reconnect', 'handle_reconnect', $this); - - $this->conn->process(); - } - } - - function handle_reconnect(&$pl) { - $this->conn->processUntil('session_start'); - $this->conn->presence('Send me a message to post a notice', 'available', NULL, 'available', 100); - } - - function get_user($from) { - $user = User::staticGet('jabber', jabber_normalize_jid($from)); - return $user; - } - - function handle_message(&$pl) { - if ($pl['type'] != 'chat') { - return; - } - if (mb_strlen($pl['body']) == 0) { - return; - } - - $from = jabber_normalize_jid($pl['from']); - - # Forwarded from another daemon (probably a broadcaster) for - # us to handle - - if ($this->is_self($from)) { - $from = $this->get_ofrom($pl); - if (is_null($from) || $this->is_self($from)) { - return; - } - } - - $user = $this->get_user($from); - - if (!$user) { - $this->from_site($from, 'Unknown user; go to ' . - common_local_url('imsettings') . - ' to add your address to your account'); - $this->log(LOG_WARNING, 'Message from unknown user ' . $from); - return; - } - if ($this->handle_command($user, $pl['body'])) { - return; - } else if ($this->is_autoreply($pl['body'])) { - $this->log(LOG_INFO, 'Ignoring auto reply from ' . $from); - return; - } else if ($this->is_otr($pl['body'])) { - $this->log(LOG_INFO, 'Ignoring OTR from ' . $from); - return; - } else if ($this->is_direct($pl['body'])) { - preg_match_all('/d[\ ]*([a-z0-9]{1,64})/', $pl['body'], $to); - - $to = preg_replace('/^d([\ ])*/', '', $to[0][0]); - $body = preg_replace('/d[\ ]*('. $to .')[\ ]*/', '', $pl['body']); - $this->add_direct($user, $body, $to, $from); - } else { - $len = mb_strlen($pl['body']); - if($len > 140) { - $this->from_site($from, 'Message too long - maximum is 140 characters, you sent ' . $len); - return; - } - $this->add_notice($user, $pl); - } - - $user->free(); - unset($user); - } - - function is_self($from) { - return preg_match('/^'.strtolower(jabber_daemon_address()).'/', strtolower($from)); - } - - function get_ofrom($pl) { - $xml = $pl['xml']; - $addresses = $xml->sub('addresses'); - if (!$addresses) { - $this->log(LOG_WARNING, 'Forwarded message without addresses'); - return NULL; - } - $address = $addresses->sub('address'); - if (!$address) { - $this->log(LOG_WARNING, 'Forwarded message without address'); - return NULL; - } - if (!array_key_exists('type', $address->attrs)) { - $this->log(LOG_WARNING, 'No type for forwarded message'); - return NULL; - } - $type = $address->attrs['type']; - if ($type != 'ofrom') { - $this->log(LOG_WARNING, 'Type of forwarded message is not ofrom'); - return NULL; - } - if (!array_key_exists('jid', $address->attrs)) { - $this->log(LOG_WARNING, 'No jid for forwarded message'); - return NULL; - } - $jid = $address->attrs['jid']; - if (!$jid) { - $this->log(LOG_WARNING, 'Could not get jid from address'); - return NULL; - } - $this->log(LOG_DEBUG, 'Got message forwarded from jid ' . $jid); - return $jid; - } - - function is_autoreply($txt) { - if (preg_match('/[\[\(]?[Aa]uto[-\s]?[Rr]e(ply|sponse)[\]\)]/', $txt)) { - return true; - } else { - return false; - } - } - - function is_otr($txt) { - if (preg_match('/^\?OTR/', $txt)) { - return true; - } else { - return false; - } - } - - function is_direct($txt) { - if (strtolower(substr($txt, 0, 2))=='d ') { - return true; - } else { - return false; - } - } - - function from_site($address, $msg) { - $text = '['.common_config('site', 'name') . '] ' . $msg; - jabber_send_message($address, $text); - } - - function handle_command($user, $body) { - $inter = new CommandInterpreter(); - $cmd = $inter->handle_command($user, $body); - if ($cmd) { - $chan = new XMPPChannel($this->conn); - $cmd->execute($chan); - return true; - } else { - return false; - } - } - - function add_notice(&$user, &$pl) { +class XMPPDaemon extends Daemon +{ + + function XMPPDaemon($resource=null) + { + static $attrs = array('server', 'port', 'user', 'password', 'host'); + + foreach ($attrs as $attr) + { + $this->$attr = common_config('xmpp', $attr); + } + + if ($resource) { + $this->resource = $resource; + } else { + $this->resource = common_config('xmpp', 'resource') . 'daemon'; + } + + $this->log(LOG_INFO, "INITIALIZE XMPPDaemon {$this->user}@{$this->server}/{$this->resource}"); + } + + function connect() + { + + $connect_to = ($this->host) ? $this->host : $this->server; + + $this->log(LOG_INFO, "Connecting to $connect_to on port $this->port"); + + $this->conn = jabber_connect($this->resource); + + if (!$this->conn) { + return false; + } + + $this->conn->setReconnectTimeout(600); + + jabber_send_presence("Send me a message to post a notice", 'available', + null, 'available', 100); + return !$this->conn->isDisconnected(); + } + + function name() + { + return strtolower('xmppdaemon.'.$this->resource); + } + + function run() + { + if ($this->connect()) { + + $this->conn->addEventHandler('message', 'handle_message', $this); + $this->conn->addEventHandler('presence', 'handle_presence', $this); + $this->conn->addEventHandler('reconnect', 'handle_reconnect', $this); + + $this->conn->process(); + } + } + + function handle_reconnect(&$pl) + { + $this->conn->processUntil('session_start'); + $this->conn->presence('Send me a message to post a notice', 'available', null, 'available', 100); + } + + function get_user($from) + { + $user = User::staticGet('jabber', jabber_normalize_jid($from)); + return $user; + } + + function handle_message(&$pl) + { + if ($pl['type'] != 'chat') { + return; + } + if (mb_strlen($pl['body']) == 0) { + return; + } + + $from = jabber_normalize_jid($pl['from']); + + # Forwarded from another daemon (probably a broadcaster) for + # us to handle + + if ($this->is_self($from)) { + $from = $this->get_ofrom($pl); + if (is_null($from) || $this->is_self($from)) { + return; + } + } + + $user = $this->get_user($from); + + if (!$user) { + $this->from_site($from, 'Unknown user; go to ' . + common_local_url('imsettings') . + ' to add your address to your account'); + $this->log(LOG_WARNING, 'Message from unknown user ' . $from); + return; + } + if ($this->handle_command($user, $pl['body'])) { + return; + } else if ($this->is_autoreply($pl['body'])) { + $this->log(LOG_INFO, 'Ignoring auto reply from ' . $from); + return; + } else if ($this->is_otr($pl['body'])) { + $this->log(LOG_INFO, 'Ignoring OTR from ' . $from); + return; + } else if ($this->is_direct($pl['body'])) { + preg_match_all('/d[\ ]*([a-z0-9]{1,64})/', $pl['body'], $to); + + $to = preg_replace('/^d([\ ])*/', '', $to[0][0]); + $body = preg_replace('/d[\ ]*('. $to .')[\ ]*/', '', $pl['body']); + $this->add_direct($user, $body, $to, $from); + } else { + $len = mb_strlen($pl['body']); + if($len > 140) { + $this->from_site($from, 'Message too long - maximum is 140 characters, you sent ' . $len); + return; + } + $this->add_notice($user, $pl); + } + + $user->free(); + unset($user); + } + + function is_self($from) + { + return preg_match('/^'.strtolower(jabber_daemon_address()).'/', strtolower($from)); + } + + function get_ofrom($pl) + { + $xml = $pl['xml']; + $addresses = $xml->sub('addresses'); + if (!$addresses) { + $this->log(LOG_WARNING, 'Forwarded message without addresses'); + return null; + } + $address = $addresses->sub('address'); + if (!$address) { + $this->log(LOG_WARNING, 'Forwarded message without address'); + return null; + } + if (!array_key_exists('type', $address->attrs)) { + $this->log(LOG_WARNING, 'No type for forwarded message'); + return null; + } + $type = $address->attrs['type']; + if ($type != 'ofrom') { + $this->log(LOG_WARNING, 'Type of forwarded message is not ofrom'); + return null; + } + if (!array_key_exists('jid', $address->attrs)) { + $this->log(LOG_WARNING, 'No jid for forwarded message'); + return null; + } + $jid = $address->attrs['jid']; + if (!$jid) { + $this->log(LOG_WARNING, 'Could not get jid from address'); + return null; + } + $this->log(LOG_DEBUG, 'Got message forwarded from jid ' . $jid); + return $jid; + } + + function is_autoreply($txt) + { + if (preg_match('/[\[\(]?[Aa]uto[-\s]?[Rr]e(ply|sponse)[\]\)]/', $txt)) { + return true; + } else { + return false; + } + } + + function is_otr($txt) + { + if (preg_match('/^\?OTR/', $txt)) { + return true; + } else { + return false; + } + } + + function is_direct($txt) + { + if (strtolower(substr($txt, 0, 2))=='d ') { + return true; + } else { + return false; + } + } + + function from_site($address, $msg) + { + $text = '['.common_config('site', 'name') . '] ' . $msg; + jabber_send_message($address, $text); + } + + function handle_command($user, $body) + { + $inter = new CommandInterpreter(); + $cmd = $inter->handle_command($user, $body); + if ($cmd) { + $chan = new XMPPChannel($this->conn); + $cmd->execute($chan); + return true; + } else { + return false; + } + } + + function add_notice(&$user, &$pl) + { $body = trim($pl['body']); $content_shortened = common_shorten_link($body); if (mb_strlen($content_shortened) > 140) { @@ -245,59 +261,62 @@ class XMPPDaemon extends Daemon { else { $content = $body; } - $notice = Notice::saveNew($user->id, $content, 'xmpp'); - if (is_string($notice)) { - $this->log(LOG_ERR, $notice); - return; - } - common_broadcast_notice($notice); - $this->log(LOG_INFO, - 'Added notice ' . $notice->id . ' from user ' . $user->nickname); - $notice->free(); - unset($notice); - } - - function handle_presence(&$pl) { - $from = jabber_normalize_jid($pl['from']); - switch ($pl['type']) { - case 'subscribe': - # We let anyone subscribe - $this->subscribed($from); - $this->log(LOG_INFO, - 'Accepted subscription from ' . $from); - break; - case 'subscribed': - case 'unsubscribed': - case 'unsubscribe': - $this->log(LOG_INFO, - 'Ignoring "' . $pl['type'] . '" from ' . $from); - break; - default: - if (!$pl['type']) { - $user = User::staticGet('jabber', $from); - if (!$user) { - $this->log(LOG_WARNING, 'Presence from unknown user ' . $from); - return; - } - if ($user->updatefrompresence) { - $this->log(LOG_INFO, 'Updating ' . $user->nickname . - ' status from presence.'); - $this->add_notice($user, $pl); - } - $user->free(); - unset($user); - } - break; - } - } - - function log($level, $msg) { - common_log($level, 'XMPPDaemon('.$this->resource.'): '.$msg); - } - - function subscribed($to) { - jabber_special_presence('subscribed', $to); - } + $notice = Notice::saveNew($user->id, $content, 'xmpp'); + if (is_string($notice)) { + $this->log(LOG_ERR, $notice); + return; + } + common_broadcast_notice($notice); + $this->log(LOG_INFO, + 'Added notice ' . $notice->id . ' from user ' . $user->nickname); + $notice->free(); + unset($notice); + } + + function handle_presence(&$pl) + { + $from = jabber_normalize_jid($pl['from']); + switch ($pl['type']) { + case 'subscribe': + # We let anyone subscribe + $this->subscribed($from); + $this->log(LOG_INFO, + 'Accepted subscription from ' . $from); + break; + case 'subscribed': + case 'unsubscribed': + case 'unsubscribe': + $this->log(LOG_INFO, + 'Ignoring "' . $pl['type'] . '" from ' . $from); + break; + default: + if (!$pl['type']) { + $user = User::staticGet('jabber', $from); + if (!$user) { + $this->log(LOG_WARNING, 'Presence from unknown user ' . $from); + return; + } + if ($user->updatefrompresence) { + $this->log(LOG_INFO, 'Updating ' . $user->nickname . + ' status from presence.'); + $this->add_notice($user, $pl); + } + $user->free(); + unset($user); + } + break; + } + } + + function log($level, $msg) + { + common_log($level, 'XMPPDaemon('.$this->resource.'): '.$msg); + } + + function subscribed($to) + { + jabber_special_presence('subscribed', $to); + } } ini_set("max_execution_time", "0"); diff --git a/theme/default/display.css b/theme/default/display.css index 0b894550c..f22f9aa69 100644 --- a/theme/default/display.css +++ b/theme/default/display.css @@ -440,6 +440,32 @@ p.time a { float: left; margin: 0 10px 18px 0; } + +#profilesettings { + margin-bottom:2em; +} + + +.avatar_view { + float:left; + margin-bottom:1em; + margin-right:1em; +} + +#avatar_preview_view { + overflow:hidden; + width:96px; + height:96px; +} +#avatar_crop { + margin-bottom:2em; +} + +#avatar_crop, +#avatarfile { + clear:both; +} + a.nickname { font-family: Verdana, Arial, Helvetica, sans-serif; font-weight: bold; |