Profile_tag.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. <?php
  2. /**
  3. * Table Definition for profile_tag
  4. */
  5. class Profile_tag extends Managed_DataObject
  6. {
  7. public $__table = 'profile_tag'; // table name
  8. public $tagger; // int(4) primary_key not_null
  9. public $tagged; // int(4) primary_key not_null
  10. public $tag; // varchar(64) primary_key not_null
  11. public $modified; // datetime() not_null default_CURRENT_TIMESTAMP
  12. public static function schemaDef()
  13. {
  14. return array(
  15. 'fields' => array(
  16. 'tagger' => array('type' => 'int', 'not null' => true, 'description' => 'user making the tag'),
  17. 'tagged' => array('type' => 'int', 'not null' => true, 'description' => 'profile tagged'),
  18. 'tag' => array('type' => 'varchar', 'length' => 64, 'not null' => true, 'description' => 'hash tag associated with this notice'),
  19. 'modified' => array('type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date the tag was added'),
  20. ),
  21. 'primary key' => array('tagger', 'tagged', 'tag'),
  22. 'foreign keys' => array(
  23. 'profile_tag_tagger_fkey' => array('profile', array('tagger' => 'id')),
  24. 'profile_tag_tagged_fkey' => array('profile', array('tagged' => 'id')),
  25. 'profile_tag_tag_fkey' => array('profile_list', array('tag' => 'tag')),
  26. ),
  27. 'indexes' => array(
  28. 'profile_tag_modified_idx' => array('modified'),
  29. 'profile_tag_tagger_tag_idx' => array('tagger', 'tag'),
  30. 'profile_tag_tagged_idx' => array('tagged'),
  31. ),
  32. );
  33. }
  34. function links()
  35. {
  36. return array('tagger,tag' => 'profile_list:tagger,tag');
  37. }
  38. function getMeta()
  39. {
  40. return Profile_list::pkeyGet(array('tagger' => $this->tagger, 'tag' => $this->tag));
  41. }
  42. static function getSelfTagsArray(Profile $target)
  43. {
  44. return self::getTagsArray($target->getID(), $target->getID(), $target);
  45. }
  46. static function setSelfTags(Profile $target, array $newtags, array $privacy=array())
  47. {
  48. return self::setTags($target->getID(), $target->getID(), $newtags, $privacy);
  49. }
  50. static function getTags($tagger, $tagged, $auth_user=null) {
  51. $profile_list = new Profile_list();
  52. $include_priv = 1;
  53. if (!($auth_user instanceof User ||
  54. $auth_user instanceof Profile) ||
  55. ($auth_user->id !== $tagger)) {
  56. $profile_list->private = false;
  57. $include_priv = 0;
  58. }
  59. $key = sprintf('profile_tag:tagger_tagged_privacy:%d-%d-%d', $tagger, $tagged, $include_priv);
  60. $tags = Profile_list::getCached($key);
  61. if ($tags !== false) {
  62. return $tags;
  63. }
  64. $qry = 'select profile_list.* from profile_list left join '.
  65. 'profile_tag on (profile_list.tag = profile_tag.tag and '.
  66. 'profile_list.tagger = profile_tag.tagger) where '.
  67. 'profile_tag.tagger = %d and profile_tag.tagged = %d ';
  68. $qry = sprintf($qry, $tagger, $tagged);
  69. if (!$include_priv) {
  70. $qry .= ' and profile_list.private = 0';
  71. }
  72. $profile_list->query($qry);
  73. Profile_list::setCache($key, $profile_list);
  74. return $profile_list;
  75. }
  76. static function getTagsArray($tagger, $tagged, Profile $scoped=null)
  77. {
  78. $ptag = new Profile_tag();
  79. $qry = sprintf('select profile_tag.tag '.
  80. 'from profile_tag join profile_list '.
  81. ' on (profile_tag.tagger = profile_list.tagger ' .
  82. ' and profile_tag.tag = profile_list.tag) ' .
  83. 'where profile_tag.tagger = %d ' .
  84. 'and profile_tag.tagged = %d ',
  85. $tagger, $tagged);
  86. if (!$scoped instanceof Profile || $scoped->getID() !== $tagger) {
  87. $qry .= 'and profile_list.private = 0';
  88. }
  89. $tags = array();
  90. $ptag->query($qry);
  91. while ($ptag->fetch()) {
  92. $tags[] = $ptag->tag;
  93. }
  94. return $tags;
  95. }
  96. static function setTags($tagger, $tagged, array $newtags, array $privacy=array()) {
  97. $newtags = array_unique($newtags);
  98. $oldtags = self::getTagsArray($tagger, $tagged, Profile::getByID($tagger));
  99. $ptag = new Profile_tag();
  100. // Delete stuff that's in old and not in new
  101. $to_delete = array_diff($oldtags, $newtags);
  102. // Insert stuff that's in new and not in old
  103. $to_insert = array_diff($newtags, $oldtags);
  104. foreach ($to_delete as $deltag) {
  105. self::unTag($tagger, $tagged, $deltag);
  106. }
  107. foreach ($to_insert as $instag) {
  108. $private = isset($privacy[$instag]) ? $privacy[$instag] : false;
  109. self::setTag($tagger, $tagged, $instag, null, $private);
  110. }
  111. return true;
  112. }
  113. # set a single tag
  114. static function setTag($tagger, $tagged, $tag, $desc=null, $private=false) {
  115. $ptag = Profile_tag::pkeyGet(array('tagger' => $tagger,
  116. 'tagged' => $tagged,
  117. 'tag' => $tag));
  118. # if tag already exists, return it
  119. if ($ptag instanceof Profile_tag) {
  120. return $ptag;
  121. }
  122. $tagger_profile = Profile::getByID($tagger);
  123. $tagged_profile = Profile::getByID($tagged);
  124. if (Event::handle('StartTagProfile', array($tagger_profile, $tagged_profile, $tag))) {
  125. if (!$tagger_profile->canTag($tagged_profile)) {
  126. // TRANS: Client exception thrown trying to set a tag for a user that cannot be tagged.
  127. throw new ClientException(_('You cannot tag this user.'));
  128. }
  129. $tags = new Profile_list();
  130. $tags->tagger = $tagger;
  131. $count = (int) $tags->count('distinct tag');
  132. if ($count >= common_config('peopletag', 'maxtags')) {
  133. // TRANS: Client exception thrown trying to set more tags than allowed.
  134. throw new ClientException(sprintf(_('You already have created %d or more tags ' .
  135. 'which is the maximum allowed number of tags. ' .
  136. 'Try using or deleting some existing tags.'),
  137. common_config('peopletag', 'maxtags')));
  138. }
  139. $plist = new Profile_list();
  140. $plist->query('BEGIN');
  141. $profile_list = Profile_list::ensureTag($tagger, $tag, $desc, $private);
  142. if ($profile_list->taggedCount() >= common_config('peopletag', 'maxpeople')) {
  143. // TRANS: Client exception thrown when trying to add more people than allowed to a list.
  144. throw new ClientException(sprintf(_('You already have %1$d or more people in list %2$s, ' .
  145. 'which is the maximum allowed number. ' .
  146. 'Try unlisting others first.'),
  147. common_config('peopletag', 'maxpeople'), $tag));
  148. }
  149. $newtag = new Profile_tag();
  150. $newtag->tagger = $tagger;
  151. $newtag->tagged = $tagged;
  152. $newtag->tag = $tag;
  153. $result = $newtag->insert();
  154. if (!$result) {
  155. common_log_db_error($newtag, 'INSERT', __FILE__);
  156. $plist->query('ROLLBACK');
  157. return false;
  158. }
  159. try {
  160. $plist->query('COMMIT');
  161. Event::handle('EndTagProfile', array($newtag));
  162. } catch (Exception $e) {
  163. $newtag->delete();
  164. $profile_list->delete();
  165. throw $e;
  166. }
  167. $profile_list->taggedCount(true);
  168. self::blowCaches($tagger, $tagged);
  169. }
  170. return $newtag;
  171. }
  172. static function unTag($tagger, $tagged, $tag) {
  173. $ptag = Profile_tag::pkeyGet(array('tagger' => $tagger,
  174. 'tagged' => $tagged,
  175. 'tag' => $tag));
  176. if (!$ptag) {
  177. return true;
  178. }
  179. if (Event::handle('StartUntagProfile', array($ptag))) {
  180. $orig = clone($ptag);
  181. $result = $ptag->delete();
  182. if ($result === false) {
  183. common_log_db_error($this, 'DELETE', __FILE__);
  184. return false;
  185. }
  186. Event::handle('EndUntagProfile', array($orig));
  187. $profile_list = Profile_list::pkeyGet(array('tag' => $tag, 'tagger' => $tagger));
  188. if (!empty($profile_list)) {
  189. $profile_list->taggedCount(true);
  190. }
  191. self::blowCaches($tagger, $tagged);
  192. return true;
  193. }
  194. }
  195. // @fixme: move this to Profile_list?
  196. static function cleanup($profile_list) {
  197. $ptag = new Profile_tag();
  198. $ptag->tagger = $profile_list->tagger;
  199. $ptag->tag = $profile_list->tag;
  200. $ptag->find();
  201. while($ptag->fetch()) {
  202. if (Event::handle('StartUntagProfile', array($ptag))) {
  203. $orig = clone($ptag);
  204. $result = $ptag->delete();
  205. if (!$result) {
  206. common_log_db_error($this, 'DELETE', __FILE__);
  207. }
  208. Event::handle('EndUntagProfile', array($orig));
  209. }
  210. }
  211. }
  212. // move a tag!
  213. static function moveTag($orig, $new) {
  214. $tags = new Profile_tag();
  215. $qry = 'UPDATE profile_tag SET ' .
  216. 'tag = "%s", tagger = "%s" ' .
  217. 'WHERE tag = "%s" ' .
  218. 'AND tagger = "%s"';
  219. $result = $tags->query(sprintf($qry,
  220. $tags->escape($new->tag),
  221. $tags->escape($new->tagger),
  222. $tags->escape($orig->tag),
  223. $tags->escape($orig->tagger)));
  224. if ($result === false) {
  225. common_log_db_error($tags, 'UPDATE', __FILE__);
  226. throw new Exception('Could not move Profile_tag, see db log for details.');
  227. }
  228. return $result;
  229. }
  230. static function blowCaches($tagger, $tagged) {
  231. foreach (array(0, 1) as $perm) {
  232. self::blow(sprintf('profile_tag:tagger_tagged_privacy:%d-%d-%d', $tagger, $tagged, $perm));
  233. }
  234. return true;
  235. }
  236. // Return profiles with a given tag
  237. static function getTagged($tagger, $tag) {
  238. $profile = new Profile();
  239. $profile->query('SELECT profile.* ' .
  240. 'FROM profile JOIN profile_tag ' .
  241. 'ON profile.id = profile_tag.tagged ' .
  242. 'WHERE profile_tag.tagger = ' . $profile->escape($tagger) . ' ' .
  243. 'AND profile_tag.tag = "' . $profile->escape($tag) . '" ');
  244. $tagged = array();
  245. while ($profile->fetch()) {
  246. $tagged[] = clone($profile);
  247. }
  248. return true;
  249. }
  250. function insert()
  251. {
  252. $result = parent::insert();
  253. if ($result) {
  254. self::blow('profile_list:tagged_count:%d:%s',
  255. $this->tagger,
  256. $this->tag);
  257. }
  258. return $result;
  259. }
  260. function delete($useWhere=false)
  261. {
  262. $result = parent::delete($useWhere);
  263. if ($result !== false) {
  264. self::blow('profile_list:tagged_count:%d:%s',
  265. $this->tagger,
  266. $this->tag);
  267. }
  268. return $result;
  269. }
  270. }