Commit 9b663369 authored by mmn's avatar mmn

Group discovery from text functions polished

Also removed the entirely unused saveGroups function.

Now avoiding multiGet and using listFind in Profile->getGroups()
so we don't have to deal with ArrayWrapper.
parent f8b49e69
...@@ -137,12 +137,11 @@ class UsergroupsAction extends ProfileAction ...@@ -137,12 +137,11 @@ class UsergroupsAction extends ProfileAction
$groups = $this->user->getGroups($offset, $limit); $groups = $this->user->getGroups($offset, $limit);
if ($groups) { if ($groups instanceof User_group) {
$gl = new GroupList($groups, $this->user, $this); $gl = new GroupList($groups, $this->user, $this);
$cnt = $gl->show(); $cnt = $gl->show();
if (0 == $cnt) { } else {
$this->showEmptyListMessage(); $this->showEmptyListMessage();
}
} }
$this->pagination($this->page > 1, $cnt > GROUPS_PER_PAGE, $this->pagination($this->page > 1, $cnt > GROUPS_PER_PAGE,
......
...@@ -410,9 +410,8 @@ class Notice extends Managed_DataObject ...@@ -410,9 +410,8 @@ class Notice extends Managed_DataObject
$notice->url = $url; $notice->url = $url;
// Get the groups here so we can figure out replies and such // Get the groups here so we can figure out replies and such
if (!isset($groups)) { if (!isset($groups)) {
$groups = self::groupsFromText($notice->content, $profile); $groups = User_group::idsFromText($notice->content, $profile);
} }
$reply = null; $reply = null;
...@@ -1154,7 +1153,7 @@ class Notice extends Managed_DataObject ...@@ -1154,7 +1153,7 @@ class Notice extends Managed_DataObject
$groups = array(); $groups = array();
foreach (array_unique($group_ids) as $id) { foreach (array_unique($group_ids) as $id) {
$group = User_group::getKV('id', $id); $group = User_group::getKV('id', $id);
if ($group) { if ($group instanceof User_group) {
common_log(LOG_ERR, "Local delivery to group id $id, $group->nickname"); common_log(LOG_ERR, "Local delivery to group id $id, $group->nickname");
$result = $this->addToGroupInbox($group); $result = $this->addToGroupInbox($group);
if (!$result) { if (!$result) {
...@@ -1181,48 +1180,7 @@ class Notice extends Managed_DataObject ...@@ -1181,48 +1180,7 @@ class Notice extends Managed_DataObject
return $groups; return $groups;
} }
/** function addToGroupInbox(User_group $group)
* Parse !group delivery and record targets into group_inbox.
* @return array of Group objects
*/
function saveGroups()
{
// Don't save groups for repeats
if (!empty($this->repeat_of)) {
return array();
}
$profile = $this->getProfile();
$groups = self::groupsFromText($this->content, $profile);
/* Add them to the database */
foreach ($groups as $group) {
/* XXX: remote groups. */
if (empty($group)) {
continue;
}
if ($profile->isMember($group)) {
$result = $this->addToGroupInbox($group);
if (!$result) {
common_log_db_error($gi, 'INSERT', __FILE__);
}
$groups[] = clone($group);
}
}
return $groups;
}
function addToGroupInbox($group)
{ {
$gi = Group_inbox::pkeyGet(array('group_id' => $group->id, $gi = Group_inbox::pkeyGet(array('group_id' => $group->id,
'notice_id' => $this->id)); 'notice_id' => $this->id));
...@@ -1445,7 +1403,7 @@ class Notice extends Managed_DataObject ...@@ -1445,7 +1403,7 @@ class Notice extends Managed_DataObject
/** /**
* Pull list of groups this notice needs to be delivered to, * Pull list of groups this notice needs to be delivered to,
* as previously recorded by saveGroups() or saveKnownGroups(). * as previously recorded by saveKnownGroups().
* *
* @return array of Group objects * @return array of Group objects
*/ */
...@@ -2500,29 +2458,6 @@ class Notice extends Managed_DataObject ...@@ -2500,29 +2458,6 @@ class Notice extends Managed_DataObject
return false; return false;
} }
static function groupsFromText($text, $profile)
{
$groups = array();
/* extract all !group */
$count = preg_match_all('/(?:^|\s)!(' . Nickname::DISPLAY_FMT . ')/',
strtolower($text),
$match);
if (!$count) {
return $groups;
}
foreach (array_unique($match[1]) as $nickname) {
$group = User_group::getForNickname($nickname, $profile);
if ($group instanceof User_group && $profile->isMember($group)) {
$groups[] = $group->id;
}
}
return $groups;
}
public function getParent() public function getParent()
{ {
$parent = Notice::getKV('id', $this->reply_to); $parent = Notice::getKV('id', $this->reply_to);
......
...@@ -221,9 +221,8 @@ class Profile extends Managed_DataObject ...@@ -221,9 +221,8 @@ class Profile extends Managed_DataObject
function isMember($group) function isMember($group)
{ {
$groups = $this->getGroups(0, null); $groups = $this->getGroups(0, null);
$gs = $groups->fetchAll(); while ($groups instanceof User_group && $groups->fetch()) {
foreach ($gs as $g) { if ($groups->id == $group->id) {
if ($group->id == $g->id) {
return true; return true;
} }
} }
...@@ -272,7 +271,18 @@ class Profile extends Managed_DataObject ...@@ -272,7 +271,18 @@ class Profile extends Managed_DataObject
$ids = array_slice($ids, $offset, $limit); $ids = array_slice($ids, $offset, $limit);
} }
return User_group::multiGet('id', $ids); try {
return User_group::listFind('id', $ids);
} catch (NoResultException $e) {
return null; // throw exception when we handle it everywhere
}
}
function getGroupCount() {
$groups = $this->getGroups(0, null);
return $groups instanceof User_group
? $groups->N
: 0;
} }
function isTagged($peopletag) function isTagged($peopletag)
......
...@@ -418,14 +418,14 @@ class User_group extends Managed_DataObject ...@@ -418,14 +418,14 @@ class User_group extends Managed_DataObject
return true; return true;
} }
static function getForNickname($nickname, $profile=null) static function getForNickname($nickname, Profile $profile=null)
{ {
$nickname = Nickname::normalize($nickname); $nickname = Nickname::normalize($nickname);
// Are there any matching remote groups this profile's in? // Are there any matching remote groups this profile's in?
if ($profile) { if ($profile instanceof Profile) {
$group = $profile->getGroups(0, null); $group = $profile->getGroups(0, null);
while ($group->fetch()) { while ($group instanceof User_group && $group->fetch()) {
if ($group->nickname == $nickname) { if ($group->nickname == $nickname) {
// @fixme is this the best way? // @fixme is this the best way?
return clone($group); return clone($group);
...@@ -434,13 +434,12 @@ class User_group extends Managed_DataObject ...@@ -434,13 +434,12 @@ class User_group extends Managed_DataObject
} }
// If not, check local groups. // If not, check local groups.
$group = Local_group::getKV('nickname', $nickname); $group = Local_group::getKV('nickname', $nickname);
if (!empty($group)) { if ($group instanceof Local_group) {
return User_group::getKV('id', $group->group_id); return User_group::getKV('id', $group->group_id);
} }
$alias = Group_alias::getKV('alias', $nickname); $alias = Group_alias::getKV('alias', $nickname);
if (!empty($alias)) { if ($alias instanceof Group_alias) {
return User_group::getKV('id', $alias->group_id); return User_group::getKV('id', $alias->group_id);
} }
return null; return null;
...@@ -822,4 +821,37 @@ class User_group extends Managed_DataObject ...@@ -822,4 +821,37 @@ class User_group extends Managed_DataObject
return ($this->join_policy == self::JOIN_POLICY_MODERATE && return ($this->join_policy == self::JOIN_POLICY_MODERATE &&
$this->force_scope == 1); $this->force_scope == 1);
} }
static function groupsFromText($text, Profile $profile)
{
$groups = array();
/* extract all !group */
$count = preg_match_all('/(?:^|\s)!(' . Nickname::DISPLAY_FMT . ')/',
strtolower($text),
$match);
if (!$count) {
return $groups;
}
foreach (array_unique($match[1]) as $nickname) {
$group = self::getForNickname($nickname, $profile);
if ($group instanceof User_group && $profile->isMember($group)) {
$groups[] = clone($group);
}
}
return $groups;
}
static function idsFromText($text, Profile $profile)
{
$ids = array();
$groups = self::groupsFromText($text, $profile);
foreach ($groups as $group) {
$ids[$group->id] = true;
}
return array_keys($ids);
}
} }
...@@ -225,7 +225,7 @@ class ApiAction extends Action ...@@ -225,7 +225,7 @@ class ApiAction extends Action
} }
$twitter_user['profile_image_url_original'] = $origurl; $twitter_user['profile_image_url_original'] = $origurl;
$twitter_user['groups_count'] = $profile->getGroups(0, null)->N; $twitter_user['groups_count'] = $profile->getGroupCount();
foreach (array('linkcolor', 'backgroundcolor') as $key) { foreach (array('linkcolor', 'backgroundcolor') as $key) {
$twitter_user[$key] = Profile_prefs::getConfigData($profile, 'theme', $key); $twitter_user[$key] = Profile_prefs::getConfigData($profile, 'theme', $key);
} }
......
...@@ -980,7 +980,7 @@ class GroupsCommand extends Command ...@@ -980,7 +980,7 @@ class GroupsCommand extends Command
{ {
$group = $this->user->getGroups(); $group = $this->user->getGroups();
$groups=array(); $groups=array();
while ($group->fetch()) { while ($group instanceof User_group && $group->fetch()) {
$groups[]=$group->nickname; $groups[]=$group->nickname;
} }
if(count($groups)==0){ if(count($groups)==0){
......
...@@ -58,7 +58,7 @@ class GroupsNav extends MoreMenu ...@@ -58,7 +58,7 @@ class GroupsNav extends MoreMenu
function haveGroups() function haveGroups()
{ {
return (!empty($this->groups) && ($this->groups->N > 0)); return ($this->groups instanceof User_group && $this->groups->N > 0);
} }
function tag() function tag()
...@@ -70,7 +70,7 @@ class GroupsNav extends MoreMenu ...@@ -70,7 +70,7 @@ class GroupsNav extends MoreMenu
{ {
$items = array(); $items = array();
while ($this->groups->fetch()) { while ($this->groups instanceof User_group && $this->groups->fetch()) {
$items[] = array('placeholder', $items[] = array('placeholder',
array('nickname' => $this->groups->nickname, array('nickname' => $this->groups->nickname,
'mainpage' => $this->groups->homeUrl()), 'mainpage' => $this->groups->homeUrl()),
......
...@@ -270,13 +270,12 @@ class ProfileAction extends Action ...@@ -270,13 +270,12 @@ class ProfileAction extends Action
$this->text($this->profile->getGroups(0, null)->N); $this->text($this->profile->getGroups(0, null)->N);
$this->elementEnd('h2'); $this->elementEnd('h2');
if ($groups) { if ($groups instanceof User_group) {
$gml = new GroupMiniList($groups, $this->profile, $this); $gml = new GroupMiniList($groups, $this->profile, $this);
$cnt = $gml->show(); $cnt = $gml->show();
if ($cnt == 0) { } else {
// TRANS: Text for user user group membership statistics if user is not a member of any group. // TRANS: Text for user user group membership statistics if user is not a member of any group.
$this->element('p', null, _('(None)')); $this->element('p', null, _('(None)'));
}
} }
Event::handle('EndShowGroupsMiniList', array($this)); Event::handle('EndShowGroupsMiniList', array($this));
......
...@@ -94,7 +94,7 @@ class ToSelector extends Widget ...@@ -94,7 +94,7 @@ class ToSelector extends Widget
$groups = $this->user->getGroups(); $groups = $this->user->getGroups();
while ($groups->fetch()) { while ($groups instanceof User_group && $groups->fetch()) {
$value = 'group:'.$groups->id; $value = 'group:'.$groups->id;
if (($this->to instanceof User_group) && $this->to->id == $groups->id) { if (($this->to instanceof User_group) && $this->to->id == $groups->id) {
$default = $value; $default = $value;
......
...@@ -320,7 +320,7 @@ function common_set_user($user) ...@@ -320,7 +320,7 @@ function common_set_user($user)
} else if (is_string($user)) { } else if (is_string($user)) {
$nickname = $user; $nickname = $user;
$user = User::getKV('nickname', $nickname); $user = User::getKV('nickname', $nickname);
} else if (!($user instanceof User)) { } else if (!$user instanceof User) {
return false; return false;
} }
...@@ -433,7 +433,7 @@ function common_remembered_user() ...@@ -433,7 +433,7 @@ function common_remembered_user()
$user = User::getKV('id', $rm->user_id); $user = User::getKV('id', $rm->user_id);
if (!$user) { if (!$user instanceof User) {
common_log(LOG_WARNING, 'No such user for rememberme: ' . $rm->user_id); common_log(LOG_WARNING, 'No such user for rememberme: ' . $rm->user_id);
common_forgetme(); common_forgetme();
return null; return null;
...@@ -488,8 +488,8 @@ function common_current_user() ...@@ -488,8 +488,8 @@ function common_current_user()
common_ensure_session(); common_ensure_session();
$id = isset($_SESSION['userid']) ? $_SESSION['userid'] : false; $id = isset($_SESSION['userid']) ? $_SESSION['userid'] : false;
if ($id) { if ($id) {
$user = User::getKV($id); $user = User::getKV('id', $id);
if ($user) { if ($user instanceof User) {
$_cur = $user; $_cur = $user;
return $_cur; return $_cur;
} }
...@@ -584,13 +584,10 @@ function common_canonical_email($email) ...@@ -584,13 +584,10 @@ function common_canonical_email($email)
* @param Notice $notice in whose context we're working * @param Notice $notice in whose context we're working
* @return string partially rendered HTML * @return string partially rendered HTML
*/ */
function common_render_content($text, $notice) function common_render_content($text, Notice $notice)
{ {
$r = common_render_text($text); $r = common_render_text($text);
$id = $notice->profile_id;
$r = common_linkify_mentions($r, $notice); $r = common_linkify_mentions($r, $notice);
$r = preg_replace_callback('/(^|[\s\.\,\:\;]+)!(' . Nickname::DISPLAY_FMT . ')/',
function ($m) { return "{$m[1]}!".common_group_link($id, $m[2]); }, $r);
return $r; return $r;
} }
...@@ -677,35 +674,39 @@ function common_linkify_mention($mention) ...@@ -677,35 +674,39 @@ function common_linkify_mention($mention)
*/ */
function common_find_mentions($text, $notice) function common_find_mentions($text, $notice)
{ {
$mentions = array(); try {
$sender = Profile::getKV('id', $notice->profile_id);
$sender = Profile::getKV('id', $notice->profile_id); } catch (NoProfileException $e) {
return array();
if (empty($sender)) {
return $mentions;
} }
$mentions = array();
if (Event::handle('StartFindMentions', array($sender, $text, &$mentions))) { if (Event::handle('StartFindMentions', array($sender, $text, &$mentions))) {
// Get the context of the original notice, if any // Get the context of the original notice, if any
$originalAuthor = null; $origAuthor = null;
$originalNotice = null; $origNotice = null;
$originalMentions = array(); $origMentions = array();
// Is it a reply? // Is it a reply?
if (!empty($notice) && !empty($notice->reply_to)) { if ($notice instanceof Notice) {
$originalNotice = Notice::getKV('id', $notice->reply_to); try {
if (!empty($originalNotice)) { $origNotice = $notice->getParent();
$originalAuthor = Profile::getKV('id', $originalNotice->profile_id); $origAuthor = $origNotice->getProfile();
$ids = $originalNotice->getReplies(); $ids = $origNotice->getReplies();
foreach ($ids as $id) { foreach ($ids as $id) {
$repliedTo = Profile::getKV('id', $id); $repliedTo = Profile::getKV('id', $id);
if (!empty($repliedTo)) { if ($repliedTo instanceof Profile) {
$originalMentions[$repliedTo->nickname] = $repliedTo; $origMentions[$repliedTo->nickname] = $repliedTo;
} }
} }
} catch (NoProfileException $e) {
common_log(LOG_WARNING, sprintf('Notice %d author profile id %d does not exist', $origNotice->id, $origNotice->profile_id));
} catch (ServerException $e) {
common_log(LOG_WARNING, __METHOD__ . ' got exception: ' . $e->getMessage());
} }
} }
...@@ -723,19 +724,19 @@ function common_find_mentions($text, $notice) ...@@ -723,19 +724,19 @@ function common_find_mentions($text, $notice)
// Start with conversation context, then go to // Start with conversation context, then go to
// sender context. // sender context.
if (!empty($originalAuthor) && $originalAuthor->nickname == $nickname) { if ($origAuthor instanceof Profile && $origAuthor->nickname == $nickname) {
$mentioned = $originalAuthor; $mentioned = $origAuthor;
} else if (!empty($originalMentions) && } else if (!empty($origMentions) &&
array_key_exists($nickname, $originalMentions)) { array_key_exists($nickname, $origMentions)) {
$mentioned = $originalMentions[$nickname]; $mentioned = $origMentions[$nickname];
} else { } else {
$mentioned = common_relative_profile($sender, $nickname); $mentioned = common_relative_profile($sender, $nickname);
} }
if (!empty($mentioned)) { if ($mentioned instanceof Profile) {
$user = User::getKV('id', $mentioned->id); $user = User::getKV('id', $mentioned->id);
if ($user) { if ($user instanceof User) {
$url = common_local_url('userbyid', array('id' => $user->id)); $url = common_local_url('userbyid', array('id' => $user->id));
} else { } else {
$url = $mentioned->profileurl; $url = $mentioned->profileurl;
...@@ -757,26 +758,42 @@ function common_find_mentions($text, $notice) ...@@ -757,26 +758,42 @@ function common_find_mentions($text, $notice)
// @#tag => mention of all subscriptions tagged 'tag' // @#tag => mention of all subscriptions tagged 'tag'
preg_match_all('/(?:^|[\s\.\,\:\;]+)@#([\pL\pN_\-\.]{1,64})/', preg_match_all('/(?:^|[\s\.\,\:\;]+)@#([\pL\pN_\-\.]{1,64})/',
$text, $text, $hmatches, PREG_OFFSET_CAPTURE);
$hmatches,
PREG_OFFSET_CAPTURE);
foreach ($hmatches[1] as $hmatch) { foreach ($hmatches[1] as $hmatch) {
$tag = common_canonical_tag($hmatch[0]); $tag = common_canonical_tag($hmatch[0]);
$plist = Profile_list::getByTaggerAndTag($sender->id, $tag); $plist = Profile_list::getByTaggerAndTag($sender->id, $tag);
if (!empty($plist) && !$plist->private) { if (!$plist instanceof Profile_list || $plist->private) {
$tagged = $sender->getTaggedSubscribers($tag); continue;
}
$tagged = $sender->getTaggedSubscribers($tag);
$url = common_local_url('showprofiletag', $url = common_local_url('showprofiletag',
array('tagger' => $sender->nickname, array('tagger' => $sender->nickname,
'tag' => $tag)); 'tag' => $tag));
$mentions[] = array('mentioned' => $tagged,
'text' => $hmatch[0],
'position' => $hmatch[1],
'url' => $url);
}
$mentions[] = array('mentioned' => $tagged, preg_match_all('/(?:^|[\s\.\,\:\;]+)!(' . Nickname::DISPLAY_FMT . ')/',
'text' => $hmatch[0], $text, $hmatches, PREG_OFFSET_CAPTURE);
'position' => $hmatch[1], foreach ($hmatches[1] as $hmatch) {
'url' => $url); $nickname = Nickname::normalize($hmatch[0]);
$group = User_group::getForNickname($nickname, $sender);
if (!$group instanceof User_group || !$sender->isMember($group)) {
continue;
} }
$profile = $group->getProfile();
$mentions[] = array('mentioned' => $profile,
'text' => $hmatch[0],
'position' => $hmatch[1],
'url' => $group->permalink,
'title' => $group->getFancyName());
} }
Event::handle('EndFindMentions', array($sender, $text, &$mentions)); Event::handle('EndFindMentions', array($sender, $text, &$mentions));
...@@ -1146,35 +1163,6 @@ function common_valid_profile_tag($str) ...@@ -1146,35 +1163,6 @@ function common_valid_profile_tag($str)
return preg_match('/^[A-Za-z0-9_\-\.]{1,64}$/', $str); return preg_match('/^[A-Za-z0-9_\-\.]{1,64}$/', $str);
} }
/**
*
* @param <type> $sender_id
* @param <type> $nickname
* @return <type>
* @access private
*/
function common_group_link($sender_id, $nickname)
{
$sender = Profile::getKV($sender_id);
$group = User_group::getForNickname($nickname, $sender);
if ($sender && $group && $sender->isMember($group)) {
$attrs = array('href' => $group->permalink(),
'class' => 'url');
if (!empty($group->fullname)) {
$attrs['title'] = $group->getFancyName();
}
$xs = new XMLStringer();
$xs->elementStart('span', 'vcard');
$xs->elementStart('a', $attrs);
$xs->element('span', 'fn nickname group', $nickname);
$xs->elementEnd('a');
$xs->elementEnd('span');
return $xs->getString();
} else {
return $nickname;
}
}
/** /**
* Resolve an ambiguous profile nickname reference, checking in following order: * Resolve an ambiguous profile nickname reference, checking in following order:
* - profiles that $sender subscribes to * - profiles that $sender subscribes to
...@@ -1222,7 +1210,7 @@ function common_relative_profile($sender, $nickname, $dt=null) ...@@ -1222,7 +1210,7 @@ function common_relative_profile($sender, $nickname, $dt=null)
return $recipient; return $recipient;
} }
// If this is a local user, try to find a local user with that nickname. // If this is a local user, try to find a local user with that nickname.
$sender = User::getKV($sender->id); $sender = User::getKV('id', $sender->id);
if ($sender instanceof User) { if ($sender instanceof User) {
$recipient_user = User::getKV('nickname', $nickname); $recipient_user = User::getKV('nickname', $nickname);
if ($recipient_user instanceof User) { if ($recipient_user instanceof User) {
...@@ -2020,8 +2008,8 @@ function common_profile_uri($profile) ...@@ -2020,8 +2008,8 @@ function common_profile_uri($profile)
if (!empty($profile)) { if (!empty($profile)) {
if (Event::handle('StartCommonProfileURI', array($profile, &$uri))) { if (Event::handle('StartCommonProfileURI', array($profile, &$uri))) {
$user = User::getKV($profile->id); $user = User::getKV('id', $profile->id);
if (!empty($user)) { if ($user instanceof User) {
$uri = $user->uri; $uri = $user->uri;
} }
Event::handle('EndCommonProfileURI', array($profile, &$uri)); Event::handle('EndCommonProfileURI', array($profile, &$uri));
......