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/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/apiaccountupdatedeliverydevice.php b/actions/apiaccountupdatedeliverydevice.php new file mode 100644 index 0000000000..684906fe90 --- /dev/null +++ b/actions/apiaccountupdatedeliverydevice.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'; + +/** + * 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; + } + + // Note: Twitter no longer supports IM + + 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); + + // 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); + $this->endDocument('xml'); + } elseif ($this->format == 'json') { + $this->initDocument('json'); + $this->showJsonObjects($twitter_user); + $this->endDocument('json'); + } + } + +} diff --git a/actions/apiaccountupdateprofile.php b/actions/apiaccountupdateprofile.php new file mode 100644 index 0000000000..fd4384a25c --- /dev/null +++ b/actions/apiaccountupdateprofile.php @@ -0,0 +1,166 @@ +. + * + * @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 (!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; + } + + $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(_('Could not 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/actions/apiaccountupdateprofilebackgroundimage.php b/actions/apiaccountupdateprofilebackgroundimage.php new file mode 100644 index 0000000000..3537b9f979 --- /dev/null +++ b/actions/apiaccountupdateprofilebackgroundimage.php @@ -0,0 +1,211 @@ +. + * + * @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; + } + + 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 + + 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; + } + + $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) { + $this->clientError($e->getMessage(), 400, $this->format); + return; + } + + $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, ($this->tile == 'true')); + + $result = $design->update($original); + + if ($result === false) { + common_log_db_error($design, 'UPDATE', __FILE__); + $this->showForm(_('Could not update your design.')); + return; + } + + $profile = $this->user->getProfile(); + + if (empty($profile)) { + $this->clientError(_('User has no profile.')); + return; + } + + $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/actions/apiaccountupdateprofilecolors.php b/actions/apiaccountupdateprofilecolors.php new file mode 100644 index 0000000000..3cac829749 --- /dev/null +++ b/actions/apiaccountupdateprofilecolors.php @@ -0,0 +1,246 @@ +. + * + * @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; + } + + 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)) { + + $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(_('Could not 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($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'); + } + } + + /** + * 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/actions/apiaccountupdateprofileimage.php b/actions/apiaccountupdateprofileimage.php index 2f8e9628c4..153ef7818e 100644 --- a/actions/apiaccountupdateprofileimage.php +++ b/actions/apiaccountupdateprofileimage.php @@ -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'); 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/lib/api.php b/lib/api.php index 5e66639c4f..e2ea87b43e 100644 --- a/lib/api.php +++ b/lib/api.php @@ -176,9 +176,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(); diff --git a/lib/router.php b/lib/router.php index db9fdb4704..bad3decad5 100644 --- a/lib/router.php +++ b/lib/router.php @@ -428,9 +428,21 @@ 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')); + $m->connect('api/account/update_profile_background_image.:format', + array('action' => 'ApiAccountUpdateProfileBackgroundImage')); + + $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', 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',