From: Evan Prodromou Date: Wed, 7 Mar 2012 13:24:28 +0000 (-0600) Subject: Added forms to train notices as spam/ham X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=4ae3e4aaaf0202d580e942dbd4843e41235ea3eb;p=quix0rs-gnu-social.git Added forms to train notices as spam/ham --- diff --git a/ActivitySpamPlugin.php b/ActivitySpamPlugin.php index fcc6673ce2..8a6ee89b3f 100644 --- a/ActivitySpamPlugin.php +++ b/ActivitySpamPlugin.php @@ -50,6 +50,9 @@ class ActivitySpamPlugin extends Plugin public $username = null; public $password = null; + const REVIEWSPAM = 'ActivitySpamPlugin::REVIEWSPAM'; + const TRAINSPAM = 'ActivitySpamPlugin::TRAINSPAM'; + /** * Initializer * @@ -101,10 +104,15 @@ class ActivitySpamPlugin extends Plugin switch ($cls) { + case 'TrainAction': + include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php'; + return false; case 'Spam_score': include_once $dir . '/'.$cls.'.php'; return false; case 'SpamFilter': + case 'TrainSpamForm': + case 'TrainHamForm': include_once $dir . '/'.strtolower($cls).'.php'; return false; default: @@ -146,6 +154,45 @@ class ActivitySpamPlugin extends Plugin return true; } + function onUserRightsCheck($profile, $right, &$result) { + switch ($right) { + case self::REVIEWSPAM: + case self::TRAINSPAM: + $result = ($profile->hasRole(Profile_role::MODERATOR) || $profile->hasRole('modhelper')); + return false; + default: + return true; + } + } + + function onGetSpamFilter(&$filter) { + $filter = $this->filter; + return false; + } + + function onEndShowNoticeOptions(&$nli) + { + $notice = $nli->notice; + $out = $nli->out; + + if (!empty($notice)) { + + $score = Spam_score::staticGet('notice_id', $notice->id); + + if (empty($score)) { + // XXX: show a question-mark or something + } else if ($score->is_spam) { + $form = new TrainHamForm($out, $notice); + $form->show(); + } else if (!$score->is_spam) { + $form = new TrainSpamForm($out, $notice); + $form->show(); + } + } + + return true; + } + function onPluginVersion(&$versions) { $versions[] = array('name' => 'ActivitySpam', diff --git a/Spam_score.php b/Spam_score.php index 08887d06b3..d6a05e2261 100644 --- a/Spam_score.php +++ b/Spam_score.php @@ -83,6 +83,33 @@ class Spam_score extends Managed_DataObject return $score; } + function save($notice, $result) { + + $score = Spam_score::staticGet('notice_id', $notice->id); + + if (empty($score)) { + $orig = null; + $score = new Spam_score(); + } else { + $orig = clone($score); + } + + $score->notice_id = $notice->id; + $score->score = $result->probability; + $score->is_spam = $result->isSpam; + $score->scaled = Spam_score::scale($score->score); + $score->created = common_sql_now(); + $score->notice_created = $notice->created; + + if (empty($orig)) { + $score->insert(); + } else { + $score->update($orig); + } + + return $score; + } + /** * The One True Thingy that must be defined and declared. */ diff --git a/train.php b/train.php new file mode 100644 index 0000000000..46579935bc --- /dev/null +++ b/train.php @@ -0,0 +1,165 @@ +. + * + * @category Spam + * @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); +} + +/** + * Train a notice as spam + * + * @category Spam + * @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 TrainAction extends Action +{ + protected $notice = null; + protected $filter = null; + protected $category = null; + + /** + * For initializing members of the class. + * + * @param array $argarray misc. arguments + * + * @return boolean true + */ + + function prepare($argarray) + { + parent::prepare($argarray); + + // User must be logged in. + + $user = common_current_user(); + + if (empty($user)) { + throw new ClientException(_("You must be logged in to train spam."), 403); + } + + // It must be a "real" login, not saved cookie login + + if (!common_is_real_login()) { + common_set_returnto($this->selfUrl()); + if (Event::handle('RedirectToLogin', array($this, $user))) { + common_redirect(common_local_url('login'), 303); + return false; + } + } + + // User must have the right to review spam + + if (!$user->hasRight(ActivitySpamPlugin::TRAINSPAM)) { + throw new ClientException(_('You cannot review spam on this site.'), 403); + } + + $id = $this->trim('notice'); + + $this->notice = Notice::staticGet('id', $id); + + if (empty($this->notice)) { + throw new ClientException(_("No such notice.")); + } + + $this->checkSessionToken(); + + $filter = null; + + Event::handle('GetSpamFilter', &$filter); + + if (empty($filter)) { + throw new ServerException(_("No spam filter configured.")); + } + + $this->filter = $filter; + + $this->category = $this->trim('category'); + + if ($this->category !== SpamFilter::SPAM && + $this->category !== SpamFilter::HAM) + { + throw new ClientException(_("No such category.")); + } + + return true; + } + + /** + * Handler method + * + * @param array $argarray is ignored since it's now passed in in prepare() + * + * @return void + */ + + function handle($argarray=null) + { + // Train + + $this->filter->trainOnError($this->notice, $this->category); + + // Re-test + + $result = $this->filter->test($this->notice); + + // Update or insert + + $score = Spam_score::save($notice, $result); + + // Show new toggle form + + if ($this->category === SpamFilter::SPAM) { + $form = new TrainHamForm($this, $this->notice); + } else { + $form = new TrainSpamForm($this, $this->notice); + } + + if ($this->boolean('ajax')) { + $this->startHTML('text/xml;charset=utf-8'); + $this->elementStart('head'); + // TRANS: Page title for page on which favorite notices can be unfavourited. + $this->element('title', null, _('Disfavor favorite.')); + $this->elementEnd('head'); + $this->elementStart('body'); + $form->show(); + $this->elementEnd('body'); + $this->elementEnd('html'); + } else { + common_redirect(common_local_url('spam'), 303); + } + } +} diff --git a/trainhamform.php b/trainhamform.php new file mode 100644 index 0000000000..71bd650a3f --- /dev/null +++ b/trainhamform.php @@ -0,0 +1,147 @@ +. + * + * @category Spam + * @package StatusNet + * @author Evan Prodromou + * @copyright 2011 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); +} + +/** + * Form + * + * @category Spam + * @package StatusNet + * @author Evan Prodromou + * @copyright 2011 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +class TrainHamForm extends Form { + + var $notice = null; + + function __construct($out, $notice) { + parent::__construct($out); + $this->notice = $notice; + } + + /** + * Name of the form + * + * Sub-classes should overload this with the name of their form. + * + * @return void + */ + + function formLegend() + { + return _("Train ham"); + } + + /** + * Visible or invisible data elements + * + * Display the form fields that make up the data of the form. + * Sub-classes should overload this to show their data. + * + * @return void + */ + + function formData() + { + $this->hidden('category', SpamFilter::HAM); + $this->hidden('notice', $this->notice->id); + } + + /** + * Buttons for form actions + * + * Submit and cancel buttons (or whatever) + * Sub-classes should overload this to show their own buttons. + * + * @return void + */ + + function formActions() + { + $this->submit('submit', + _('Train ham'), + 'submit', + null, + _("Mark as ham")); + } + + /** + * ID of the form + * + * Should be unique on the page. Sub-classes should overload this + * to show their own IDs. + * + * @return int ID of the form + */ + + function id() + { + return 'train_ham_' . $this->notice->id; + } + + /** + * Action of the form. + * + * URL to post to. Should be overloaded by subclasses to give + * somewhere to post to. + * + * @return string URL to post to + */ + + function action() + { + return common_local_url('train'); + } + + /** + * Class of the form. May include space-separated list of multiple classes. + * + * If 'ajax' is included, the form will automatically be submitted with + * an 'ajax=1' parameter added, and the resulting form or error message + * will replace the form after submission. + * + * It's up to you to make sure that the target action supports this! + * + * @return string the form's class + */ + + function formClass() + { + return 'form_train_ham ajax'; + } +} diff --git a/trainspamform.php b/trainspamform.php new file mode 100644 index 0000000000..07942f4546 --- /dev/null +++ b/trainspamform.php @@ -0,0 +1,147 @@ +. + * + * @category Spam + * @package StatusNet + * @author Evan Prodromou + * @copyright 2011 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); +} + +/** + * Form + * + * @category Spam + * @package StatusNet + * @author Evan Prodromou + * @copyright 2011 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0 + * @link http://status.net/ + */ + +class TrainSpamForm extends Form { + + var $notice = null; + + function __construct($out, $notice) { + parent::__construct($out); + $this->notice = $notice; + } + + /** + * Name of the form + * + * Sub-classes should overload this with the name of their form. + * + * @return void + */ + + function formLegend() + { + return _("Train spam"); + } + + /** + * Visible or invisible data elements + * + * Display the form fields that make up the data of the form. + * Sub-classes should overload this to show their data. + * + * @return void + */ + + function formData() + { + $this->hidden('category', SpamFilter::SPAM); + $this->hidden('notice', $this->notice->id); + } + + /** + * Buttons for form actions + * + * Submit and cancel buttons (or whatever) + * Sub-classes should overload this to show their own buttons. + * + * @return void + */ + + function formActions() + { + $this->submit('submit', + _('Train spam'), + 'submit', + null, + _("Mark as spam")); + } + + /** + * ID of the form + * + * Should be unique on the page. Sub-classes should overload this + * to show their own IDs. + * + * @return int ID of the form + */ + + function id() + { + return 'train_spam_' . $this->notice->id; + } + + /** + * Action of the form. + * + * URL to post to. Should be overloaded by subclasses to give + * somewhere to post to. + * + * @return string URL to post to + */ + + function action() + { + return common_local_url('train'); + } + + /** + * Class of the form. May include space-separated list of multiple classes. + * + * If 'ajax' is included, the form will automatically be submitted with + * an 'ajax=1' parameter added, and the resulting form or error message + * will replace the form after submission. + * + * It's up to you to make sure that the target action supports this! + * + * @return string the form's class + */ + + function formClass() + { + return 'form_train_spam ajax'; + } +}