diff --git a/README b/README index 388d67ed2a..ec2e2ec4f7 100644 --- a/README +++ b/README @@ -879,6 +879,8 @@ notice: A plain string that will appear on every page. A good place to put introductory information about your service, or info about upgrades and outages, or other community info. Any HTML will be escaped. +dupelimit: Time in which it's not OK for the same person to post the + same notice; default = 60 seconds. db -- diff --git a/classes/Notice.php b/classes/Notice.php index 907239b084..eac90ce95b 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -121,6 +121,8 @@ class Notice extends Memcached_DataObject $profile = Profile::staticGet($profile_id); + $final = common_shorten_links($content); + if (!$profile) { common_log(LOG_ERR, 'Problem saving notice. Unknown user.'); return _('Problem saving notice. Unknown user.'); @@ -131,7 +133,12 @@ class Notice extends Memcached_DataObject return _('Too many notices too fast; take a breather and post again in a few minutes.'); } - $banned = common_config('profile', 'banned'); + if (common_config('site', 'dupelimit') > 0 && !Notice::checkDupes($profile_id, $final)) { + common_log(LOG_WARNING, 'Dupe posting by profile #' . $profile_id . '; throttled.'); + return _('Too many duplicate messages too quickly; take a breather and post again in a few minutes.'); + } + + $banned = common_config('profile', 'banned'); if ( in_array($profile_id, $banned) || in_array($profile->nickname, $banned)) { common_log(LOG_WARNING, "Attempted post from banned user: $profile->nickname (user id = $profile_id)."); @@ -155,12 +162,12 @@ class Notice extends Memcached_DataObject $notice->query('BEGIN'); - $notice->reply_to = $reply_to; - $notice->created = common_sql_now(); - $notice->content = common_shorten_links($content); - $notice->rendered = common_render_content($notice->content, $notice); - $notice->source = $source; - $notice->uri = $uri; + $notice->reply_to = $reply_to; + $notice->created = common_sql_now(); + $notice->content = $final; + $notice->rendered = common_render_content($final, $notice); + $notice->source = $source; + $notice->uri = $uri; if (Event::handle('StartNoticeSave', array(&$notice))) { @@ -204,6 +211,32 @@ class Notice extends Memcached_DataObject return $notice; } + static function checkDupes($profile_id, $content) { + $profile = Profile::staticGet($profile_id); + if (!$profile) { + return false; + } + $notice = $profile->getNotices(0, NOTICE_CACHE_WINDOW); + if ($notice) { + $last = 0; + while ($notice->fetch()) { + if (time() - strtotime($notice->created) >= common_config('site', 'dupelimit')) { + return true; + } else if ($notice->content == $content) { + return false; + } + } + } + # If we get here, oldest item in cache window is not + # old enough for dupe limit; do direct check against DB + $notice = new Notice(); + $notice->profile_id = $profile_id; + $notice->content = $content; + $notice->whereAdd('now() - created < ' . common_config('notice', 'dupelimit')); + $cnt = $notice->count(); + return ($cnt > 0); + } + static function checkEditThrottle($profile_id) { $profile = Profile::staticGet($profile_id); if (!$profile) { diff --git a/config.php.sample b/config.php.sample index a6cada77a2..c2b27408cd 100644 --- a/config.php.sample +++ b/config.php.sample @@ -159,3 +159,9 @@ $config['sphinx']['port'] = 3312; # Add Google Analytics # require_once('plugins/GoogleAnalyticsPlugin.php'); # $ga = new GoogleAnalyticsPlugin('your secret code'); + +#Don't allow saying the same thing more than once per hour +#$config['site']['dupelimit'] = 3600; +#Don't enforce the dupe limit +#$config['site']['dupelimit'] = -1; + diff --git a/lib/common.php b/lib/common.php index 0355d01e3a..917fdeafa4 100644 --- a/lib/common.php +++ b/lib/common.php @@ -85,7 +85,8 @@ $config = 'broughtbyurl' => null, 'closed' => false, 'inviteonly' => false, - 'private' => false), + 'private' => false, + 'dupelimit' => 60), # default for same person saying the same thing 'syslog' => array('appname' => 'laconica', # for syslog 'priority' => 'debug'), # XXX: currently ignored