- function getProfile() {
- return Profile::staticGet('id', $this->profile_id);
- }
-
- function delete() {
- $this->blowCaches(true);
- $this->blowFavesCache(true);
- $this->blowInboxes();
- parent::delete();
- }
-
- function saveTags() {
- /* extract all #hastags */
- $count = preg_match_all('/(?:^|\s)#([A-Za-z0-9_\-\.]{1,64})/', strtolower($this->content), $match);
- if (!$count) {
- return true;
- }
-
- /* elide characters we don't want in the tag */
- $match[1] = str_replace(array('-', '_', '.'), '', $match[1]);
-
- /* Add them to the database */
- foreach(array_unique($match[1]) as $hashtag) {
- $tag = DB_DataObject::factory('Notice_tag');
- $tag->notice_id = $this->id;
- $tag->tag = $hashtag;
- $tag->created = $this->created;
- $id = $tag->insert();
- if (!$id) {
- $last_error = PEAR::getStaticProperty('DB_DataObject','lastError');
- common_log(LOG_ERR, 'DB error inserting hashtag: ' . $last_error->message);
- common_server_error(sprintf(_('DB error inserting hashtag: %s'), $last_error->message));
- return;
- }
- }
- return true;
- }
-
- static function saveNew($profile_id, $content, $source=NULL, $is_local=1, $reply_to=NULL, $uri=NULL) {
-
- $notice = new Notice();
- $notice->profile_id = $profile_id;
-
- $blacklist = common_config('public', 'blacklist');
-
- # Blacklisted are non-false, but not 1, either
-
- if ($blacklist && in_array($profile_id, $blacklist)) {
- $notice->is_local = -1;
- } else {
- $notice->is_local = $is_local;
- }
-
- $notice->reply_to = $reply_to;
- $notice->created = common_sql_now();
- $notice->content = $content;
- $notice->rendered = common_render_content($notice->content, $notice);
- $notice->source = $source;
- $notice->uri = $uri;
-
- $id = $notice->insert();
-
- if (!$id) {
- common_log_db_error($notice, 'INSERT', __FILE__);
- return _('Problem saving notice.');
- }
-
- # Update the URI after the notice is in the database
- if (!$uri) {
- $orig = clone($notice);
- $notice->uri = common_notice_uri($notice);
-
- if (!$notice->update($orig)) {
- common_log_db_error($notice, 'UPDATE', __FILE__);
- return _('Problem saving notice.');
- }
- }
-
- # XXX: do we need to change this for remote users?
-
- common_save_replies($notice);
- $notice->saveTags();
-
- # Clear the cache for subscribed users, so they'll update at next request
- # XXX: someone clever could prepend instead of clearing the cache
-
- if (common_config('memcached', 'enabled')) {
- $notice->blowCaches();
- }
-
- $notice->addToInboxes();
- return $notice;
- }
-
- function blowCaches($blowLast=false) {
- $this->blowSubsCache($blowLast);
- $this->blowNoticeCache($blowLast);
- $this->blowRepliesCache($blowLast);
- $this->blowPublicCache($blowLast);
- $this->blowTagCache($blowLast);
- }
-
- function blowTagCache($blowLast=false) {
- $cache = common_memcache();
- if ($cache) {
- $tag = new Notice_tag();
- $tag->notice_id = $this->id;
- if ($tag->find()) {
- while ($tag->fetch()) {
- $cache->delete(common_cache_key('notice_tag:notice_stream:' . $tag->tag));
- if ($blowLast) {
- $cache->delete(common_cache_key('notice_tag:notice_stream:' . $tag->tag . ';last'));
- }
- }
- }
- $tag->free();
- unset($tag);
- }
- }
-
- function blowSubsCache($blowLast=false) {
- $cache = common_memcache();
- if ($cache) {
- $user = new User();
-
- $user->query('SELECT id ' .
- 'FROM user JOIN subscription ON user.id = subscription.subscriber ' .
- 'WHERE subscription.subscribed = ' . $this->profile_id);
-
- while ($user->fetch()) {
- $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id));
- if ($blowLast) {
- $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id . ';last'));
- }
- }
- $user->free();
- unset($user);
- }
- }
-
- function blowNoticeCache($blowLast=false) {
- if ($this->is_local) {
- $cache = common_memcache();
- if ($cache) {
- $cache->delete(common_cache_key('user:notices:'.$this->profile_id));
- if ($blowLast) {
- $cache->delete(common_cache_key('user:notices:'.$this->profile_id.';last'));
- }
- }
- }
- }
-
- function blowRepliesCache($blowLast=false) {
- $cache = common_memcache();
- if ($cache) {
- $reply = new Reply();
- $reply->notice_id = $this->id;
- if ($reply->find()) {
- while ($reply->fetch()) {
- $cache->delete(common_cache_key('user:replies:'.$reply->profile_id));
- if ($blowLast) {
- $cache->delete(common_cache_key('user:replies:'.$reply->profile_id.';last'));
- }
- }
- }
- $reply->free();
- unset($reply);
- }
- }
-
- function blowPublicCache($blowLast=false) {
- if ($this->is_local == 1) {
- $cache = common_memcache();
- if ($cache) {
- $cache->delete(common_cache_key('public'));
- if ($blowLast) {
- $cache->delete(common_cache_key('public').';last');
- }
- }
- }
- }
-
- function blowFavesCache($blowLast=false) {
- $cache = common_memcache();
- if ($cache) {
- $fave = new Fave();
- $fave->notice_id = $this->id;
- if ($fave->find()) {
- while ($fave->fetch()) {
- $cache->delete(common_cache_key('user:faves:'.$fave->user_id));
- if ($blowLast) {
- $cache->delete(common_cache_key('user:faves:'.$fave->user_id.';last'));
- }
- }
- }
- $fave->free();
- unset($fave);
- }
- }
-
- # XXX: too many args; we need to move to named params or even a separate
- # class for notice streams
-
- static function getStream($qry, $cachekey, $offset=0, $limit=20, $since_id=0, $before_id=0, $order=NULL) {
-
- if (common_config('memcached', 'enabled')) {
-
- # Skip the cache if this is a since_id or before_id qry
- if ($since_id > 0 || $before_id > 0) {
- return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order);
- } else {
- return Notice::getCachedStream($qry, $cachekey, $offset, $limit, $order);
- }
- }
-
- return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order);
- }
-
- static function getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order) {
-
- $needAnd = FALSE;
- $needWhere = TRUE;
-
- if (preg_match('/\bWHERE\b/i', $qry)) {
- $needWhere = FALSE;
- $needAnd = TRUE;
- }
-
- if ($since_id > 0) {
-
- if ($needWhere) {
- $qry .= ' WHERE ';
- $needWhere = FALSE;
- } else {
- $qry .= ' AND ';
- }
-
- $qry .= ' notice.id > ' . $since_id;
- }
-
- if ($before_id > 0) {
-
- if ($needWhere) {
- $qry .= ' WHERE ';
- $needWhere = FALSE;
- } else {
- $qry .= ' AND ';
- }
+ function getProfile()
+ {
+ return Profile::staticGet('id', $this->profile_id);
+ }
+
+ function delete()
+ {
+ $this->blowCaches(true);
+ $this->blowFavesCache(true);
+ $this->blowInboxes();
+ return parent::delete();
+ }
+
+ function saveTags()
+ {
+ /* extract all #hastags */
+ $count = preg_match_all('/(?:^|\s)#([A-Za-z0-9_\-\.]{1,64})/', strtolower($this->content), $match);
+ if (!$count) {
+ return true;
+ }
+
+ /* elide characters we don't want in the tag */
+ $match[1] = str_replace(array('-', '_', '.'), '', $match[1]);
+
+ /* Add them to the database */
+ foreach(array_unique($match[1]) as $hashtag) {
+ $tag = DB_DataObject::factory('Notice_tag');
+ $tag->notice_id = $this->id;
+ $tag->tag = $hashtag;
+ $tag->created = $this->created;
+ $id = $tag->insert();
+ if (!$id) {
+ $last_error = PEAR::getStaticProperty('DB_DataObject','lastError');
+ common_log(LOG_ERR, 'DB error inserting hashtag: ' . $last_error->message);
+ common_server_error(sprintf(_('DB error inserting hashtag: %s'), $last_error->message));
+ return;
+ }
+ }
+ return true;
+ }
+
+ static function saveNew($profile_id, $content, $source=null, $is_local=1, $reply_to=null, $uri=null) {
+
+ $profile = Profile::staticGet($profile_id);
+
+ if (!$profile) {
+ common_log(LOG_ERR, 'Problem saving notice. Unknown user.');
+ return _('Problem saving notice. Unknown user.');
+ }
+
+ if (common_config('throttle', 'enabled') && !Notice::checkEditThrottle($profile_id)) {
+ common_log(LOG_WARNING, 'Excessive posting by profile #' . $profile_id . '; throttled.');
+ return _('Too many notices too fast; 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).");
+ return _('You are banned from posting notices on this site.');
+ }
+
+ $notice = new Notice();
+ $notice->profile_id = $profile_id;
+
+ $blacklist = common_config('public', 'blacklist');
+
+ # Blacklisted are non-false, but not 1, either
+
+ if ($blacklist && in_array($profile_id, $blacklist)) {
+ $notice->is_local = -1;
+ } else {
+ $notice->is_local = $is_local;
+ }
+
+ $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;
+
+ $id = $notice->insert();
+
+ if (!$id) {
+ common_log_db_error($notice, 'INSERT', __FILE__);
+ return _('Problem saving notice.');
+ }
+
+ # Update the URI after the notice is in the database
+ if (!$uri) {
+ $orig = clone($notice);
+ $notice->uri = common_notice_uri($notice);
+
+ if (!$notice->update($orig)) {
+ common_log_db_error($notice, 'UPDATE', __FILE__);
+ return _('Problem saving notice.');
+ }
+ }
+
+ # XXX: do we need to change this for remote users?
+
+ $notice->saveReplies();
+ $notice->saveTags();
+ $notice->saveGroups();
+
+ # Clear the cache for subscribed users, so they'll update at next request
+ # XXX: someone clever could prepend instead of clearing the cache
+
+ if (common_config('memcached', 'enabled')) {
+ $notice->blowCaches();
+ }
+
+ $notice->addToInboxes();
+ return $notice;
+ }
+
+ static function checkEditThrottle($profile_id) {
+ $profile = Profile::staticGet($profile_id);
+ if (!$profile) {
+ return false;
+ }
+ # Get the Nth notice
+ $notice = $profile->getNotices(common_config('throttle', 'count') - 1, 1);
+ if ($notice && $notice->fetch()) {
+ # If the Nth notice was posted less than timespan seconds ago
+ if (time() - strtotime($notice->created) <= common_config('throttle', 'timespan')) {
+ # Then we throttle
+ return false;
+ }
+ }
+ # Either not N notices in the stream, OR the Nth was not posted within timespan seconds
+ return true;
+ }
+
+ function blowCaches($blowLast=false)
+ {
+ $this->blowSubsCache($blowLast);
+ $this->blowNoticeCache($blowLast);
+ $this->blowRepliesCache($blowLast);
+ $this->blowPublicCache($blowLast);
+ $this->blowTagCache($blowLast);
+ $this->blowGroupCache($blowLast);
+ }
+
+ function blowGroupCache($blowLast=false)
+ {
+ $cache = common_memcache();
+ if ($cache) {
+ $group_inbox = new Group_inbox();
+ $group_inbox->notice_id = $this->id;
+ if ($group_inbox->find()) {
+ while ($group_inbox->fetch()) {
+ $cache->delete(common_cache_key('group:notices:'.$group_inbox->group_id));
+ if ($blowLast) {
+ $cache->delete(common_cache_key('group:notices:'.$group_inbox->group_id.';last'));
+ }
+ $member = new Group_member();
+ $member->group_id = $group_inbox->group_id;
+ if ($member->find()) {
+ while ($member->fetch()) {
+ $cache->delete(common_cache_key('user:notices_with_friends:' . $member->profile_id));
+ if ($blowLast) {
+ $cache->delete(common_cache_key('user:notices_with_friends:' . $member->profile_id . ';last'));
+ }
+ }
+ }
+ }
+ }
+ $group_inbox->free();
+ unset($group_inbox);
+ }
+ }
+
+ function blowTagCache($blowLast=false)
+ {
+ $cache = common_memcache();
+ if ($cache) {
+ $tag = new Notice_tag();
+ $tag->notice_id = $this->id;
+ if ($tag->find()) {
+ while ($tag->fetch()) {
+ $cache->delete(common_cache_key('notice_tag:notice_stream:' . $tag->tag));
+ if ($blowLast) {
+ $cache->delete(common_cache_key('notice_tag:notice_stream:' . $tag->tag . ';last'));
+ }
+ }
+ }
+ $tag->free();
+ unset($tag);
+ }
+ }
+
+ function blowSubsCache($blowLast=false)
+ {
+ $cache = common_memcache();
+ if ($cache) {
+ $user = new User();
+
+ $user->query('SELECT id ' .
+ 'FROM user JOIN subscription ON user.id = subscription.subscriber ' .
+ 'WHERE subscription.subscribed = ' . $this->profile_id);
+
+ while ($user->fetch()) {
+ $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id));
+ if ($blowLast) {
+ $cache->delete(common_cache_key('user:notices_with_friends:' . $user->id . ';last'));
+ }
+ }
+ $user->free();
+ unset($user);
+ }
+ }
+
+ function blowNoticeCache($blowLast=false)
+ {
+ if ($this->is_local) {
+ $cache = common_memcache();
+ if ($cache) {
+ $cache->delete(common_cache_key('profile:notices:'.$this->profile_id));
+ if ($blowLast) {
+ $cache->delete(common_cache_key('profile:notices:'.$this->profile_id.';last'));
+ }
+ }
+ }
+ }
+
+ function blowRepliesCache($blowLast=false)
+ {
+ $cache = common_memcache();
+ if ($cache) {
+ $reply = new Reply();
+ $reply->notice_id = $this->id;
+ if ($reply->find()) {
+ while ($reply->fetch()) {
+ $cache->delete(common_cache_key('user:replies:'.$reply->profile_id));
+ if ($blowLast) {
+ $cache->delete(common_cache_key('user:replies:'.$reply->profile_id.';last'));
+ }
+ }
+ }
+ $reply->free();
+ unset($reply);
+ }
+ }
+
+ function blowPublicCache($blowLast=false)
+ {
+ if ($this->is_local == 1) {
+ $cache = common_memcache();
+ if ($cache) {
+ $cache->delete(common_cache_key('public'));
+ if ($blowLast) {
+ $cache->delete(common_cache_key('public').';last');
+ }
+ }
+ }
+ }
+
+ function blowFavesCache($blowLast=false)
+ {
+ $cache = common_memcache();
+ if ($cache) {
+ $fave = new Fave();
+ $fave->notice_id = $this->id;
+ if ($fave->find()) {
+ while ($fave->fetch()) {
+ $cache->delete(common_cache_key('user:faves:'.$fave->user_id));
+ if ($blowLast) {
+ $cache->delete(common_cache_key('user:faves:'.$fave->user_id.';last'));
+ }
+ }
+ }
+ $fave->free();
+ unset($fave);
+ }
+ }
+
+ # XXX: too many args; we need to move to named params or even a separate
+ # class for notice streams
+
+ static function getStream($qry, $cachekey, $offset=0, $limit=20, $since_id=0, $before_id=0, $order=null, $since=null) {
+
+ if (common_config('memcached', 'enabled')) {
+
+ # Skip the cache if this is a since, since_id or before_id qry
+ if ($since_id > 0 || $before_id > 0 || $since) {
+ return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order, $since);
+ } else {
+ return Notice::getCachedStream($qry, $cachekey, $offset, $limit, $order);
+ }
+ }
+
+ return Notice::getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order, $since);
+ }
+
+ static function getStreamDirect($qry, $offset, $limit, $since_id, $before_id, $order, $since) {
+
+ $needAnd = false;
+ $needWhere = true;
+
+ if (preg_match('/\bWHERE\b/i', $qry)) {
+ $needWhere = false;
+ $needAnd = true;
+ }
+
+ if ($since_id > 0) {
+
+ if ($needWhere) {
+ $qry .= ' WHERE ';
+ $needWhere = false;
+ } else {
+ $qry .= ' AND ';
+ }
+
+ $qry .= ' notice.id > ' . $since_id;
+ }
+
+ if ($before_id > 0) {
+
+ if ($needWhere) {
+ $qry .= ' WHERE ';
+ $needWhere = false;
+ } else {
+ $qry .= ' AND ';
+ }
+
+ $qry .= ' notice.id < ' . $before_id;
+ }
+
+ if ($since) {
+
+ if ($needWhere) {
+ $qry .= ' WHERE ';
+ $needWhere = false;
+ } else {
+ $qry .= ' AND ';
+ }