start.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. <?php
  2. /**
  3. * Elgg wire plugin
  4. *
  5. * Forked from Curverider's version
  6. *
  7. * JHU/APL Contributors:
  8. * Cash Costello
  9. * Clark Updike
  10. * John Norton
  11. * Max Thomas
  12. * Nathan Koterba
  13. */
  14. elgg_register_event_handler('init', 'system', 'thewire_init');
  15. /**
  16. * The Wire initialization
  17. */
  18. function thewire_init() {
  19. // register the wire's JavaScript
  20. $thewire_js = elgg_get_simplecache_url('js', 'thewire');
  21. elgg_register_simplecache_view('js/thewire');
  22. elgg_register_js('elgg.thewire', $thewire_js, 'footer');
  23. elgg_register_ajax_view('thewire/previous');
  24. // add a site navigation item
  25. $item = new ElggMenuItem('thewire', elgg_echo('thewire'), 'thewire/all');
  26. elgg_register_menu_item('site', $item);
  27. // owner block menu
  28. elgg_register_plugin_hook_handler('register', 'menu:owner_block', 'thewire_owner_block_menu');
  29. // remove edit and access and add thread, reply, view previous
  30. elgg_register_plugin_hook_handler('register', 'menu:entity', 'thewire_setup_entity_menu_items');
  31. // Extend system CSS with our own styles, which are defined in the thewire/css view
  32. elgg_extend_view('css/elgg', 'thewire/css');
  33. //extend views
  34. elgg_extend_view('activity/thewire', 'thewire/activity_view');
  35. elgg_extend_view('profile/status', 'thewire/profile_status');
  36. elgg_extend_view('js/initialise_elgg', 'thewire/js/textcounter');
  37. // Register a page handler, so we can have nice URLs
  38. elgg_register_page_handler('thewire', 'thewire_page_handler');
  39. // Register a URL handler for thewire posts
  40. elgg_register_entity_url_handler('object', 'thewire', 'thewire_url');
  41. elgg_register_widget_type('thewire', elgg_echo('thewire'), elgg_echo("thewire:widget:desc"));
  42. // Register for search
  43. elgg_register_entity_type('object', 'thewire');
  44. // Register granular notification for this type
  45. register_notification_object('object', 'thewire', elgg_echo('thewire:notify:subject'));
  46. // Listen to notification events and supply a more useful message
  47. elgg_register_plugin_hook_handler('notify:entity:message', 'object', 'thewire_notify_message');
  48. // Register actions
  49. $action_base = elgg_get_plugins_path() . 'thewire/actions';
  50. elgg_register_action("thewire/add", "$action_base/add.php");
  51. elgg_register_action("thewire/delete", "$action_base/delete.php");
  52. elgg_register_action("thewire/get_smiley", "$action_base/get_smiley.php");
  53. elgg_register_plugin_hook_handler('unit_test', 'system', 'thewire_test');
  54. }
  55. /**
  56. * The wire page handler
  57. *
  58. * Supports:
  59. * thewire/all View site wire posts
  60. * thewire/owner/<username> View this user's wire posts
  61. * thewire/following/<username> View the posts of those this user follows
  62. * thewire/reply/<guid> Reply to a post
  63. * thewire/view/<guid> View a post
  64. * thewire/thread/<id> View a conversation thread
  65. * thewire/tag/<tag> View wire posts tagged with <tag>
  66. *
  67. * @param array $page From the page_handler function
  68. * @return bool
  69. */
  70. function thewire_page_handler($page) {
  71. $base_dir = elgg_get_plugins_path() . 'thewire/pages/thewire';
  72. if (!isset($page[0])) {
  73. $page = array('all');
  74. }
  75. switch ($page[0]) {
  76. case "all":
  77. include "$base_dir/everyone.php";
  78. break;
  79. case "friends":
  80. include "$base_dir/friends.php";
  81. break;
  82. case "owner":
  83. include "$base_dir/owner.php";
  84. break;
  85. case "view":
  86. if (isset($page[1])) {
  87. set_input('guid', $page[1]);
  88. }
  89. include "$base_dir/view.php";
  90. break;
  91. case "thread":
  92. if (isset($page[1])) {
  93. set_input('thread_id', $page[1]);
  94. }
  95. include "$base_dir/thread.php";
  96. break;
  97. case "reply":
  98. if (isset($page[1])) {
  99. set_input('guid', $page[1]);
  100. }
  101. include "$base_dir/reply.php";
  102. break;
  103. case "tag":
  104. if (isset($page[1])) {
  105. set_input('tag', $page[1]);
  106. }
  107. include "$base_dir/tag.php";
  108. break;
  109. case "previous":
  110. if (isset($page[1])) {
  111. set_input('guid', $page[1]);
  112. }
  113. include "$base_dir/previous.php";
  114. break;
  115. default:
  116. return false;
  117. }
  118. return true;
  119. }
  120. /**
  121. * Override the url for a wire post to return the thread
  122. *
  123. * @param ElggObject $thewirepost Wire post object
  124. */
  125. function thewire_url($thewirepost) {
  126. global $CONFIG;
  127. return $CONFIG->url . "thewire/view/" . $thewirepost->guid;
  128. }
  129. /**
  130. * Returns the notification body
  131. *
  132. * @return $string
  133. */
  134. function thewire_notify_message($hook, $entity_type, $returnvalue, $params) {
  135. global $CONFIG;
  136. $entity = $params['entity'];
  137. if (($entity instanceof ElggEntity) && ($entity->getSubtype() == 'thewire')) {
  138. $descr = $entity->description;
  139. $owner = $entity->getOwnerEntity();
  140. if ($entity->reply) {
  141. // have to do this because of poor design of Elgg notification system
  142. $parent_post = get_entity(get_input('parent_guid'));
  143. if ($parent_post) {
  144. $parent_owner = $parent_post->getOwnerEntity();
  145. }
  146. $body = sprintf(elgg_echo('thewire:notify:reply'), $owner->name, $parent_owner->name);
  147. } else {
  148. $body = sprintf(elgg_echo('thewire:notify:post'), $owner->name);
  149. }
  150. $body .= "\n\n" . $descr . "\n\n";
  151. $body .= elgg_echo('thewire') . ": {$CONFIG->url}thewire";
  152. return $body;
  153. }
  154. return $returnvalue;
  155. }
  156. /**
  157. * Get an array of hashtags from a text string
  158. *
  159. * @param string $text The text of a post
  160. * @return array
  161. */
  162. function thewire_get_hashtags($text) {
  163. // beginning of text or white space followed by hashtag
  164. // hashtag must begin with # and contain at least one character not digit, space, or punctuation
  165. $matches = array();
  166. preg_match_all('/(^|[^\w])#(\w*[^\s\d!-\/:-@]+\w*)/', $text, $matches);
  167. return $matches[2];
  168. }
  169. /**
  170. * Replace urls, hash tags, and @'s by links
  171. *
  172. * @param string $text The text of a post
  173. * @return string
  174. */
  175. function thewire_filter($text) {
  176. global $CONFIG;
  177. $text = ' ' . $text;
  178. // email addresses
  179. $text = preg_replace(
  180. '/(^|[^\w])([\w\-\.]+)@(([0-9a-z-]+\.)+[0-9a-z]{2,})/i',
  181. '$1<a href="mailto:$2@$3">$2@$3</a>',
  182. $text);
  183. // links
  184. $text = parse_urls($text);
  185. // usernames
  186. $text = preg_replace(
  187. '/(^|[^\w])@([\p{L}\p{Nd}._]+)/u',
  188. '$1<a href="' . $CONFIG->wwwroot . 'thewire/owner/$2">@$2</a>',
  189. $text);
  190. // hashtags
  191. $text = preg_replace(
  192. '/(^|[^\w])#(\w*[^\s\d!-\/:-@]+\w*)/',
  193. '$1<a href="' . $CONFIG->wwwroot . 'thewire/tag/$2">#$2</a>',
  194. $text);
  195. $text = trim($text);
  196. return $text;
  197. }
  198. /**
  199. * Create a new wire post.
  200. *
  201. * @param string $text The post text
  202. * @param int $userid The user's guid
  203. * @param int $access_id Public/private etc
  204. * @param int $parent_guid Parent post guid (if any)
  205. * @param string $method The method (default: 'site')
  206. * @return guid or false if failure
  207. */
  208. function thewire_save_post($text, $userid, $access_id, $parent_guid = 0, $method = "site") {
  209. $post = new ElggObject();
  210. $post->subtype = "thewire";
  211. $post->owner_guid = $userid;
  212. $post->access_id = $access_id;
  213. // only 200 characters allowed
  214. $text = elgg_substr($text, 0, 200);
  215. // no html tags allowed so we escape
  216. $post->description = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
  217. $post->method = $method; //method: site, email, api, ...
  218. $tags = thewire_get_hashtags($text);
  219. if ($tags) {
  220. $post->tags = $tags;
  221. }
  222. // must do this before saving so notifications pick up that this is a reply
  223. if ($parent_guid) {
  224. $post->reply = true;
  225. }
  226. $guid = $post->save();
  227. // set thread guid
  228. if ($parent_guid) {
  229. $post->addRelationship($parent_guid, 'parent');
  230. // name conversation threads by guid of first post (works even if first post deleted)
  231. $parent_post = get_entity($parent_guid);
  232. $post->wire_thread = $parent_post->wire_thread;
  233. } else {
  234. // first post in this thread
  235. $post->wire_thread = $guid;
  236. }
  237. if ($guid) {
  238. add_to_river('river/object/thewire/create', 'create', $post->owner_guid, $post->guid);
  239. // let other plugins know we are setting a user status
  240. $params = array(
  241. 'entity' => $post,
  242. 'user' => $post->getOwnerEntity(),
  243. 'message' => $post->description,
  244. 'url' => $post->getURL(),
  245. 'origin' => 'thewire',
  246. );
  247. elgg_trigger_plugin_hook('status', 'user', $params);
  248. }
  249. return $guid;
  250. }
  251. /**
  252. * Send notification to poster of parent post if not notified already
  253. *
  254. * @param int $guid The guid of the reply wire post
  255. * @param int $parent_guid The guid of the original wire post
  256. * @param ElggUser $user The user who posted the reply
  257. * @return void
  258. */
  259. function thewire_send_response_notification($guid, $parent_guid, $user) {
  260. $parent_owner = get_entity($parent_guid)->getOwnerEntity();
  261. $user = elgg_get_logged_in_user_entity();
  262. // check to make sure user is not responding to self
  263. if ($parent_owner->guid != $user->guid) {
  264. // check if parent owner has notification for this user
  265. $send_response = true;
  266. global $NOTIFICATION_HANDLERS;
  267. foreach ($NOTIFICATION_HANDLERS as $method => $foo) {
  268. if (check_entity_relationship($parent_owner->guid, 'notify' . $method, $user->guid)) {
  269. $send_response = false;
  270. }
  271. }
  272. // create the notification message
  273. if ($send_response) {
  274. // grab same notification message that goes to everyone else
  275. $params = array(
  276. 'entity' => get_entity($guid),
  277. 'method' => "email",
  278. );
  279. $msg = thewire_notify_message("", "", "", $params);
  280. notify_user(
  281. $parent_owner->guid,
  282. $user->guid,
  283. elgg_echo('thewire:notify:subject'),
  284. $msg);
  285. }
  286. }
  287. }
  288. /**
  289. * Get the latest wire guid - used for ajax update
  290. *
  291. * @return guid
  292. */
  293. function thewire_latest_guid() {
  294. $post = elgg_get_entities(array(
  295. 'type' => 'object',
  296. 'subtype' => 'thewire',
  297. 'limit' => 1,
  298. ));
  299. if ($post) {
  300. return $post[0]->guid;
  301. } else {
  302. return 0;
  303. }
  304. }
  305. /**
  306. * Get the parent of a wire post
  307. *
  308. * @param int $post_guid The guid of the reply
  309. * @return ElggObject or null
  310. */
  311. function thewire_get_parent($post_guid) {
  312. $parents = elgg_get_entities_from_relationship(array(
  313. 'relationship' => 'parent',
  314. 'relationship_guid' => $post_guid,
  315. ));
  316. if ($parents) {
  317. return $parents[0];
  318. }
  319. return null;
  320. }
  321. /**
  322. * Sets up the entity menu for thewire
  323. *
  324. * Adds reply, thread, and view previous links. Removes edit and access.
  325. *
  326. * @param string $hook Hook name
  327. * @param string $type Hook type
  328. * @param array $value Array of menu items
  329. * @param array $params Array with the entity
  330. * @return array
  331. */
  332. function thewire_setup_entity_menu_items($hook, $type, $value, $params) {
  333. $handler = elgg_extract('handler', $params, false);
  334. if ($handler != 'thewire') {
  335. return $value;
  336. }
  337. foreach ($value as $index => $item) {
  338. $name = $item->getName();
  339. if ($name == 'access' || $name == 'edit') {
  340. unset($value[$index]);
  341. }
  342. }
  343. $entity = $params['entity'];
  344. if (elgg_is_logged_in()) {
  345. $options = array(
  346. 'name' => 'reply',
  347. 'text' => elgg_echo('thewire:reply'),
  348. 'href' => "thewire/reply/$entity->guid",
  349. 'priority' => 150,
  350. );
  351. $value[] = ElggMenuItem::factory($options);
  352. }
  353. if ($entity->reply) {
  354. $options = array(
  355. 'name' => 'previous',
  356. 'text' => elgg_echo('thewire:previous'),
  357. 'href' => "thewire/previous/$entity->guid",
  358. 'priority' => 160,
  359. 'link_class' => 'thewire-previous',
  360. 'title' => elgg_echo('thewire:previous:help'),
  361. );
  362. $value[] = ElggMenuItem::factory($options);
  363. }
  364. $options = array(
  365. 'name' => 'thread',
  366. 'text' => elgg_echo('thewire:thread'),
  367. 'href' => "thewire/thread/$entity->wire_thread",
  368. 'priority' => 170,
  369. );
  370. $value[] = ElggMenuItem::factory($options);
  371. return $value;
  372. }
  373. /**
  374. * Add a menu item to an ownerblock
  375. *
  376. * @return array
  377. */
  378. function thewire_owner_block_menu($hook, $type, $return, $params) {
  379. if (elgg_instanceof($params['entity'], 'user')) {
  380. $url = "thewire/owner/{$params['entity']->username}";
  381. $item = new ElggMenuItem('thewire', elgg_echo('item:object:thewire'), $url);
  382. $return[] = $item;
  383. }
  384. return $return;
  385. }
  386. /**
  387. * Runs unit tests for the wire
  388. *
  389. * @return array
  390. */
  391. function thewire_test($hook, $type, $value, $params) {
  392. global $CONFIG;
  393. $value[] = $CONFIG->pluginspath . 'thewire/tests/regex.php';
  394. return $value;
  395. }