From 5c091dab9be50d6955755c1420c09070ea2da7b6 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Sun, 8 Nov 2009 15:29:04 -0800 Subject: [PATCH 1/8] Implement /api/account/update_profile_background_image.format --- ...apiaccountupdateprofilebackgroundimage.php | 170 ++++++++++++++++++ lib/router.php | 3 + 2 files changed, 173 insertions(+) create mode 100644 actions/apiaccountupdateprofilebackgroundimage.php diff --git a/actions/apiaccountupdateprofilebackgroundimage.php b/actions/apiaccountupdateprofilebackgroundimage.php new file mode 100644 index 0000000000..26d55d448b --- /dev/null +++ b/actions/apiaccountupdateprofilebackgroundimage.php @@ -0,0 +1,170 @@ +. + * + * @category API + * @package StatusNet + * @author Zach Copley + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/apiauth.php'; + +/** + * Update the authenticating user's profile background image + * + * @category API + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction +{ + + var $tile = false; + + /** + * Take arguments for running + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + * + */ + + function prepare($args) + { + parent::prepare($args); + + $this->user = $this->auth_user; + $this->tile = $this->arg('tile'); + + return true; + } + + /** + * Handle the request + * + * Check whether the credentials are valid and output the result + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + $this->clientError( + _('This method requires a POST.'), + 400, $this->format + ); + return; + } + + // Workaround for PHP returning empty $_POST and $_FILES when POST + // length > post_max_size in php.ini + + if (empty($_FILES) + && empty($_POST) + && ($_SERVER['CONTENT_LENGTH'] > 0) + ) { + $msg = _('The server was unable to handle that much POST ' . + 'data (%s bytes) due to its current configuration.'); + + $this->clientError(sprintf($msg, $_SERVER['CONTENT_LENGTH'])); + return; + } + + if (empty($this->user)) { + $this->clientError(_('No such user!'), 404, $this->format); + return; + } + + try { + $imagefile = ImageFile::fromUpload('image'); + } catch (Exception $e) { + $this->clientError($e->getMessage(), 400, $this->format); + return; + } + + $design = $this->user->getDesign(); + + $filename = Design::filename( + $design->id, + image_type_to_extension($imagefile->type), + common_timestamp() + ); + + $filepath = Design::path($filename); + + move_uploaded_file($imagefile->filepath, $filepath); + + // delete any old backround img laying around + + if (isset($design->backgroundimage)) { + @unlink(Design::path($design->backgroundimage)); + } + + $original = clone($design); + + $design->backgroundimage = $filename; + + $design->setDisposition(true, false, !empty($this->tile)); + + $result = $design->update($original); + + if ($result === false) { + common_log_db_error($design, 'UPDATE', __FILE__); + $this->showForm(_('Couldn\'t update your design.')); + return; + } + + $profile = $this->user->getProfile(); + + if (empty($profile)) { + $this->clientError(_('User has no profile.')); + return; + } + + $twitter_user = $this->twitterUserArray($this->user->getProfile(), true); + + if ($this->format == 'xml') { + $this->initDocument('xml'); + $this->showTwitterXmlUser($twitter_user); + $this->endDocument('xml'); + } elseif ($this->format == 'json') { + $this->initDocument('json'); + $this->showJsonObjects($twitter_user); + $this->endDocument('json'); + } + } + +} diff --git a/lib/router.php b/lib/router.php index db9fdb4704..d47828d1c1 100644 --- a/lib/router.php +++ b/lib/router.php @@ -431,6 +431,9 @@ class Router $m->connect('api/account/update_profile_image.:format', array('action' => 'ApiAccountUpdateProfileImage')); + $m->connect('api/account/update_profile_background_image.:format', + array('action' => 'ApiAccountUpdateProfileBackgroundImage')); + // special case where verify_credentials is called w/out a format $m->connect('api/account/verify_credentials', From 322a4c3ed1536eede0607112116ee46d592fc9a9 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 9 Nov 2009 22:06:22 -0800 Subject: [PATCH 2/8] Implement /api/account/update_profile_colors.format --- actions/apiaccountupdateprofilecolors.php | 237 ++++++++++++++++++++++ lib/router.php | 3 + 2 files changed, 240 insertions(+) create mode 100644 actions/apiaccountupdateprofilecolors.php diff --git a/actions/apiaccountupdateprofilecolors.php b/actions/apiaccountupdateprofilecolors.php new file mode 100644 index 0000000000..d7d2161fea --- /dev/null +++ b/actions/apiaccountupdateprofilecolors.php @@ -0,0 +1,237 @@ +. + * + * @category API + * @package StatusNet + * @author Zach Copley + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/apiauth.php'; + +/** + * Sets one or more hex values that control the color scheme of the + * authenticating user's design + * + * @category API + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class ApiAccountUpdateProfileColorsAction extends ApiAuthAction +{ + + var $profile_background_color = null; + var $profile_text_color = null; + var $profile_link_color = null; + var $profile_sidebar_fill_color = null; + var $profile_sidebar_border_color = null; + + /** + * Take arguments for running + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + * + */ + + function prepare($args) + { + parent::prepare($args); + + $this->user = $this->auth_user; + + $this->profile_background_color + = $this->trimmed('profile_background_color'); + $this->profile_text_color + = $this->trimmed('profile_text_color'); + $this->profile_link_color + = $this->trimmed('profile_link_color'); + $this->profile_sidebar_fill_color + = $this->trimmed('profile_sidebar_fill_color'); + + // XXX: we don't support changing the sidebar border color + // in our designs. + + $this->profile_sidebar_border_color + = $this->trimmed('profile_sidebar_border_color'); + + // XXX: Unlike Twitter, we do allow people to change the 'content color' + + $this->profile_content_color = $this->trimmed('profile_content_color'); + + return true; + } + + /** + * Handle the request + * + * Try to save the user's colors in her design. Create a new design + * if the user doesn't already have one. + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + $this->clientError( + _('This method requires a POST.'), + 400, $this->format + ); + return; + } + + $design = $this->user->getDesign(); + + if (!empty($design)) { + + $original = clone($design); + + try { + $this->setColors($design); + } catch (WebColorException $e) { + $this->clientError($e->getMessage()); + return false; + } + + $result = $design->update($original); + + if ($result === false) { + common_log_db_error($design, 'UPDATE', __FILE__); + $this->clientError(_('Couldn\'t update your design.')); + return; + } + + } else { + + $this->user->query('BEGIN'); + + // save new design + $design = new Design(); + + try { + $this->setColors($design); + } catch (WebColorException $e) { + $this->clientError($e->getMessage()); + return false; + } + + $id = $design->insert(); + + if (empty($id)) { + common_log_db_error($id, 'INSERT', __FILE__); + $this->clientError(_('Unable to save your design settings!')); + return; + } + + $original = clone($this->user); + $this->user->design_id = $id; + $result = $this->user->update($original); + + if (empty($result)) { + common_log_db_error($original, 'UPDATE', __FILE__); + $this->clientError(_('Unable to save your design settings!')); + $this->user->query('ROLLBACK'); + return; + } + + $this->user->query('COMMIT'); + } + + $profile = $this->user->getProfile(); + + if (empty($profile)) { + $this->clientError(_('User has no profile.')); + return; + } + + $twitter_user = $this->twitterUserArray($this->user->getProfile(), true); + + if ($this->format == 'xml') { + $this->initDocument('xml'); + $this->showTwitterXmlUser($twitter_user); + $this->endDocument('xml'); + } elseif ($this->format == 'json') { + $this->initDocument('json'); + $this->showJsonObjects($twitter_user); + $this->endDocument('json'); + } + } + + /** + * Sets the user's design colors based on the request parameters + * + * @param Design $design the user's Design + * + * @return void + */ + + function setColors($design) + { + $bgcolor = empty($this->profile_background_color) ? + null : new WebColor($this->profile_background_color); + $tcolor = empty($this->profile_text_color) ? + null : new WebColor($this->profile_text_color); + $sbcolor = empty($this->profile_sidebar_fill_color) ? + null : new WebColor($this->profile_sidebar_fill_color); + $lcolor = empty($this->profile_link_color) ? + null : new WebColor($this->profile_link_color); + $ccolor = empty($this->profile_content_color) ? + null : new WebColor($this->profile_content_color); + + if (!empty($bgcolor)) { + $design->backgroundcolor = $bgcolor->intValue(); + } + + if (!empty($ccolor)) { + $design->contentcolor = $ccolor->intValue(); + } + + if (!empty($sbcolor)) { + $design->sidebarcolor = $sbcolor->intValue(); + } + + if (!empty($tcolor)) { + $design->textcolor = $tcolor->intValue(); + } + + if (!empty($lcolor)) { + $design->linkcolor = $lcolor->intValue(); + } + + return true; + } + +} diff --git a/lib/router.php b/lib/router.php index d47828d1c1..03f6036b22 100644 --- a/lib/router.php +++ b/lib/router.php @@ -434,6 +434,9 @@ class Router $m->connect('api/account/update_profile_background_image.:format', array('action' => 'ApiAccountUpdateProfileBackgroundImage')); + $m->connect('api/account/update_profile_colors.:format', + array('action' => 'ApiAccountUpdateProfileColors')); + // special case where verify_credentials is called w/out a format $m->connect('api/account/verify_credentials', From 312c745884654a461ed6da22eebe78f07f500426 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 9 Nov 2009 23:13:59 -0800 Subject: [PATCH 3/8] Implement /api/account/update_profile.format --- actions/apiaccountupdateprofile.php | 157 ++++++++++++++++++++++++++++ lib/router.php | 3 + 2 files changed, 160 insertions(+) create mode 100644 actions/apiaccountupdateprofile.php diff --git a/actions/apiaccountupdateprofile.php b/actions/apiaccountupdateprofile.php new file mode 100644 index 0000000000..8af0fb8660 --- /dev/null +++ b/actions/apiaccountupdateprofile.php @@ -0,0 +1,157 @@ +. + * + * @category API + * @package StatusNet + * @author Zach Copley + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/apiauth.php'; + +/** + * API analog to the profile settings page + * Only the parameters specified will be updated. + * + * @category API + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class ApiAccountUpdateProfileAction extends ApiAuthAction +{ + + /** + * Take arguments for running + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + * + */ + + function prepare($args) + { + parent::prepare($args); + + $this->user = $this->auth_user; + + $this->name = $this->trimmed('name'); + $this->url = $this->trimmed('url'); + $this->location = $this->trimmed('location'); + $this->description = $this->trimmed('description'); + + return true; + } + + /** + * Handle the request + * + * See which request params have been set, and update the profile + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + $this->clientError( + _('This method requires a POST.'), + 400, $this->format + ); + return; + } + + if (empty($this->user)) { + $this->clientError(_('No such user.'), 404, $this->format); + return; + } + + $profile = $this->user->getProfile(); + + if (empty($profile)) { + $this->clientError(_('User has no profile.')); + return; + } + + $original = clone($profile); + + if (empty($this->name)) { + $profile->fullname = $this->name; + } + + if (empty($this->url)) { + $profile->homepage = $this->url; + } + + if (!empty($this->description)) { + $profile->bio = $this->description; + } + + if (!empty($this->location)) { + $profile->location = $this->location; + + $loc = Location::fromName($location); + + if (!empty($loc)) { + $profile->lat = $loc->lat; + $profile->lon = $loc->lon; + $profile->location_id = $loc->location_id; + $profile->location_ns = $loc->location_ns; + } + } + + $result = $profile->update($original); + + if (!$result) { + common_log_db_error($profile, 'UPDATE', __FILE__); + $this->serverError(_('Couldn\'t save profile.')); + return; + } + + common_broadcast_profile($profile); + + $twitter_user = $this->twitterUserArray($profile, true); + + if ($this->format == 'xml') { + $this->initDocument('xml'); + $this->showTwitterXmlUser($twitter_user); + $this->endDocument('xml'); + } elseif ($this->format == 'json') { + $this->initDocument('json'); + $this->showJsonObjects($twitter_user); + $this->endDocument('json'); + } + } + +} diff --git a/lib/router.php b/lib/router.php index 03f6036b22..35f43e5cb7 100644 --- a/lib/router.php +++ b/lib/router.php @@ -428,6 +428,9 @@ class Router $m->connect('api/account/verify_credentials.:format', array('action' => 'ApiAccountVerifyCredentials')); + $m->connect('api/account/update_profile.:format', + array('action' => 'ApiAccountUpdateProfile')); + $m->connect('api/account/update_profile_image.:format', array('action' => 'ApiAccountUpdateProfileImage')); From c8bd6d9f7afde597f3b404acb05622a73f3738f7 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 9 Nov 2009 23:56:02 -0800 Subject: [PATCH 4/8] Make /api/account/update_profile_background_image.format work even when there isn't an existing Design for the user. Plus a few other fixups. --- actions/apiaccountratelimitstatus.php | 2 +- actions/apiaccountupdateprofile.php | 11 +++- ...apiaccountupdateprofilebackgroundimage.php | 57 ++++++++++++++++--- actions/apiaccountupdateprofilecolors.php | 17 ++++-- actions/apiaccountupdateprofileimage.php | 4 +- 5 files changed, 75 insertions(+), 16 deletions(-) diff --git a/actions/apiaccountratelimitstatus.php b/actions/apiaccountratelimitstatus.php index 96179f175b..1a5afd552c 100644 --- a/actions/apiaccountratelimitstatus.php +++ b/actions/apiaccountratelimitstatus.php @@ -67,7 +67,7 @@ class ApiAccountRateLimitStatusAction extends ApiBareAuthAction if (!in_array($this->format, array('xml', 'json'))) { $this->clientError( - _('API method not found!'), + _('API method not found.'), 404, $this->format ); diff --git a/actions/apiaccountupdateprofile.php b/actions/apiaccountupdateprofile.php index 8af0fb8660..fd4384a25c 100644 --- a/actions/apiaccountupdateprofile.php +++ b/actions/apiaccountupdateprofile.php @@ -92,6 +92,15 @@ class ApiAccountUpdateProfileAction extends ApiAuthAction return; } + if (!in_array($this->format, array('xml', 'json'))) { + $this->clientError( + _('API method not found.'), + 404, + $this->format + ); + return; + } + if (empty($this->user)) { $this->clientError(_('No such user.'), 404, $this->format); return; @@ -135,7 +144,7 @@ class ApiAccountUpdateProfileAction extends ApiAuthAction if (!$result) { common_log_db_error($profile, 'UPDATE', __FILE__); - $this->serverError(_('Couldn\'t save profile.')); + $this->serverError(_('Could not save profile.')); return; } diff --git a/actions/apiaccountupdateprofilebackgroundimage.php b/actions/apiaccountupdateprofilebackgroundimage.php index 26d55d448b..3537b9f979 100644 --- a/actions/apiaccountupdateprofilebackgroundimage.php +++ b/actions/apiaccountupdateprofilebackgroundimage.php @@ -89,6 +89,15 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction return; } + if (!in_array($this->format, array('xml', 'json'))) { + $this->clientError( + _('API method not found.'), + 404, + $this->format + ); + return; + } + // Workaround for PHP returning empty $_POST and $_FILES when POST // length > post_max_size in php.ini @@ -104,10 +113,46 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction } if (empty($this->user)) { - $this->clientError(_('No such user!'), 404, $this->format); + $this->clientError(_('No such user.'), 404, $this->format); return; } + $design = $this->user->getDesign(); + + // XXX: This is kinda gross, but before we can add a background + // img we have to make sure there's a Design because design ID + // is part of the img filename. + + if (empty($design)) { + + $this->user->query('BEGIN'); + + // save new design + $design = new Design(); + $id = $design->insert(); + + if (empty($id)) { + common_log_db_error($id, 'INSERT', __FILE__); + $this->clientError(_('Unable to save your design settings.')); + return; + } + + $original = clone($this->user); + $this->user->design_id = $id; + $result = $this->user->update($original); + + if (empty($result)) { + common_log_db_error($original, 'UPDATE', __FILE__); + $this->clientError(_('Unable to save your design settings.')); + $this->user->query('ROLLBACK'); + return; + } + + $this->user->query('COMMIT'); + } + + // Okay, now get the image and add it to the design + try { $imagefile = ImageFile::fromUpload('image'); } catch (Exception $e) { @@ -115,8 +160,6 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction return; } - $design = $this->user->getDesign(); - $filename = Design::filename( $design->id, image_type_to_extension($imagefile->type), @@ -134,16 +177,14 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction } $original = clone($design); - $design->backgroundimage = $filename; - - $design->setDisposition(true, false, !empty($this->tile)); + $design->setDisposition(true, false, ($this->tile == 'true')); $result = $design->update($original); if ($result === false) { common_log_db_error($design, 'UPDATE', __FILE__); - $this->showForm(_('Couldn\'t update your design.')); + $this->showForm(_('Could not update your design.')); return; } @@ -154,7 +195,7 @@ class ApiAccountUpdateProfileBackgroundImageAction extends ApiAuthAction return; } - $twitter_user = $this->twitterUserArray($this->user->getProfile(), true); + $twitter_user = $this->twitterUserArray($profile, true); if ($this->format == 'xml') { $this->initDocument('xml'); diff --git a/actions/apiaccountupdateprofilecolors.php b/actions/apiaccountupdateprofilecolors.php index d7d2161fea..3cac829749 100644 --- a/actions/apiaccountupdateprofilecolors.php +++ b/actions/apiaccountupdateprofilecolors.php @@ -113,6 +113,15 @@ class ApiAccountUpdateProfileColorsAction extends ApiAuthAction return; } + if (!in_array($this->format, array('xml', 'json'))) { + $this->clientError( + _('API method not found.'), + 404, + $this->format + ); + return; + } + $design = $this->user->getDesign(); if (!empty($design)) { @@ -130,7 +139,7 @@ class ApiAccountUpdateProfileColorsAction extends ApiAuthAction if ($result === false) { common_log_db_error($design, 'UPDATE', __FILE__); - $this->clientError(_('Couldn\'t update your design.')); + $this->clientError(_('Could not update your design.')); return; } @@ -152,7 +161,7 @@ class ApiAccountUpdateProfileColorsAction extends ApiAuthAction if (empty($id)) { common_log_db_error($id, 'INSERT', __FILE__); - $this->clientError(_('Unable to save your design settings!')); + $this->clientError(_('Unable to save your design settings.')); return; } @@ -162,7 +171,7 @@ class ApiAccountUpdateProfileColorsAction extends ApiAuthAction if (empty($result)) { common_log_db_error($original, 'UPDATE', __FILE__); - $this->clientError(_('Unable to save your design settings!')); + $this->clientError(_('Unable to save your design settings.')); $this->user->query('ROLLBACK'); return; } @@ -177,7 +186,7 @@ class ApiAccountUpdateProfileColorsAction extends ApiAuthAction return; } - $twitter_user = $this->twitterUserArray($this->user->getProfile(), true); + $twitter_user = $this->twitterUserArray($profile, true); if ($this->format == 'xml') { $this->initDocument('xml'); diff --git a/actions/apiaccountupdateprofileimage.php b/actions/apiaccountupdateprofileimage.php index 72fb361bf8..153ef7818e 100644 --- a/actions/apiaccountupdateprofileimage.php +++ b/actions/apiaccountupdateprofileimage.php @@ -102,7 +102,7 @@ class ApiAccountUpdateProfileImageAction extends ApiAuthAction } if (empty($this->user)) { - $this->clientError(_('No such user!'), 404, $this->format); + $this->clientError(_('No such user.'), 404, $this->format); return; } @@ -135,7 +135,7 @@ class ApiAccountUpdateProfileImageAction extends ApiAuthAction common_broadcast_profile($profile); - $twitter_user = $this->twitterUserArray($this->user->getProfile(), true); + $twitter_user = $this->twitterUserArray($profile, true); if ($this->format == 'xml') { $this->initDocument('xml'); From dbb86f948684cd5a5a49f6881f50082698fd39d1 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 10 Nov 2009 00:30:56 -0800 Subject: [PATCH 5/8] Output profile background image info in the API user objects --- lib/api.php | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/api.php b/lib/api.php index a1236ab7ec..45762ef0d3 100644 --- a/lib/api.php +++ b/lib/api.php @@ -177,9 +177,14 @@ class ApiAction extends Action $twitter_user['utc_offset'] = $t->format('Z'); $twitter_user['time_zone'] = $timezone; - // To be supported some day, perhaps - $twitter_user['profile_background_image_url'] = ''; - $twitter_user['profile_background_tile'] = false; + $twitter_user['profile_background_image_url'] + = empty($design->backgroundimage) + ? '' : ($design->disposition & BACKGROUND_ON) + ? Design::url($design->backgroundimage) : ''; + + $twitter_user['profile_background_tile'] + = empty($design->disposition) + ? '' : ($design->disposition & BACKGROUND_TILE) ? 'true' : 'false'; $twitter_user['statuses_count'] = $profile->noticeCount(); From 4e4ba24c007fee94662018e7135f2db1e7c847b3 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 10 Nov 2009 14:36:41 -0800 Subject: [PATCH 6/8] Implement /api/account/update_delivery_device.format --- actions/apiaccountupdatedeliverydevice.php | 149 +++++++++++++++++++++ lib/router.php | 3 + 2 files changed, 152 insertions(+) create mode 100644 actions/apiaccountupdatedeliverydevice.php diff --git a/actions/apiaccountupdatedeliverydevice.php b/actions/apiaccountupdatedeliverydevice.php new file mode 100644 index 0000000000..5b7176bbbe --- /dev/null +++ b/actions/apiaccountupdatedeliverydevice.php @@ -0,0 +1,149 @@ +. + * + * @category API + * @package StatusNet + * @author Zach Copley + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +require_once INSTALLDIR . '/lib/apiauth.php'; + +/** + * Sets which channel (device) StatusNet delivers updates to for + * the authenticating user. Sending none as the device parameter + * will disable IM and/or SMS updates. + * + * @category API + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction +{ + /** + * Take arguments for running + * + * @param array $args $_REQUEST args + * + * @return boolean success flag + * + */ + + function prepare($args) + { + parent::prepare($args); + + $this->user = $this->auth_user; + $this->device = $this->trimmed('device'); + + return true; + } + + /** + * Handle the request + * + * See which request params have been set, and update the user settings + * + * @param array $args $_REQUEST data (unused) + * + * @return void + */ + + function handle($args) + { + parent::handle($args); + + if ($_SERVER['REQUEST_METHOD'] != 'POST') { + $this->clientError( + _('This method requires a POST.'), + 400, $this->format + ); + return; + } + + if (!in_array($this->format, array('xml', 'json'))) { + $this->clientError( + _('API method not found.'), + 404, + $this->format + ); + return; + } + + if (!in_array(strtolower($this->device), array('sms', 'im', 'none'))) { + $this->clientError( + _( + 'You must specify a parameter named ' . + '\'device\' with a value of one of: sms, im, none' + ) + ); + return; + } + + if (empty($this->user)) { + $this->clientError(_('No such user.'), 404, $this->format); + return; + } + + $original = clone($this->user); + + if (strtolower($this->device) == 'sms') { + $this->user->smsnotify = true; + } elseif (strtolower($this->device) == 'im') { + $this->user->jabbernotify = true; + } elseif (strtolower($this->device == 'none')) { + $this->user->smsnotify = false; + $this->user->jabbernotify = false; + } + + $result = $this->user->update($original); + + if ($result === false) { + common_log_db_error($this->user, 'UPDATE', __FILE__); + $this->serverError(_('Could not update user.')); + return; + } + + $profile = $this->user->getProfile(); + + $twitter_user = $this->twitterUserArray($profile, true); + + if ($this->format == 'xml') { + $this->initDocument('xml'); + $this->showTwitterXmlUser($twitter_user); + $this->endDocument('xml'); + } elseif ($this->format == 'json') { + $this->initDocument('json'); + $this->showJsonObjects($twitter_user); + $this->endDocument('json'); + } + } + +} diff --git a/lib/router.php b/lib/router.php index 35f43e5cb7..bad3decad5 100644 --- a/lib/router.php +++ b/lib/router.php @@ -440,6 +440,9 @@ class Router $m->connect('api/account/update_profile_colors.:format', array('action' => 'ApiAccountUpdateProfileColors')); + $m->connect('api/account/update_delivery_device.:format', + array('action' => 'ApiAccountUpdateDeliveryDevice')); + // special case where verify_credentials is called w/out a format $m->connect('api/account/verify_credentials', From 91332cdadc20e721c22fcf22ca1773cedbde95c5 Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Tue, 10 Nov 2009 17:54:24 -0500 Subject: [PATCH 7/8] Added a events for the settings menu items --- EVENTS.txt | 40 +++++++++++++++++-- lib/accountsettingsaction.php | 57 +++++++++++++------------- plugins/Auth/AuthPlugin.php | 75 ++++++++++++++++++++++++----------- plugins/Ldap/LdapPlugin.php | 13 +++++- plugins/Ldap/README | 11 +++-- 5 files changed, 136 insertions(+), 60 deletions(-) diff --git a/EVENTS.txt b/EVENTS.txt index 97b7de299f..f75dcebca6 100644 --- a/EVENTS.txt +++ b/EVENTS.txt @@ -162,6 +162,42 @@ StartAccountSettingsNav: Before showing the account settings menu EndAccountSettingsNav: After showing the account settings menu - $action: the current action +StartAccountSettingsProfileMenuItem: Before showing the Profile menu item +- $widget: AccountSettingsNav instance being shown + +EndAccountSettingsProfileMenuItem: After showing the Profile menu item +- $widget: AccountSettingsNav instance being shown + +StartAccountSettingsAvatarMenuItem: Before showing the Avatar menu item +- $widget: AccountSettingsNav instance being shown + +EndAccountSettingsAvatarMenuItem: After showing the Avatar menu item +- $widget: AccountSettingsNav instance being shown + +StartAccountSettingsPasswordMenuItem: Before showing the Password menu item +- $widget: AccountSettingsNav instance being shown + +EndAccountSettingsPasswordMenuItem: After showing the Password menu item +- $widget: AccountSettingsNav instance being shown + +StartAccountSettingsEmailMenuItem: Before showing the Email menu item +- $widget: AccountSettingsNav instance being shown + +EndAccountSettingsEmailMenuItem: After showing the Email menu item +- $widget: AccountSettingsNav instance being shown + +StartAccountSettingsDesignMenuItem: Before showing the Design menu item +- $widget: AccountSettingsNav instance being shown + +EndAccountSettingsDesignMenuItem: After showing the Design menu item +- $widget: AccountSettingsNav instance being shown + +StartAccountSettingsOtherMenuItem: Before showing the Other menu item +- $widget: AccountSettingsNav instance being shown + +EndAccountSettingsOtherMenuItem: After showing the Other menu item +- $widget: AccountSettingsNav instance being shown + Autoload: When trying to autoload a class - $cls: the class being sought. A plugin might require_once the file for the class. @@ -499,10 +535,6 @@ StartChangePassword: Before changing a password EndChangePassword: After changing a password - $nickname: user's nickname -CanUserChangeField: Determines if a user is allowed to change a specific profile field -- $nickname: nickname of the user who would like to know which of their profile fields are mutable -- $field: name of the field the user wants to change (nickname, fullname, password, avatar, etc) - UserDeleteRelated: Specify additional tables to delete entries from when deleting users - $user: User object - &$related: array of DB_DataObject class names to delete entries on matching user_id. diff --git a/lib/accountsettingsaction.php b/lib/accountsettingsaction.php index 9865e17489..c79a1f5d77 100644 --- a/lib/accountsettingsaction.php +++ b/lib/accountsettingsaction.php @@ -104,35 +104,29 @@ class AccountSettingsNav extends Widget if (Event::handle('StartAccountSettingsNav', array(&$this->action))) { $user = common_current_user(); - $menu = array(); - $menu['profilesettings'] = - array(_('Profile'), - _('Change your profile settings')); - if(Event::handle('CanUserChangeField', array($user->nickname, 'avatar'))){ - $menu['avatarsettings'] = - array(_('Avatar'), - _('Upload an avatar')); + if(Event::handle('StartAccountSettingsProfileMenuItem', array($this, &$menu))){ + $this->showMenuItem('profilesettings',_('Profile'),_('Change your profile settings')); + Event::handle('EndAccountSettingsProfileMenuItem', array($this, &$menu)); } - if(Event::handle('CanUserChangeField', array($user->nickname, 'password'))){ - $menu['passwordsettings'] = - array(_('Password'), - _('Change your password')); + if(Event::handle('StartAccountSettingsAvatarMenuItem', array($this, &$menu))){ + $this->showMenuItem('avatarsettings',_('Avatar'),_('Upload an avatar')); + Event::handle('EndAccountSettingsAvatarMenuItem', array($this, &$menu)); } - $menu['emailsettings'] = - array(_('Email'), - _('Change email handling')); - $menu['userdesignsettings'] = - array(_('Design'), - _('Design your profile')); - $menu['othersettings'] = - array(_('Other'), - _('Other options')); - - foreach ($menu as $menuaction => $menudesc) { - $this->action->menuItem(common_local_url($menuaction), - $menudesc[0], - $menudesc[1], - $action_name === $menuaction); + if(Event::handle('StartAccountSettingsPasswordMenuItem', array($this, &$menu))){ + $this->showMenuItem('passwordsettings',_('Password'),_('Change your password')); + Event::handle('EndAccountSettingsPasswordMenuItem', array($this, &$menu)); + } + if(Event::handle('StartAccountSettingsEmailMenuItem', array($this, &$menu))){ + $this->showMenuItem('emailsettings',_('Email'),_('Change email handling')); + Event::handle('EndAccountSettingsEmailMenuItem', array($this, &$menu)); + } + if(Event::handle('StartAccountSettingsDesignMenuItem', array($this, &$menu))){ + $this->showMenuItem('userdesignsettings',_('Design'),_('Design your profile')); + Event::handle('EndAccountSettingsDesignMenuItem', array($this, &$menu)); + } + if(Event::handle('StartAccountSettingsOtherMenuItem', array($this, &$menu))){ + $this->showMenuItem('othersettings',_('Other'),_('Other options')); + Event::handle('EndAccountSettingsOtherMenuItem', array($this, &$menu)); } Event::handle('EndAccountSettingsNav', array(&$this->action)); @@ -140,4 +134,13 @@ class AccountSettingsNav extends Widget $this->action->elementEnd('ul'); } + + function showMenuItem($menuaction, $desc1, $desc2) + { + $action_name = $this->action->trimmed('action'); + $this->action->menuItem(common_local_url($menuaction), + $desc1, + $desc2, + $action_name === $menuaction); + } } diff --git a/plugins/Auth/AuthPlugin.php b/plugins/Auth/AuthPlugin.php index 71e7ae4fbc..cb52730f67 100644 --- a/plugins/Auth/AuthPlugin.php +++ b/plugins/Auth/AuthPlugin.php @@ -43,11 +43,17 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { abstract class AuthPlugin extends Plugin { //is this plugin authoritative for authentication? - protected $authn_authoritative = false; + public $authn_authoritative = false; //should accounts be automatically created after a successful login attempt? - protected $autoregistration = false; - + public $autoregistration = false; + + //can the user change their email address + public $email_changeable=true; + + //can the user change their email address + public $password_changeable=true; + //------------Auth plugin should implement some (or all) of these methods------------\\ /** * Check if a nickname/password combination is valid @@ -102,44 +108,65 @@ abstract class AuthPlugin extends Plugin } function StartCheckPassword($nickname, $password, &$authenticatedUser){ - $authenticated = $this->checkPassword($nickname, $password); - if($authenticated){ - $authenticatedUser = User::staticGet('nickname', $nickname); - if(!$authenticatedUser && $this->autoregistration){ - if($this->autoregister($nickname)){ - $authenticatedUser = User::staticGet('nickname', $nickname); + if($this->password_changeable){ + $authenticated = $this->checkPassword($nickname, $password); + if($authenticated){ + $authenticatedUser = User::staticGet('nickname', $nickname); + if(!$authenticatedUser && $this->autoregistration){ + if($this->autoregister($nickname)){ + $authenticatedUser = User::staticGet('nickname', $nickname); + } + } + return false; + }else{ + if($this->authn_authoritative){ + return false; } } - return false; + //we're not authoritative, so let other handlers try }else{ if($this->authn_authoritative){ - return false; + //since we're authoritative, no other plugin could do this + throw new Exception(_('Password changing is not allowed')); } } - //we're not authoritative, so let other handlers try } function onStartChangePassword($nickname,$oldpassword,$newpassword) { - $authenticated = $this->checkPassword($nickname, $oldpassword); - if($authenticated){ - $result = $this->changePassword($nickname,$oldpassword,$newpassword); - if($result){ - //stop handling of other handlers, because what was requested was done - return false; + if($this->password_changeable){ + $authenticated = $this->checkPassword($nickname, $oldpassword); + if($authenticated){ + $result = $this->changePassword($nickname,$oldpassword,$newpassword); + if($result){ + //stop handling of other handlers, because what was requested was done + return false; + }else{ + throw new Exception(_('Password changing failed')); + } }else{ - throw new Exception(_('Password changing failed')); + if($this->authn_authoritative){ + //since we're authoritative, no other plugin could do this + throw new Exception(_('Password changing failed')); + }else{ + //let another handler try + return null; + } } }else{ if($this->authn_authoritative){ //since we're authoritative, no other plugin could do this - throw new Exception(_('Password changing failed')); - }else{ - //let another handler try - return null; + throw new Exception(_('Password changing is not allowed')); } } - + } + + function onStartAccountSettingsPasswordMenuItem($widget) + { + if($this->authn_authoritative && !$this->password_changeable){ + //since we're authoritative, no other plugin could change passwords, so do render the menu item + return false; + } } } diff --git a/plugins/Ldap/LdapPlugin.php b/plugins/Ldap/LdapPlugin.php index 8a416bccc7..88ca92b372 100644 --- a/plugins/Ldap/LdapPlugin.php +++ b/plugins/Ldap/LdapPlugin.php @@ -36,6 +36,17 @@ require_once 'Net/LDAP2.php'; class LdapPlugin extends AuthPlugin { + public $host=null; + public $port=null; + public $version=null; + public $starttls=null; + public $binddn=null; + public $bindpw=null; + public $basedn=null; + public $options=null; + public $filter=null; + public $scope=null; + public $attributes=array(); function __construct() { @@ -125,7 +136,7 @@ class LdapPlugin extends AuthPlugin $keys = array('host','port','version','starttls','binddn','bindpw','basedn','options','filter','scope'); foreach($keys as $key){ $value = $this->$key; - if($value!==false){ + if($value!==null){ $config[$key]=$value; } } diff --git a/plugins/Ldap/README b/plugins/Ldap/README index 1b6e3e75a9..063286cef5 100644 --- a/plugins/Ldap/README +++ b/plugins/Ldap/README @@ -4,12 +4,12 @@ Installation ============ add "addPlugin('ldap', array('setting'=>'value', 'setting2'=>'value2', ...);" to the bottom of your config.php - - Settings ======== -authn_authoritative: Set to true if LDAP's responses are authoritative (meaning if LDAP fails, do check the any other plugins or the internal password database). -autoregistration: Set to true if users should be automatically created when they attempt to login. +authn_authoritative (false): Set to true if LDAP's responses are authoritative (meaning if LDAP fails, do check the any other plugins or the internal password database). +autoregistration (false): Set to true if users should be automatically created when they attempt to login. +email_changeable (true): Are users allowed to change their email address? (true or false) +password_changeable (true): Are users allowed to change their passwords? (true or false) host*: LDAP server name to connect to. You can provide several hosts in an array in which case the hosts are tried from left to right.. See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php port: Port on the server. See http://pear.php.net/manual/en/package.networking.net-ldap2.connecting.php @@ -30,12 +30,15 @@ attributes: an array with the key being the StatusNet user attribute name, and t location * required +default values are in (parenthesis) Example ======= Here's an example of an LDAP plugin configuration that connects to Microsoft Active Directory. addPlugin('ldap', array( + 'authn_authoritative'=>true, + 'autoregistration'=>true, 'binddn'=>'username', 'bindpw'=>'password', 'basedn'=>'OU=Users,OU=StatusNet,OU=US,DC=americas,DC=global,DC=loc', From ee3fc8ba03ddd8451cac60547af72ea2cef7dc6a Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 10 Nov 2009 15:23:33 -0800 Subject: [PATCH 8/8] Added some notes in the comments --- actions/apiaccountupdatedeliverydevice.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/actions/apiaccountupdatedeliverydevice.php b/actions/apiaccountupdatedeliverydevice.php index 5b7176bbbe..684906fe90 100644 --- a/actions/apiaccountupdatedeliverydevice.php +++ b/actions/apiaccountupdatedeliverydevice.php @@ -97,6 +97,8 @@ class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction return; } + // Note: Twitter no longer supports IM + if (!in_array(strtolower($this->device), array('sms', 'im', 'none'))) { $this->clientError( _( @@ -135,6 +137,12 @@ class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction $twitter_user = $this->twitterUserArray($profile, true); + // Note: this Twitter API method is retarded because it doesn't give + // any success/failure information. Twitter's docs claim that the + // notification field will change to reflect notification choice, + // but that's not true; notification> is used to indicate + // whether the auth user is following the user in question. + if ($this->format == 'xml') { $this->initDocument('xml'); $this->showTwitterXmlUser($twitter_user);