diff --git a/plugins/ModLog/ModLog.php b/plugins/ModLog/ModLog.php new file mode 100644 index 0000000000..b2c6546838 --- /dev/null +++ b/plugins/ModLog/ModLog.php @@ -0,0 +1,123 @@ +. + * + * @category Moderation + * @package StatusNet + * @author Evan Prodromou + * @copyright 2012 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + exit(1); +} + +/** + * Class comment here + * + * @category Category here + * @package StatusNet + * @author Evan Prodromou + * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 + * @link http://status.net/ + * + * @see DB_DataObject + */ + +class ModLog extends Managed_DataObject +{ + public $__table = 'mod_log'; // table name + + public $id; // UUID + public $profile_id; // profile id + public $moderator_id; // profile id + public $role; // the role + public $grant; // 1 = grant, 0 = revoke + public $created; // datetime + + /** + * Get an instance by key + * + * @param string $k Key to use to lookup (usually 'user_id' for this class) + * @param mixed $v Value to lookup + * + * @return TagSub object found, or null for no hits + * + */ + function staticGet($k, $v=null) + { + return Managed_DataObject::staticGet('ModLog', $k, $v); + } + + /** + * Get an instance by compound key + * + * @param array $kv array of key-value mappings + * + * @return TagSub object found, or null for no hits + * + */ + function pkeyGet($kv) + { + return Managed_DataObject::pkeyGet('ModLog', $kv); + } + + /** + * The One True Thingy that must be defined and declared. + */ + public static function schemaDef() + { + return array('description' => 'Log of moderation events', + 'fields' => array( + 'id' => array('type' => 'varchar', + 'length' => 36, + 'not null' => true, + 'description' => 'unique event ID'), + 'profile_id' => array('type' => 'int', + 'not null' => true, + 'description' => 'profile getting the role'), + 'moderator_id' => array('type' => 'int', + 'description' => 'profile granting or revoking the role'), + 'role' => array('type' => 'varchar', + 'length' => 32, + 'not null' => true, + 'description' => 'role granted or revoked'), + 'is_grant' => array('type' => 'int', + 'size' => 'tiny', + 'default' => 1, + 'description' => 'Was this a grant or revocation of a role'), + 'created' => array('type' => 'datetime', + 'not null' => true, + 'description' => 'date this record was created') + ), + 'primary key' => array('id'), + 'foreign keys' => array( + 'mod_log_profile_id_fkey' => array('profile', array('profile_id' => 'id')), + 'mod_log_moderator_id_fkey' => array('user', array('user_id' => 'id')) + ), + 'indexes' => array( + 'mod_log_profile_id_created_idx' => array('profile_id', 'created'), + ), + ); + } +} diff --git a/plugins/ModLog/ModLogPlugin.php b/plugins/ModLog/ModLogPlugin.php new file mode 100644 index 0000000000..459df63e82 --- /dev/null +++ b/plugins/ModLog/ModLogPlugin.php @@ -0,0 +1,218 @@ +. + * + * @category Moderation + * @package StatusNet + * @author Evan Prodromou + * @copyright 2012 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + // This check helps protect against security problems; + // your code file can't be executed directly from the web. + exit(1); +} + +/** + * Moderation logging + * + * Shows a history of moderation for this user in the sidebar + * + * @category Moderation + * @package StatusNet + * @author Evan Prodromou + * @copyright 2012 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +class ModLogPlugin extends Plugin +{ + const VIEWMODLOG = 'ModLogPlugin::VIEWMODLOG'; + + /** + * Database schema setup + * + * We keep a moderation log table + * + * @see Schema + * @see ColumnDef + * + * @return boolean hook value; true means continue processing, false means stop. + */ + + function onCheckSchema() + { + $schema = Schema::get(); + + $schema->ensureTable('mod_log', ModLog::schemaDef()); + + return true; + } + + /** + * Load related modules when needed + * + * @param string $cls Name of the class to be loaded + * + * @return boolean hook value; true means continue processing, false means stop. + */ + + function onAutoload($cls) + { + $dir = dirname(__FILE__); + + switch ($cls) + { + case 'ModLog': + include_once $dir . '/'.$cls.'.php'; + return false; + default: + return true; + } + } + + function onEndGrantRole($profile, $role) + { + $modlog = new ModLog(); + + $modlog->id = UUID::gen(); + $modlog->profile_id = $profile->id; + + $cur = common_current_user(); + + if (!empty($cur)) { + $modlog->moderator_id = $cur->id; + } + + $modlog->role = $role; + $modlog->is_grant = 1; + $modlog->created = common_sql_now(); + + $modlog->insert(); + + return true; + } + + function onEndRevokeRole($profile, $role) + { + $modlog = new ModLog(); + + $modlog->id = UUID::gen(); + + $modlog->profile_id = $profile->id; + + $cur = common_current_user(); + + if (!empty($cur)) { + $modlog->moderator_id = $cur->id; + } + + $modlog->role = $role; + $modlog->is_grant = 0; + $modlog->created = common_sql_now(); + + $modlog->insert(); + + return true; + } + + function onEndShowSections($action) + { + if ($action->arg('action') != 'showstream') { + return true; + } + + $cur = common_current_user(); + + if (empty($cur) || !$cur->hasRight(self::VIEWMODLOG)) { + return true; + } + + $profile = $action->profile; + + $ml = new ModLog(); + + $ml->profile_id = $profile->id; + $ml->orderBy("created"); + + $cnt = $ml->find(); + + if ($cnt > 0) { + + $action->elementStart('div', array('id' => 'entity_mod_log', + 'class' => 'section')); + + $action->element('h2', null, _('Moderation')); + + $action->elementStart('table'); + + while ($ml->fetch()) { + $action->elementStart('tr'); + $action->element('td', null, strftime('%y-%m-%d', strtotime($ml->created))); + $action->element('td', null, sprintf(($ml->is_grant) ? _('+%s') : _('-%s'), $ml->role)); + $action->elementStart('td'); + if ($ml->moderator_id) { + $mod = Profile::staticGet('id', $ml->moderator_id); + if (empty($mod)) { + $action->text(_('[unknown]')); + } else { + $action->element('a', array('href' => $mod->profileurl, + 'title' => $mod->fullname), + $mod->nickname); + } + } else { + $action->text(_('[unknown]')); + } + $action->elementEnd('td'); + $action->elementEnd('tr'); + } + + $action->elementEnd('table'); + + $action->elementEnd('div'); + } + } + + function onUserRightsCheck($profile, $right, &$result) { + switch ($right) { + case self::VIEWMODLOG: + $result = ($profile->hasRole(Profile_role::MODERATOR) || $profile->hasRole('modhelper')); + return false; + default: + return true; + } + } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'ModLog', + 'version' => STATUSNET_VERSION, + 'author' => 'Evan Prodromou', + 'homepage' => 'http://status.net/wiki/Plugin:ModLog', + 'description' => + _m('Show the moderation history for a profile in the sidebar')); + return true; + } +}