- $msg
- $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
return;
}
- if ($this->user->hasBlocked($this->other)
- || $this->user->block($this->other)
- ) {
+ if (!$this->user->hasBlocked($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->showProfile($this->other, $this->format);
$this->endDocument($this->format);
return;
}
- if (!$this->user->hasBlocked($this->other)
- || $this->user->unblock($this->other)
- ) {
+ if ($this->user->hasBlocked($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->showProfile($this->other, $this->format);
$this->endDocument($this->format);
}
}
- $location = null;
-
- if (!empty($this->lat) && !empty($this->lon)) {
- $location = Location::fromLatLon($this->lat, $this->lon);
- }
-
$upload = null;
try {
$options = array('reply_to' => $reply_to);
- if (!empty($location)) {
- $options['lat'] = $location->lat;
- $options['lon'] = $location->lon;
- $options['location_id'] = $location->location_id;
- $options['location_ns'] = $location->location_ns;
+ if ($this->user->shareLocation()) {
+
+ $locOptions = Notice::locationOptions($this->lat,
+ $this->lon,
+ null,
+ null,
+ $this->user->getProfile());
+
+ $options = array_merge($options, $locOptions);
}
$this->notice =
{
$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) {
$this->serverError(_('Failed to save block information.'));
}
}
}
-
$replyto = 'false';
}
- $lat = $this->trimmed('lat');
- $lon = $this->trimmed('lon');
- $location_id = $this->trimmed('location_id');
- $location_ns = $this->trimmed('location_ns');
-
- if (!empty($lat) && !empty($lon) && empty($location_id)) {
- $location = Location::fromLatLon($lat, $lon);
- if (!empty($location)) {
- $location_id = $location->location_id;
- $location_ns = $location->location_ns;
- }
- }
-
$upload = null;
$upload = MediaFile::fromUpload('attach');
}
}
- $notice = Notice::saveNew($user->id, $content_shortened, 'web',
- array('reply_to' => ($replyto == 'false') ? null : $replyto,
- 'lat' => $lat,
- 'lon' => $lon,
- 'location_id' => $location_id,
- 'location_ns' => $location_ns));
+ $options = array('reply_to' => ($replyto == 'false') ? null : $replyto);
+
+ if ($user->shareLocation()) {
+
+ $locOptions = Notice::locationOptions($this->trimmed('lat'),
+ $this->trimmed('lon'),
+ $this->trimmed('location_id'),
+ $this->trimmed('location_ns'),
+ $user->getProfile());
+
+ $options = array_merge($options, $locOptions);
+ }
+
+ $notice = Notice::saveNew($user->id, $content_shortened, 'web', $options);
if (isset($upload)) {
$upload->attachToNotice($notice);
($this->arg('location')) ? $this->arg('location') : $profile->location,
_('Where you are, like "City, State (or Region), Country"'));
$this->elementEnd('li');
+ if (common_config('location', 'share') == 'user') {
+ $this->elementStart('li');
+ $this->checkbox('sharelocation', _('Share my current location when posting notices'),
+ ($this->arg('sharelocation')) ?
+ $this->arg('sharelocation') : $user->shareLocation());
+ $this->elementEnd('li');
+ }
Event::handle('EndProfileFormData', array($this));
$this->elementStart('li');
$this->input('tags', _('Tags'),
$profile->profileurl = common_profile_url($nickname);
+ if (common_config('location', 'share') == 'user') {
+
+ $exists = false;
+
+ $prefs = User_location_prefs::staticGet('user_id', $user->id);
+
+ if (empty($prefs)) {
+ $prefs = new User_location_prefs();
+
+ $prefs->user_id = $user->id;
+ $prefs->created = common_sql_now();
+ } else {
+ $exists = true;
+ $orig = clone($prefs);
+ }
+
+ $prefs->share_location = $this->boolean('sharelocation');
+
+ if ($exists) {
+ $result = $prefs->update($orig);
+ } else {
+ $result = $prefs->insert();
+ }
+
+ if ($result === false) {
+ common_log_db_error($prefs, ($exists) ? 'UPDATE' : 'INSERT', __FILE__);
+ $this->serverError(_('Couldn\'t save location prefs.'));
+ return;
+ }
+ }
+
common_debug('Old profile: ' . common_log_objstring($orig_profile), __FILE__);
common_debug('New profile: ' . common_log_objstring($profile), __FILE__);
function handlePost()
{
- $cur = common_current_user();
- $result = $cur->unblock($this->profile);
+ $cur = common_current_user();
+
+ $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) {
$this->serverError(_('Error removing the block.'));
return;
if (!empty($lat) && !empty($lon)) {
$notice->lat = $lat;
$notice->lon = $lon;
+ }
+
+ if (!empty($location_ns) && !empty($location_id)) {
$notice->location_id = $location_id;
$notice->location_ns = $location_ns;
- } else if (!empty($location_ns) && !empty($location_id)) {
- $location = Location::fromId($location_id, $location_ns);
- if (!empty($location)) {
- $notice->lat = $location->lat;
- $notice->lon = $location->lon;
- $notice->location_id = $location_id;
- $notice->location_ns = $location_ns;
- }
- } else {
- $notice->lat = $profile->lat;
- $notice->lon = $profile->lon;
- $notice->location_id = $profile->location_id;
- $notice->location_ns = $profile->location_ns;
}
if (Event::handle('StartNoticeSave', array(&$notice))) {
return $ids;
}
+
+ function locationOptions($lat, $lon, $location_id, $location_ns, $profile = null)
+ {
+ $options = array();
+
+ if (!empty($location_id) && !empty($location_ns)) {
+
+ $options['location_id'] = $location_id;
+ $options['location_ns'] = $location_ns;
+
+ $location = Location::fromId($location_id, $location_ns);
+
+ if (!empty($location)) {
+ $options['lat'] = $location->lat;
+ $options['lon'] = $location->lon;
+ }
+
+ } else if (!empty($lat) && !empty($lon)) {
+
+ $options['lat'] = $lat;
+ $options['lon'] = $lon;
+
+ $location = Location::fromLatLon($lat, $lon);
+
+ if (!empty($location)) {
+ $options['location_id'] = $location->location_id;
+ $options['location_ns'] = $location->location_ns;
+ }
+ } else if (!empty($profile)) {
+
+ if (isset($profile->lat) && isset($profile->lon)) {
+ $options['lat'] = $profile->lat;
+ $options['lon'] = $profile->lon;
+ }
+
+ if (isset($profile->location_id) && isset($profile->location_ns)) {
+ $options['location_id'] = $profile->location_id;
+ $options['location_ns'] = $profile->location_ns;
+ }
+ }
+
+ return $options;
+ }
}
// 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');
return $ids;
}
+
+ function shareLocation()
+ {
+ $cfg = common_config('location', 'share');
+
+ if ($cfg == 'always') {
+ return true;
+ } else if ($cfg == 'never') {
+ return false;
+ } else { // user
+ $share = true;
+
+ $prefs = User_location_prefs::staticGet('user_id', $this->id);
+
+ if (empty($prefs)) {
+ $share = common_config('location', 'sharedefault');
+ } else {
+ $share = $prefs->share_location;
+ $prefs->free();
+ }
+
+ return $share;
+ }
+ }
}
--- /dev/null
+<?php
+/**
+ * StatusNet, the distributed open-source microblogging tool
+ *
+ * Data class for user location preferences
+ *
+ * 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 Data
+ * @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/
+ */
+
+require_once INSTALLDIR.'/classes/Memcached_DataObject.php';
+
+class User_location_prefs extends Memcached_DataObject
+{
+ ###START_AUTOCODE
+ /* the code below is auto generated do not remove the above tag */
+
+ public $__table = 'user_location_prefs'; // table name
+ public $user_id; // int(4) primary_key not_null
+ public $share_location; // tinyint(1) default_1
+ public $created; // datetime not_null default_0000-00-00%2000%3A00%3A00
+ public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
+
+ /* Static get */
+ function staticGet($k,$v=NULL) { return Memcached_DataObject::staticGet('User_location_prefs',$k,$v); }
+
+ /* the code above is auto generated do not remove the tag below */
+ ###END_AUTOCODE
+}
-
[avatar]
profile_id = 129
original = 17
[user_openid__keys]
trustroot = K
-user_id = K
\ No newline at end of file
+user_id = K
+
+[user_location_prefs]
+user_id = 129
+share_location = 17
+created = 142
+modified = 384
+
+[user_location_prefs__keys]
+user_id = N
+
constraint primary key (user_id)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
+create table user_location_prefs (
+ user_id integer not null comment 'user who has the preference' references user (id),
+ share_location tinyint default 1 comment 'Whether to share location data',
+ created datetime not null comment 'date this record was created',
+ modified timestamp comment 'date this record was modified',
+
+ constraint primary key (user_id)
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
constraint primary key (user_id)
) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+create table user_location_prefs (
+ user_id integer not null comment 'user who has the preference' references user (id),
+ share_location tinyint default 1 comment 'Whether to share location data',
+ created datetime not null comment 'date this record was created',
+ modified timestamp comment 'date this record was modified',
+
+ constraint primary key (user_id)
+) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
+
'message' =>
array('contentlimit' => null),
'location' =>
- array('namespace' => 1), // 1 = geonames, 2 = Yahoo Where on Earth
+ array('share' => 'user', // whether to share location; 'always', 'user', 'never'
+ 'sharedefault' => true),
'omb' =>
array('timeout' => 5), // HTTP request timeout in seconds when contacting remote hosts for OMB updates
'logincommand' =>
--- /dev/null
+<?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;
+ }
+}
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
+if (!defined('STATUSNET')) {
exit(1);
}
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()
{
$schema = Schema::get();
return true;
}
- function onInitializePlugin()
- {
- // XXX: do something here?
- return true;
- }
+ /**
+ * Add our actions to the URL router
+ *
+ * @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/clear', array('action' => 'clearflag'));
$m->connect('admin/profile/flag', array('action' => 'adminprofileflag'));
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)
{
case 'FlagprofileAction':
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;
case 'FlagProfileForm':
- require_once(INSTALLDIR.'/plugins/UserFlag/' . strtolower($cls . '.php'));
+ case 'ClearFlagForm':
+ include_once INSTALLDIR.'/plugins/UserFlag/' . strtolower($cls . '.php');
return false;
case 'User_flag_profile':
- require_once(INSTALLDIR.'/plugins/UserFlag/'.$cls.'.php');
+ include_once INSTALLDIR.'/plugins/UserFlag/'.$cls.'.php';
return false;
default:
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)
{
$user = common_current_user();
$action->element('p', 'flagged', _('Flagged'));
} else {
$form = new FlagProfileForm($action, $profile,
- array('action' => 'showstream',
- 'nickname' => $profile->nickname));
+ array('action' => 'showstream',
+ 'nickname' => $profile->nickname));
$form->show();
}
return true;
}
+ /**
+ * Add a 'flag' button to profiles in a list
+ *
+ * @param ProfileListItem $item item being shown
+ *
+ * @return boolean hook result
+ */
+
function onEndProfileListItemActionElements($item)
{
$user = common_current_user();
return true;
}
+ /**
+ * Add our plugin's CSS to page output
+ *
+ * @param Action $action action being shown
+ *
+ * @return boolean hook result
+ */
+
function onEndShowStatusNetStyles($action)
{
- $action->cssLink(common_path('plugins/UserFlag/userflag.css'),
+ $action->cssLink(common_path('plugins/UserFlag/userflag.css'),
null, 'screen, projection, tv');
return true;
}
+ /**
+ * Initialize any flagging buttons on the page
+ *
+ * @param Action $action action being shown
+ *
+ * @return boolean hook result
+ */
+
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;
}
}
<?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
* Copyright (C) 2009, StatusNet, Inc.
*
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
{
###START_AUTOCODE
/* the code above is auto generated do not remove the tag below */
###END_AUTOCODE
- function table() {
+ /**
+ * return table definition for DB_DataObject
+ *
+ * @return array array of column definitions
+ */
+
+ function table()
+ {
return array(
'profile_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
'user_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
);
}
- function keys() {
+ /**
+ * return key definitions for DB_DataObject
+ *
+ * @return array key definitions
+ */
+
+ function keys()
+ {
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)
{
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)
{
$ufp = User_flag_profile::pkeyGet(array('profile_id' => $profile_id,
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;
+ }
}
class AdminprofileflagAction extends Action
{
+ var $page = null;
+ var $profiles = null;
+
/**
* Take arguments for running
*
{
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;
}
$this->showPage();
}
- function title() {
+ /**
+ * Title of this page
+ *
+ * @return string Title of the page
+ */
+
+ function title()
+ {
return _('Flagged profiles');
}
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()
{
$ufp = new User_flag_profile();
$ufp->whereAdd('cleared is NULL');
$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();
}
}
-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)
{
}
}
+/**
+ * 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
{
- var $user = null;
+ const MAX_FLAGGERS = 5;
+
+ var $user = null;
var $r2args = null;
+ /**
+ * Overload parent's action list with our own moderation-oriented buttons
+ *
+ * @return void
+ */
+
function showActions()
{
$this->user = common_current_user();
$this->endActions();
}
+ /**
+ * Show a button to sandbox the profile
+ *
+ * @return void
+ */
+
function showSandboxButton()
{
if ($this->user->hasRight(Right::SANDBOXUSER)) {
}
}
+ /**
+ * Show a button to silence the profile
+ *
+ * @return void
+ */
+
function showSilenceButton()
{
if ($this->user->hasRight(Right::SILENCEUSER)) {
}
}
+ /**
+ * Show a button to delete user and profile
+ *
+ * @return void
+ */
+
function showDeleteButton()
{
}
}
+ /**
+ * Show a button to clear flags
+ *
+ * @return void
+ */
+
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');
+ }
}
}
--- /dev/null
+<?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');
+ }
+}
--- /dev/null
+<?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');
+ }
+}
assert(!empty($this->profile)); // checked above
if (User_flag_profile::exists($this->profile->id,
- $user->id))
- {
+ $user->id)) {
$this->clientError(_('Flag already exists.'));
return false;
}
return true;
}
-
/**
* Handle request
*
assert(!empty($user));
assert(!empty($this->profile));
- $ufp = new User_flag_profile();
-
- $ufp->profile_id = $this->profile->id;
- $ufp->user_id = $user->id;
- $ufp->created = common_sql_now();
+ // throws an exception on error
- if (!$ufp->insert()) {
- throw new ServerException(sprintf(_("Couldn't flag profile '%s' for review."),
- $this->profile->nickname));
- }
-
- $ufp->free();
+ User_flag_profile::create($user->id, $this->profile->id);
if ($this->boolean('ajax')) {
$this->ajaxResults();
}
}
- function ajaxResults() {
+ /**
+ * Return results as AJAX message
+ *
+ * @return void
+ */
+
+ function ajaxResults()
+ {
header('Content-Type: text/xml;charset=utf-8');
$this->xw->startDocument('1.0', 'UTF-8');
$this->elementStart('html');