Merge branch 'master' into 0.9.x

This commit is contained in:
Evan Prodromou 2009-12-28 15:49:14 -08:00
commit 98a579fedf
15 changed files with 1013 additions and 169 deletions

View File

@ -640,3 +640,18 @@ EndLog: After writing to the logs
- $msg - $msg
- $filename - $filename
StartBlockProfile: when we're about to block
- $user: the person doing the block
- $profile: the person getting blocked, can be remote
EndBlockProfile: when a block has succeeded
- $user: the person doing the block
- $profile: the person blocked, can be remote
StartUnblockProfile: when we're about to unblock
- $user: the person doing the unblock
- $profile: the person getting unblocked, can be remote
EndUnblockProfile: when an unblock has succeeded
- $user: the person doing the unblock
- $profile: the person unblocked, can be remote

View File

@ -109,9 +109,16 @@ class ApiBlockCreateAction extends ApiAuthAction
return; return;
} }
if ($this->user->hasBlocked($this->other) if (!$this->user->hasBlocked($this->other)) {
|| $this->user->block($this->other) if (Event::handle('StartBlockProfile', array($this->user, $this->other))) {
) { $result = $this->user->block($this->other);
if ($result) {
Event::handle('EndBlockProfile', array($this->user, $this->other));
}
}
}
if ($this->user->hasBlocked($this->other)) {
$this->initDocument($this->format); $this->initDocument($this->format);
$this->showProfile($this->other, $this->format); $this->showProfile($this->other, $this->format);
$this->endDocument($this->format); $this->endDocument($this->format);

View File

@ -97,9 +97,16 @@ class ApiBlockDestroyAction extends ApiAuthAction
return; return;
} }
if (!$this->user->hasBlocked($this->other) if ($this->user->hasBlocked($this->other)) {
|| $this->user->unblock($this->other) if (Event::handle('StartUnblockProfile', array($this->user, $this->other))) {
) { $result = $this->user->unblock($this->other);
if ($result) {
Event::handle('EndUnblockProfile', array($this->user, $this->other));
}
}
}
if (!$this->user->hasBlocked($this->other)) {
$this->initDocument($this->format); $this->initDocument($this->format);
$this->showProfile($this->other, $this->format); $this->showProfile($this->other, $this->format);
$this->endDocument($this->format); $this->endDocument($this->format);

View File

@ -156,7 +156,12 @@ class BlockAction extends ProfileFormAction
{ {
$cur = common_current_user(); $cur = common_current_user();
$result = $cur->block($this->profile); if (Event::handle('StartBlockProfile', array($cur, $this->profile))) {
$result = $cur->block($this->profile);
if ($result) {
Event::handle('EndBlockProfile', array($cur, $this->profile));
}
}
if (!$result) { if (!$result) {
$this->serverError(_('Failed to save block information.')); $this->serverError(_('Failed to save block information.'));
@ -164,4 +169,3 @@ class BlockAction extends ProfileFormAction
} }
} }
} }

View File

@ -71,8 +71,17 @@ class UnblockAction extends ProfileFormAction
function handlePost() function handlePost()
{ {
$cur = common_current_user(); $cur = common_current_user();
$result = $cur->unblock($this->profile);
$result = false;
if (Event::handle('StartUnblockProfile', array($cur, $this->profile))) {
$result = $cur->unblock($this->profile);
if ($result) {
Event::handle('EndUnblockProfile', array($cur, $this->profile));
}
}
if (!$result) { if (!$result) {
$this->serverError(_('Error removing the block.')); $this->serverError(_('Error removing the block.'));
return; return;

View File

@ -625,7 +625,11 @@ class User extends Memcached_DataObject
// Cancel their subscription, if it exists // Cancel their subscription, if it exists
subs_unsubscribe_to($other->getUser(),$this->getProfile()); $otherUser = User::staticGet('id', $other->id);
if (!empty($otherUser)) {
subs_unsubscribe_to($otherUser, $this->getProfile());
}
$block->query('COMMIT'); $block->query('COMMIT');

View File

@ -1,4 +1,4 @@
// A shim to implement the W3C Geolocation API Specification using Gears or the Ajax API // A shim to implement the W3C Geolocation API Specification using Gears
if (typeof navigator.geolocation == "undefined" || navigator.geolocation.shim ) (function(){ if (typeof navigator.geolocation == "undefined" || navigator.geolocation.shim ) (function(){
// -- BEGIN GEARS_INIT // -- BEGIN GEARS_INIT
@ -96,122 +96,9 @@ var GearsGeoLocation = (function() {
}; };
}); });
var AjaxGeoLocation = (function() { // If you have Gears installed use that
// -- PRIVATE if (window.google && google.gears) {
var loading = false; navigator.geolocation = GearsGeoLocation();
var loadGoogleLoader = function() { }
if (!hasGoogleLoader() && !loading) {
loading = true;
var s = document.createElement('script');
s.src = (document.location.protocol == "https:"?"https://":"http://") + 'www.google.com/jsapi?callback=_google_loader_apiLoaded';
s.type = "text/javascript";
document.getElementsByTagName('body')[0].appendChild(s);
}
};
var queue = [];
var addLocationQueue = function(callback) {
queue.push(callback);
}
var runLocationQueue = function() {
if (hasGoogleLoader()) {
while (queue.length > 0) {
var call = queue.pop();
call();
}
}
}
window['_google_loader_apiLoaded'] = function() {
runLocationQueue();
}
var hasGoogleLoader = function() {
return (window['google'] && google['loader']);
}
var checkGoogleLoader = function(callback) {
if (hasGoogleLoader()) return true;
addLocationQueue(callback);
loadGoogleLoader();
return false;
};
loadGoogleLoader(); // start to load as soon as possible just in case
// -- PUBLIC
return {
shim: true,
type: "ClientLocation",
lastPosition: null,
getCurrentPosition: function(successCallback, errorCallback, options) {
var self = this;
if (!checkGoogleLoader(function() {
self.getCurrentPosition(successCallback, errorCallback, options);
})) return;
if (google.loader.ClientLocation) {
var cl = google.loader.ClientLocation;
var position = {
coords: {
latitude: cl.latitude,
longitude: cl.longitude,
altitude: null,
accuracy: 43000, // same as Gears accuracy over wifi?
altitudeAccuracy: null,
heading: null,
speed: null,
},
// extra info that is outside of the bounds of the core API
address: {
city: cl.address.city,
country: cl.address.country,
country_code: cl.address.country_code,
region: cl.address.region
},
timestamp: new Date()
};
successCallback(position);
this.lastPosition = position;
} else if (errorCallback === "function") {
errorCallback({ code: 3, message: "Using the Google ClientLocation API and it is not able to calculate a location."});
}
},
watchPosition: function(successCallback, errorCallback, options) {
this.getCurrentPosition(successCallback, errorCallback, options);
var self = this;
var watchId = setInterval(function() {
self.getCurrentPosition(successCallback, errorCallback, options);
}, 10000);
return watchId;
},
clearWatch: function(watchId) {
clearInterval(watchId);
},
getPermission: function(siteName, imageUrl, extraMessage) {
// for now just say yes :)
return true;
}
};
});
// If you have Gears installed use that, else use Ajax ClientLocation
navigator.geolocation = (window.google && google.gears) ? GearsGeoLocation() : AjaxGeoLocation();
})(); })();

View File

@ -0,0 +1,203 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Plugin to prevent use of nicknames or URLs on a blacklist
*
* 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 Action
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @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);
}
/**
* Plugin to prevent use of nicknames or URLs on a blacklist
*
* @category Plugin
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class BlacklistPlugin extends Plugin
{
public $nicknames = array();
public $urls = array();
/**
* Hook registration to prevent blacklisted homepages or nicknames
*
* Throws an exception if there's a blacklisted homepage or nickname.
*
* @param Action $action Action being called (usually register)
*
* @return boolean hook value
*/
function onStartRegistrationTry($action)
{
$homepage = strtolower($action->trimmed('homepage'));
if (!empty($homepage)) {
if (!$this->_checkUrl($homepage)) {
$msg = sprintf(_m("You may not register with homepage '%s'"),
$homepage);
throw new ClientException($msg);
}
}
$nickname = strtolower($action->trimmed('nickname'));
if (!empty($nickname)) {
if (!$this->_checkNickname($nickname)) {
$msg = sprintf(_m("You may not register with nickname '%s'"),
$nickname);
throw new ClientException($msg);
}
}
return true;
}
/**
* Hook profile update to prevent blacklisted homepages or nicknames
*
* Throws an exception if there's a blacklisted homepage or nickname.
*
* @param Action $action Action being called (usually register)
*
* @return boolean hook value
*/
function onStartProfileSaveForm($action)
{
$homepage = strtolower($action->trimmed('homepage'));
if (!empty($homepage)) {
if (!$this->_checkUrl($homepage)) {
$msg = sprintf(_m("You may not use homepage '%s'"),
$homepage);
throw new ClientException($msg);
}
}
$nickname = strtolower($action->trimmed('nickname'));
if (!empty($nickname)) {
if (!$this->_checkNickname($nickname)) {
$msg = sprintf(_m("You may not use nickname '%s'"),
$nickname);
throw new ClientException($msg);
}
}
return true;
}
/**
* Hook notice save to prevent blacklisted urls
*
* Throws an exception if there's a blacklisted url in the content.
*
* @param Notice &$notice Notice being saved
*
* @return boolean hook value
*/
function onStartNoticeSave(&$notice)
{
common_replace_urls_callback($notice->content,
array($this, 'checkNoticeUrl'));
return true;
}
/**
* Helper callback for notice save
*
* Throws an exception if there's a blacklisted url in the content.
*
* @param string $url URL in the notice content
*
* @return boolean hook value
*/
function checkNoticeUrl($url)
{
// It comes in special'd, so we unspecial it
// before comparing against patterns
$url = htmlspecialchars_decode($url);
if (!$this->_checkUrl($url)) {
$msg = sprintf(_m("You may not use url '%s' in notices"),
$url);
throw new ClientException($msg);
}
return $url;
}
/**
* Helper for checking URLs
*
* Checks an URL against our patterns for a match.
*
* @param string $url URL to check
*
* @return boolean true means it's OK, false means it's bad
*/
private function _checkUrl($url)
{
foreach ($this->urls as $pattern) {
if (preg_match("/$pattern/", $url)) {
return false;
}
}
return true;
}
/**
* Helper for checking nicknames
*
* Checks a nickname against our patterns for a match.
*
* @param string $nickname nickname to check
*
* @return boolean true means it's OK, false means it's bad
*/
private function _checkNickname($nickname)
{
foreach ($this->nicknames as $pattern) {
if (preg_match("/$pattern/", $nickname)) {
return false;
}
}
return true;
}
}

View File

@ -27,7 +27,7 @@
* @link http://status.net/ * @link http://status.net/
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { if (!defined('STATUSNET')) {
exit(1); exit(1);
} }
@ -43,6 +43,20 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
class UserFlagPlugin extends Plugin class UserFlagPlugin extends Plugin
{ {
const REVIEWFLAGS = 'UserFlagPlugin::reviewflags';
const CLEARFLAGS = 'UserFlagPlugin::clearflags';
public $flagOnBlock = true;
/**
* Hook for ensuring our tables are created
*
* Ensures that the user_flag_profile table exists
* and has the right columns.
*
* @return boolean hook return
*/
function onCheckSchema() function onCheckSchema()
{ {
$schema = Schema::get(); $schema = Schema::get();
@ -62,37 +76,61 @@ class UserFlagPlugin extends Plugin
return true; return true;
} }
function onInitializePlugin() /**
{ * Add our actions to the URL router
// XXX: do something here? *
return true; * @param Net_URL_Mapper $m URL mapper for this hit
} *
* @return boolean hook return
*/
function onRouterInitialized($m) { function onRouterInitialized($m)
{
$m->connect('main/flag/profile', array('action' => 'flagprofile')); $m->connect('main/flag/profile', array('action' => 'flagprofile'));
$m->connect('main/flag/clear', array('action' => 'clearflag'));
$m->connect('admin/profile/flag', array('action' => 'adminprofileflag')); $m->connect('admin/profile/flag', array('action' => 'adminprofileflag'));
return true; return true;
} }
function onAutoload($cls) /**
* Auto-load our classes if called
*
* @param string $cls Class to load
*
* @return boolean hook return
*/
function onAutoload($cls)
{ {
switch ($cls) switch ($cls)
{ {
case 'FlagprofileAction': case 'FlagprofileAction':
case 'AdminprofileflagAction': case 'AdminprofileflagAction':
require_once(INSTALLDIR.'/plugins/UserFlag/' . strtolower(mb_substr($cls, 0, -6)) . '.php'); case 'ClearflagAction':
include_once INSTALLDIR.'/plugins/UserFlag/' .
strtolower(mb_substr($cls, 0, -6)) . '.php';
return false; return false;
case 'FlagProfileForm': case 'FlagProfileForm':
require_once(INSTALLDIR.'/plugins/UserFlag/' . strtolower($cls . '.php')); case 'ClearFlagForm':
include_once INSTALLDIR.'/plugins/UserFlag/' . strtolower($cls . '.php');
return false; return false;
case 'User_flag_profile': case 'User_flag_profile':
require_once(INSTALLDIR.'/plugins/UserFlag/'.$cls.'.php'); include_once INSTALLDIR.'/plugins/UserFlag/'.$cls.'.php';
return false; return false;
default: default:
return true; return true;
} }
} }
/**
* Add a 'flag' button to profile page
*
* @param Action &$action The action being called
* @param Profile $profile Profile being shown
*
* @return boolean hook result
*/
function onEndProfilePageActionsElements(&$action, $profile) function onEndProfilePageActionsElements(&$action, $profile)
{ {
$user = common_current_user(); $user = common_current_user();
@ -105,8 +143,8 @@ class UserFlagPlugin extends Plugin
$action->element('p', 'flagged', _('Flagged')); $action->element('p', 'flagged', _('Flagged'));
} else { } else {
$form = new FlagProfileForm($action, $profile, $form = new FlagProfileForm($action, $profile,
array('action' => 'showstream', array('action' => 'showstream',
'nickname' => $profile->nickname)); 'nickname' => $profile->nickname));
$form->show(); $form->show();
} }
@ -116,6 +154,14 @@ class UserFlagPlugin extends Plugin
return true; return true;
} }
/**
* Add a 'flag' button to profiles in a list
*
* @param ProfileListItem $item item being shown
*
* @return boolean hook result
*/
function onEndProfileListItemActionElements($item) function onEndProfileListItemActionElements($item)
{ {
$user = common_current_user(); $user = common_current_user();
@ -136,16 +182,78 @@ class UserFlagPlugin extends Plugin
return true; return true;
} }
/**
* Add our plugin's CSS to page output
*
* @param Action $action action being shown
*
* @return boolean hook result
*/
function onEndShowStatusNetStyles($action) function onEndShowStatusNetStyles($action)
{ {
$action->cssLink(common_path('plugins/UserFlag/userflag.css'), $action->cssLink(common_path('plugins/UserFlag/userflag.css'),
null, 'screen, projection, tv'); null, 'screen, projection, tv');
return true; return true;
} }
/**
* Initialize any flagging buttons on the page
*
* @param Action $action action being shown
*
* @return boolean hook result
*/
function onEndShowScripts($action) function onEndShowScripts($action)
{ {
$action->inlineScript('if ($(".form_entity_flag").length > 0) { SN.U.FormXHR($(".form_entity_flag")); }'); $action->inlineScript('if ($(".form_entity_flag").length > 0) { '.
'SN.U.FormXHR($(".form_entity_flag")); '.
'}');
return true;
}
/**
* Check whether a user has one of our defined rights
*
* We define extra rights; this function checks to see if a
* user has one of them.
*
* @param User $user User being checked
* @param string $right Right we're checking
* @param boolean &$result out, result of the check
*
* @return boolean hook result
*/
function onUserRightsCheck($user, $right, &$result)
{
switch ($right) {
case self::REVIEWFLAGS:
case self::CLEARFLAGS:
$result = $user->hasRole('moderator');
return false; // done processing!
}
return true; // unchanged!
}
/**
* Optionally flag profile when a block happens
*
* We optionally add a flag when a profile has been blocked
*
* @param User $user User doing the block
* @param Profile $profile Profile being blocked
*
* @return boolean hook result
*/
function onEndBlockProfile($user, $profile)
{
if ($this->flagOnBlock) {
User_flag_profile::create($user->id, $profile->id);
}
return true; return true;
} }
} }

View File

@ -1,5 +1,15 @@
<?php <?php
/* /**
* Data class for profile flags
*
* PHP version 5
*
* @category Data
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*
* StatusNet - the distributed open-source microblogging tool * StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2009, StatusNet, Inc. * Copyright (C) 2009, StatusNet, Inc.
* *
@ -23,6 +33,18 @@ if (!defined('STATUSNET')) {
require_once INSTALLDIR . '/classes/Memcached_DataObject.php'; require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
/**
* Data class for profile flags
*
* A class representing a user flagging another profile for review.
*
* @category Action
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
class User_flag_profile extends Memcached_DataObject class User_flag_profile extends Memcached_DataObject
{ {
###START_AUTOCODE ###START_AUTOCODE
@ -40,7 +62,14 @@ class User_flag_profile extends Memcached_DataObject
/* the code above is auto generated do not remove the tag below */ /* the code above is auto generated do not remove the tag below */
###END_AUTOCODE ###END_AUTOCODE
function table() { /**
* return table definition for DB_DataObject
*
* @return array array of column definitions
*/
function table()
{
return array( return array(
'profile_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, 'profile_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
'user_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, 'user_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
@ -49,15 +78,39 @@ class User_flag_profile extends Memcached_DataObject
); );
} }
function keys() { /**
* return key definitions for DB_DataObject
*
* @return array key definitions
*/
function keys()
{
return array('profile_id' => 'N', 'user_id' => 'N'); return array('profile_id' => 'N', 'user_id' => 'N');
} }
/**
* Get a single object with multiple keys
*
* @param array $kv Map of key-value pairs
*
* @return User_flag_profile found object or null
*/
function &pkeyGet($kv) function &pkeyGet($kv)
{ {
return Memcached_DataObject::pkeyGet('User_flag_profile', $kv); return Memcached_DataObject::pkeyGet('User_flag_profile', $kv);
} }
/**
* Check if a flag exists for given profile and user
*
* @param integer $profile_id Profile to check for
* @param integer $user_id User to check for
*
* @return boolean true if exists, else false
*/
static function exists($profile_id, $user_id) static function exists($profile_id, $user_id)
{ {
$ufp = User_flag_profile::pkeyGet(array('profile_id' => $profile_id, $ufp = User_flag_profile::pkeyGet(array('profile_id' => $profile_id,
@ -65,4 +118,23 @@ class User_flag_profile extends Memcached_DataObject
return !empty($ufp); return !empty($ufp);
} }
static function create($user_id, $profile_id)
{
$ufp = new User_flag_profile();
$ufp->profile_id = $profile_id;
$ufp->user_id = $user_id;
$ufp->created = common_sql_now();
if (!$ufp->insert()) {
$msg = sprintf(_("Couldn't flag profile '%d' for review."),
$profile_id);
throw new ServerException($msg);
}
$ufp->free();
return true;
}
} }

View File

@ -43,6 +43,9 @@ if (!defined('STATUSNET')) {
class AdminprofileflagAction extends Action class AdminprofileflagAction extends Action
{ {
var $page = null;
var $profiles = null;
/** /**
* Take arguments for running * Take arguments for running
* *
@ -55,6 +58,47 @@ class AdminprofileflagAction extends Action
{ {
parent::prepare($args); parent::prepare($args);
$user = common_current_user();
// User must be logged in.
if (!common_logged_in()) {
$this->clientError(_('Not logged in.'));
return;
}
$user = common_current_user();
// ...because they're logged in
assert(!empty($user));
// It must be a "real" login, not saved cookie login
if (!common_is_real_login()) {
// Cookie theft is too easy; we require automatic
// logins to re-authenticate before admining the site
common_set_returnto($this->selfUrl());
if (Event::handle('RedirectToLogin', array($this, $user))) {
common_redirect(common_local_url('login'), 303);
}
}
// User must have the right to review flags
if (!$user->hasRight(UserFlagPlugin::REVIEWFLAGS)) {
$this->clientError(_('You cannot review profile flags.'));
return false;
}
$this->page = $this->trimmed('page');
if (empty($this->page)) {
$this->page = 1;
}
$this->profiles = $this->getProfiles();
return true; return true;
} }
@ -73,7 +117,14 @@ class AdminprofileflagAction extends Action
$this->showPage(); $this->showPage();
} }
function title() { /**
* Title of this page
*
* @return string Title of the page
*/
function title()
{
return _('Flagged profiles'); return _('Flagged profiles');
} }
@ -85,13 +136,20 @@ class AdminprofileflagAction extends Action
function showContent() function showContent()
{ {
$profile = $this->getProfiles(); $pl = new FlaggedProfileList($this->profiles, $this);
$pl = new FlaggedProfileList($profile, $this); $cnt = $pl->show();
$pl->show(); $this->pagination($this->page > 1, $cnt > PROFILES_PER_PAGE,
$this->page, 'adminprofileflag');
} }
/**
* Retrieve this action's profiles
*
* @return Profile $profile Profile query results
*/
function getProfiles() function getProfiles()
{ {
$ufp = new User_flag_profile(); $ufp = new User_flag_profile();
@ -103,7 +161,12 @@ class AdminprofileflagAction extends Action
$ufp->whereAdd('cleared is NULL'); $ufp->whereAdd('cleared is NULL');
$ufp->groupBy('profile_id'); $ufp->groupBy('profile_id');
$ufp->orderBy('flag_count DESC'); $ufp->orderBy('flag_count DESC, profile_id DESC');
$offset = ($this->page-1) * PROFILES_PER_PAGE;
$limit = PROFILES_PER_PAGE + 1;
$ufp->limit($offset, $limit);
$profiles = array(); $profiles = array();
@ -122,7 +185,27 @@ class AdminprofileflagAction extends Action
} }
} }
class FlaggedProfileList extends ProfileList { /**
* Specialization of ProfileList to show flagging information
*
* Most of the hard part is done in FlaggedProfileListItem.
*
* @category Widget
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
class FlaggedProfileList extends ProfileList
{
/**
* Factory method for creating new list items
*
* @param Profile $profile Profile to create an item for
*
* @return ProfileListItem newly-created item
*/
function newListItem($profile) function newListItem($profile)
{ {
@ -130,11 +213,29 @@ class FlaggedProfileList extends ProfileList {
} }
} }
/**
* Specialization of ProfileListItem to show flagging information
*
* @category Widget
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
class FlaggedProfileListItem extends ProfileListItem class FlaggedProfileListItem extends ProfileListItem
{ {
var $user = null; const MAX_FLAGGERS = 5;
var $user = null;
var $r2args = null; var $r2args = null;
/**
* Overload parent's action list with our own moderation-oriented buttons
*
* @return void
*/
function showActions() function showActions()
{ {
$this->user = common_current_user(); $this->user = common_current_user();
@ -159,6 +260,12 @@ class FlaggedProfileListItem extends ProfileListItem
$this->endActions(); $this->endActions();
} }
/**
* Show a button to sandbox the profile
*
* @return void
*/
function showSandboxButton() function showSandboxButton()
{ {
if ($this->user->hasRight(Right::SANDBOXUSER)) { if ($this->user->hasRight(Right::SANDBOXUSER)) {
@ -174,6 +281,12 @@ class FlaggedProfileListItem extends ProfileListItem
} }
} }
/**
* Show a button to silence the profile
*
* @return void
*/
function showSilenceButton() function showSilenceButton()
{ {
if ($this->user->hasRight(Right::SILENCEUSER)) { if ($this->user->hasRight(Right::SILENCEUSER)) {
@ -189,6 +302,12 @@ class FlaggedProfileListItem extends ProfileListItem
} }
} }
/**
* Show a button to delete user and profile
*
* @return void
*/
function showDeleteButton() function showDeleteButton()
{ {
@ -200,7 +319,92 @@ class FlaggedProfileListItem extends ProfileListItem
} }
} }
/**
* Show a button to clear flags
*
* @return void
*/
function showClearButton() function showClearButton()
{ {
if ($this->user->hasRight(UserFlagPlugin::CLEARFLAGS)) {
$this->out->elementStart('li', 'entity_clear');
$cf = new ClearFlagForm($this->out, $this->profile, $this->r2args);
$cf->show();
$this->out->elementEnd('li');
}
}
/**
* Overload parent function to add flaggers list
*
* @return void
*/
function endProfile()
{
$this->showFlaggersList();
parent::endProfile();
}
/**
* Show a list of people who've flagged this profile
*
* @return void
*/
function showFlaggersList()
{
$flaggers = array();
$ufp = new User_flag_profile();
$ufp->selectAdd();
$ufp->selectAdd('user_id');
$ufp->profile_id = $this->profile->id;
$ufp->orderBy('created');
if ($ufp->find()) { // XXX: this should always happen
while ($ufp->fetch()) {
$user = User::staticGet('id', $ufp->user_id);
if (!empty($user)) { // XXX: this would also be unusual
$flaggers[] = clone($user);
}
}
}
$cnt = count($flaggers);
$others = 0;
if ($cnt > self::MAX_FLAGGERS) {
$flaggers = array_slice($flaggers, 0, self::MAX_FLAGGERS);
$others = $cnt - self::MAX_FLAGGERS;
}
$lnks = array();
foreach ($flaggers as $flagger) {
$url = common_local_url('showstream',
array('nickname' => $flagger->nickname));
$lnks[] = XMLStringer::estring('a', array('href' => $url,
'class' => 'flagger'),
$flagger->nickname);
}
if ($cnt > 0) {
$text = _('Flagged by ');
$text .= implode(', ', $lnks);
if ($others > 0) {
$text .= sprintf(_(' and %d others'), $others);
}
$this->out->elementStart('p', array('class' => 'flaggers'));
$this->out->raw($text);
$this->out->elementEnd('p');
}
} }
} }

View File

@ -0,0 +1,138 @@
<?php
/**
* Clear all flags for a profile
*
* PHP version 5
*
* @category Action
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*
* StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2009, StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
if (!defined('STATUSNET')) {
exit(1);
}
/**
* Action to clear flags for a profile
*
* @category Action
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://status.net/
*/
class ClearflagAction extends ProfileFormAction
{
/**
* Take arguments for running
*
* @param array $args $_REQUEST args
*
* @return boolean success flag
*/
function prepare($args)
{
if (!parent::prepare($args)) {
return false;
}
$user = common_current_user();
assert(!empty($user)); // checked above
assert(!empty($this->profile)); // checked above
return true;
}
/**
* Handle request
*
* Overriding the base Action's handle() here to deal check
* for Ajax and return an HXR response if necessary
*
* @param array $args $_REQUEST args; handled in prepare()
*
* @return void
*/
function handle($args)
{
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$this->handlePost();
if (!$this->boolean('ajax')) {
$this->returnToArgs();
}
}
}
/**
* Handle POST
*
* Executes the actions; deletes all flags
*
* @return void
*/
function handlePost()
{
$ufp = new User_flag_profile();
$result = $ufp->query('UPDATE user_flag_profile ' .
'SET cleared = now() ' .
'WHERE cleared is null ' .
'AND profile_id = ' . $this->profile->id);
if ($result == false) {
$msg = sprintf(_("Couldn't clear flags for profile '%s'."),
$this->profile->nickname);
throw new ServerException($msg);
}
$ufp->free();
if ($this->boolean('ajax')) {
$this->ajaxResults();
}
}
/**
* Return results in ajax form
*
* @return void
*/
function ajaxResults()
{
header('Content-Type: text/xml;charset=utf-8');
$this->xw->startDocument('1.0', 'UTF-8');
$this->elementStart('html');
$this->elementStart('head');
$this->element('title', null, _('Flags cleared'));
$this->elementEnd('head');
$this->elementStart('body');
$this->element('p', 'cleared', _('Cleared'));
$this->elementEnd('body');
$this->elementEnd('html');
}
}

View File

@ -0,0 +1,92 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Form for clearing profile flags
*
* 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 Form
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @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/form.php';
/**
* Form for clearing profile flags
*
* @category Form
* @package StatusNet
* @author Evan Prodromou <evan@status.net>
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
class ClearFlagForm extends ProfileActionForm
{
/**
* class of the form
* Action this form provides
*
* @return string class of the form
*/
function formClass()
{
return 'form_entity_clearflag';
}
/**
* Action this form provides
*
* @return string Name of the action, lowercased.
*/
function target()
{
return 'clearflag';
}
/**
* Title of the form
*
* @return string Title of the form, internationalized
*/
function title()
{
return _('Clear');
}
/**
* Description of the form
*
* @return string description of the form, internationalized
*/
function description()
{
return _('Clear all flags');
}
}

View File

@ -63,8 +63,7 @@ class FlagprofileAction extends ProfileFormAction
assert(!empty($this->profile)); // checked above assert(!empty($this->profile)); // checked above
if (User_flag_profile::exists($this->profile->id, if (User_flag_profile::exists($this->profile->id,
$user->id)) $user->id)) {
{
$this->clientError(_('Flag already exists.')); $this->clientError(_('Flag already exists.'));
return false; return false;
} }
@ -72,7 +71,6 @@ class FlagprofileAction extends ProfileFormAction
return true; return true;
} }
/** /**
* Handle request * Handle request
* *
@ -107,25 +105,23 @@ class FlagprofileAction extends ProfileFormAction
assert(!empty($user)); assert(!empty($user));
assert(!empty($this->profile)); assert(!empty($this->profile));
$ufp = new User_flag_profile(); // throws an exception on error
$ufp->profile_id = $this->profile->id; User_flag_profile::create($user->id, $this->profile->id);
$ufp->user_id = $user->id;
$ufp->created = common_sql_now();
if (!$ufp->insert()) {
throw new ServerException(sprintf(_("Couldn't flag profile '%s' for review."),
$this->profile->nickname));
}
$ufp->free();
if ($this->boolean('ajax')) { if ($this->boolean('ajax')) {
$this->ajaxResults(); $this->ajaxResults();
} }
} }
function ajaxResults() { /**
* Return results as AJAX message
*
* @return void
*/
function ajaxResults()
{
header('Content-Type: text/xml;charset=utf-8'); header('Content-Type: text/xml;charset=utf-8');
$this->xw->startDocument('1.0', 'UTF-8'); $this->xw->startDocument('1.0', 'UTF-8');
$this->elementStart('html'); $this->elementStart('html');

98
scripts/setconfig.php Normal file
View File

@ -0,0 +1,98 @@
#!/usr/bin/env php
<?php
/*
* StatusNet - a distributed open-source microblogging tool
* Copyright (C) 2008, 2009, StatusNet, 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/>.
*/
define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
$shortoptions = 'd';
$longoptions = array('delete');
$helptext = <<<END_OF_SETCONFIG_HELP
setconfig.php [options] [section] [setting] <value>
With three args, set the setting to the value.
With two args, just show the setting.
With -d, delete the setting.
[section] section to use (required)
[setting] setting to use (required)
<value> value to set (optional)
-d --delete delete the setting (no value)
END_OF_SETCONFIG_HELP;
require_once INSTALLDIR.'/scripts/commandline.inc';
if (count($args) < 2 || count($args) > 3) {
show_help();
exit(1);
}
$section = $args[0];
$setting = $args[1];
if (count($args) == 3) {
$value = $args[2];
} else {
$value = null;
}
try {
if (have_option('d', 'delete')) { // Delete
if (count($args) != 2) {
show_help();
exit(1);
}
if (have_option('v', 'verbose')) {
print "Deleting setting $section/$setting...";
}
$setting = Config::pkeyGet(array('section' => $section,
'setting' => $setting));
if (empty($setting)) {
print "Not found.\n";
} else {
$result = $setting->delete();
if ($result) {
print "DONE.\n";
} else {
print "ERROR.\n";
}
}
} else if (count($args) == 2) { // show
if (have_option('v', 'verbose')) {
print "$section/$setting = ";
}
$value = common_config($section, $setting);
print "$value\n";
} else { // set
if (have_option('v', 'verbose')) {
print "Setting $section/$setting...";
}
Config::save($section, $setting, $value);
print "DONE.\n";
}
} catch (Exception $e) {
print $e->getMessage() . "\n";
exit(1);
}