1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
16: class Agora_Driver {
17:
18: 19: 20: 21: 22:
23: protected $_charset;
24:
25: 26: 27: 28: 29:
30: protected $_db;
31:
32: 33: 34: 35: 36:
37: protected $_scope;
38:
39: 40: 41: 42: 43:
44: public $_forum;
45:
46: 47: 48: 49: 50:
51: public $_forum_id;
52:
53: 54: 55: 56: 57:
58: protected $_threads_table = 'agora_messages';
59:
60: 61: 62: 63: 64:
65: protected $_forums_table = 'agora_forums';
66:
67: 68: 69: 70: 71:
72: protected $_cache;
73:
74: 75: 76:
77: public function __construct($scope, $params)
78: {
79: if (empty($params['db'])) {
80: throw new InvalidArgumentException('Missing required connection parameter(s).');
81: }
82:
83:
84: $this->_scope = $scope;
85: $this->_db = $params['db'];
86: $this->_charset = $params['charset'];
87:
88:
89: $this->_cache = $GLOBALS['injector']->getInstance('Horde_Cache');
90: }
91:
92: 93: 94: 95: 96:
97: public function allowAttachments()
98: {
99: return ($GLOBALS['conf']['forums']['enable_attachments'] == '1' ||
100: ($GLOBALS['conf']['forums']['enable_attachments'] == '0' &&
101: $this->_forum['forum_attachments']));
102: }
103:
104: 105: 106: 107: 108: 109: 110: 111:
112: public function saveMessage($info)
113: {
114:
115: if ($info['message_parent_id'] &&
116: $this->isThreadLocked($info['message_parent_id'])) {
117: return PEAR::raiseError(_("This thread has been locked."));
118: }
119:
120:
121: if (!$this->hasPermission(Horde_Perms::EDIT)) {
122: return PEAR::raiseError(sprintf(_("You don't have permission to post messages in forum %s."), $this->_forum_id));
123: }
124:
125: if (empty($info['message_id'])) {
126:
127:
128: if ($info['message_parent_id'] > 0) {
129: $parents = $this->_db->selectValue('SELECT parents FROM ' . $this->_threads_table . ' WHERE message_id = ?',
130: array($info['message_parent_id']));
131: $info['parents'] = $parents . ':' . $info['message_parent_id'];
132: $info['message_thread'] = $this->getThreadRoot($info['message_parent_id']);
133: } else {
134: $info['parents'] = '';
135: $info['message_thread'] = 0;
136: }
137:
138:
139: $sql = 'INSERT INTO ' . $this->_threads_table
140: . ' (forum_id, message_thread, parents, '
141: . 'message_author, message_subject, body, attachments, '
142: . 'message_timestamp, message_modifystamp, ip) '
143: . ' VALUES (?, ?, ?, ?, ?, ?, 0, ?, ?, ?)';
144:
145: $author = $GLOBALS['registry']->getAuth() ? $GLOBALS['registry']->getAuth() : $info['posted_by'];
146: $values = array($this->_forum_id,
147: $info['message_thread'],
148: $info['parents'],
149: $author,
150: $this->convertToDriver($info['message_subject']),
151: $this->convertToDriver($info['message_body']),
152: $_SERVER['REQUEST_TIME'],
153: $_SERVER['REQUEST_TIME'],
154: $_SERVER['REMOTE_ADDR']);
155:
156: try {
157: $info['message_id'] = $this->_db->insert($sql, $values);
158: } catch (Horde_Db_Exception $e) {
159: throw new Agora_Exception($e->getMessage());
160: }
161:
162:
163: if (!$this->_forum['forum_moderated']) {
164:
165: if ($this->_forum['forum_distribution_address']) {
166: Agora::distribute($info['message_id']);
167: }
168:
169: $this->_lastInForum($this->_forum_id, $info['message_id'], $author, $_SERVER['REQUEST_TIME']);
170: $this->_forumSequence($this->_forum_id, 'message', '+');
171: if ($info['message_thread']) {
172: $this->_sequence($info['message_thread'], '+');
173: $this->_lastInThread($info['message_thread'], $info['message_id'], $author, $_SERVER['REQUEST_TIME']);
174: } else {
175: $this->_forumSequence($this->_forum_id, 'thread', '+');
176: }
177: }
178:
179: } else {
180:
181:
182: $sql = 'UPDATE ' . $this->_threads_table . ' SET ' .
183: 'message_subject = ?, body = ?, message_modifystamp = ? WHERE message_id = ?';
184: $values = array($this->convertToDriver($info['message_subject']),
185: $this->convertToDriver($info['message_body']),
186: $_SERVER['REQUEST_TIME'],
187: $info['message_id']);
188:
189: try {
190: $this->_db->execute($sql, $values);
191: } catch (Horde_Db_Exception $e) {
192: throw new Agora_Exception($e->getMessage());
193: }
194:
195:
196: $info['message_thread'] = $this->getThreadRoot($info['message_id']);
197: }
198:
199:
200: if (!empty($info['message_attachment']) ||
201: !empty($info['attachment_delete'])) {
202: $vfs = Agora::getVFS();
203: if ($vfs instanceof PEAR_Error) {
204: return $vfs;
205: }
206: $vfs_dir = Agora::VFS_PATH . $this->_forum_id . '/' . $info['message_id'];
207:
208: 209:
210: if (!empty($info['attachment_delete'])) {
211: $sql = 'SELECT file_id FROM agore_files WHERE message_id = ?';
212: foreach ($this->_db->selectValues($sql, array($info['message_id'])) as $file_id) {
213: if ($vfs->exists($vfs_dir, $file_id)) {
214: $delete = $vfs->deleteFile($vfs_dir, $file_id);
215: if ($delete instanceof PEAR_Error) {
216: return $delete;
217: }
218: }
219: }
220: try {
221: $this->_db->execute('DELETE FROM agore_files WHERE message_id = ?', array($info['message_id']));
222: } catch (Horde_Db_Exception $e) {
223: throw new Agora_Exception($e->getMessage());
224: }
225: $attachments = 0;
226: }
227:
228:
229: if (!empty($info['message_attachment'])) {
230: $file_sql = 'INSERT INTO agora_files (file_name, file_type, file_size, message_id) VALUES (?, ?, ?, ?)';
231: $file_data = array($info['message_attachment']['name'],
232: $info['message_attachment']['type'],
233: $info['message_attachment']['size'],
234: $info['message_id']);
235:
236: try {
237: $file_id = $this->_db->insert($file_sql, $file_data);
238: } catch (Horde_Db_Exception $e) {
239: throw new Agora_Exception($e->getMessage());
240: }
241:
242: $result = $vfs->write($vfs_dir, $file_id, $info['message_attachment']['file'], true);
243: if ($result instanceof PEAR_Error) {
244: return $result;
245: }
246: $attachments = 1;
247: }
248:
249: $sql = 'UPDATE ' . $this->_threads_table . ' SET attachments = ? WHERE message_id = ?';
250: try {
251: $this->_db->execute($sql, array($attachments, $info['message_id']));
252: } catch (Horde_Db_Exception $e) {
253: throw new Agora_Exception($e->getMessage());
254: }
255: }
256:
257:
258: $this->_updateCacheState($info['message_thread']);
259:
260: return $info['message_id'];
261: }
262:
263: 264: 265: 266: 267: 268: 269: 270: 271: 272:
273: public function moveThread($thread_id, $forum_id)
274: {
275: $sql = 'SELECT forum_id FROM ' . $this->_threads_table . ' WHERE message_id = ?';
276: try {
277: $old_forum = $this->_db->selectValue($sql, array($thread_id));
278: } catch (Horde_Db_Exception $e) {
279: throw new Agora_Exception($e->getMessage());
280: }
281:
282: $sql = 'UPDATE ' . $this->_threads_table . ' SET forum_id = ? WHERE message_thread = ? OR message_id = ?';
283: try {
284: $this->_db->execute($sql, array($forum_id, $thread_id, $thread_id));
285: } catch (Horde_Db_Exception $e) {
286: throw new Agora_Exception($e->getMessage());
287: }
288:
289: $this->_forumSequence($old_forum, 'thread', '-');
290: $this->_forumSequence($forum_id, 'thread', '+');
291:
292:
293: $this->_lastInForum($old_forum);
294: $this->_lastInForum($forum_id);
295:
296:
297: $this->_updateCacheState($thread_id);
298:
299: return true;
300: }
301:
302: 303: 304: 305: 306: 307: 308:
309: public function splitThread($message_id)
310: {
311: $sql = 'SELECT message_thread FROM ' . $this->_threads_table . ' WHERE message_id = ?';
312: try {
313: $thread_id = $this->_db->selectValue($sql, array($message_id));
314: } catch (Horde_Db_Exception $e) {
315: throw new Agora_Exception($e->getMessage());
316: }
317:
318: $sql = 'UPDATE ' . $this->_threads_table . ' SET message_thread = ?, parents = ? WHERE message_id = ?';
319: try {
320: $this->_db->execute($sql, array(0, '', $message_id));
321: } catch (Horde_Db_Exception $e) {
322: throw new Agora_Exception($e->getMessage());
323: }
324:
325: $sql = 'SELECT message_thread, parents, message_id FROM ' . $this->_threads_table . ' WHERE parents LIKE ?';
326: try {
327: $children = $this->_db->selectAll($sql, array(":$thread_id:%$message_id%"));
328: } catch (Horde_Db_Exception $e) {
329: throw new Agora_Exception($e->getMessage());
330: }
331:
332: if (!empty($children)) {
333: $pos = strpos($children[0]['parents'], ':' . $message_id);
334: foreach ($children as $i => $message) {
335: $children[$i]['message_thread'] = (int)$message_id;
336: $children[$i]['parents'] = substr($message['parents'], $pos);
337: }
338:
339: $sql = 'UPDATE ' . $this->_threads_table . ' SET message_thread = ?, parents = ? WHERE message_id = ?';
340: try {
341: $this->_db->execute($sql, $children);
342: } catch (Horde_Db_Exception $e) {
343: throw new Agora_Exception($e->getMessage());
344: }
345: }
346:
347:
348: $count = $this->countThreads($thread_id);
349: $sql = 'UPDATE ' . $this->_threads_table . ' SET message_seq = ? WHERE message_id = ?';
350: try {
351: $this->_db->execute($sql, array($count, $thread_id));
352: } catch (Horde_Db_Exception $e) {
353: throw new Agora_Exception($e->getMessage());
354: }
355:
356:
357: $count = $this->countThreads($message_id);
358: $sql = 'UPDATE ' . $this->_threads_table . ' SET message_seq = ? WHERE message_id = ?';
359: try {
360: $this->_db->execute($sql, array($count, $message_id));
361: } catch (Horde_Db_Exception $e) {
362: throw new Agora_Exception($e->getMessage());
363: }
364:
365:
366: $this->_lastInForum($this->_forum_id);
367: $this->_lastInThread($thread_id);
368: $this->_lastInThread($message_id);
369:
370: $this->_forumSequence($this->_forum_id, 'thread', '+');
371:
372:
373: $this->_updateCacheState($thread_id);
374: }
375:
376: 377: 378: 379: 380: 381: 382: 383:
384: public function mergeThread($thread_from, $message_id)
385: {
386: $sql = 'SELECT message_thread, parents FROM ' . $this->_threads_table . ' WHERE message_id = ?';
387: try {
388: $destination = $this->_db->selectOne($sql, array($message_id));
389: } catch (Horde_Db_Exception $e) {
390: throw new Agora_Exception($e->getMessage());
391: }
392:
393:
394: if ($destination['message_thread'] == 0) {
395: $destination['message_thread'] = $message_id;
396: }
397:
398: $sql = 'SELECT message_thread, parents, message_id FROM ' . $this->_threads_table . ' WHERE message_id = ? OR message_thread = ?';
399: try {
400: $children = $this->_db->selectAll($sql, array($thread_from, $thread_from));
401: } catch (Horde_Db_Exception $e) {
402: throw new Agora_Exception($e->getMessage());
403: }
404:
405: 406:
407: if (!empty($children)) {
408: $sql = 'UPDATE ' . $this->_threads_table . ' SET message_thread = ?, parents = ? WHERE message_id = ?';
409:
410: foreach ($children as $i => $message) {
411: $children[$i]['message_thread'] = $destination['message_thread'];
412: if (!empty($destination['parents'])) {
413: $children[$i]['parents'] = $destination['parents'] . $message['parents'];
414: } else {
415: $children[$i]['parents'] = ':' . $message_id;
416: }
417:
418: try {
419: $this->_db->execute($sql, $children[$i]);
420: } catch (Horde_Db_Exception $e) {
421: throw new Agora_Exception($e->getMessage());
422: }
423: }
424: }
425:
426: $count = $this->countThreads($destination['message_thread']);
427: $sql = 'UPDATE ' . $this->_threads_table . ' SET message_seq = ? WHERE message_id = ?';
428: try {
429: $this->_db->execute($sql, array($count, $destination['message_thread']));
430: } catch (Horde_Db_Exception $e) {
431: throw new Agora_Exception($e->getMessage());
432: }
433:
434:
435: $this->_lastInForum($this->_forum_id);
436: $this->_lastInThread($destination['message_thread']);
437:
438: $this->_forumSequence($this->_forum_id, 'thread', '-');
439:
440:
441: $this->_updateCacheState($destination['message_thread']);
442: }
443:
444: 445: 446: 447: 448: 449: 450: 451:
452: public function getMessage($message_id)
453: {
454: $message = $this->_cache->get('agora_msg' . $message_id, $GLOBALS['conf']['cache']['default_lifetime']);
455: if ($message) {
456: return unserialize($message);
457: }
458:
459: $sql = 'SELECT message_id, forum_id, message_thread, parents, '
460: . 'message_author, message_subject, body, message_seq, '
461: . 'message_timestamp, view_count, locked, attachments FROM '
462: . $this->_threads_table . ' WHERE message_id = ?';
463: try {
464: $message = $this->_db->selectOne($sql, array($message_id));
465: } catch (Horde_Db_Exception $e) {
466: throw new Agora_Exception($e->getMessage());
467: }
468:
469: if (empty($message)) {
470: throw new Horde_Exception_NotFound(sprintf(_("Message ID \"%d\" not found"), $message_id));
471: }
472:
473: $message['message_subject'] = $this->convertFromDriver($message['message_subject']);
474: $message['body'] = $this->convertFromDriver($message['body']);
475: if ($message['message_thread'] == 0) {
476: $message['message_thread'] = $message_id;
477: }
478:
479:
480: if (isset($this->_forum['moderators']) &&
481: in_array($message['message_author'], $this->_forum['moderators'])) {
482: $message['message_author_moderator'] = 1;
483: }
484:
485: $this->_cache->set('agora_msg' . $message_id, serialize($message));
486:
487: return $message;
488: }
489:
490: 491: 492: 493: 494: 495: 496: 497: 498:
499: public function replyMessage($message)
500: {
501: if (!is_array($message)) {
502: $message = $this->getMessage($message);
503: }
504:
505:
506: if (Horde_String::lower(Horde_String::substr($message['message_subject'], 0, 3)) != 're:') {
507: $message['message_subject'] = 'Re: ' . $message['message_subject'];
508: } else {
509: $message['message_subject'] = $message['message_subject'];
510: }
511:
512:
513: $message['body'] = sprintf(_("Posted by %s on %s"),
514: htmlspecialchars($message['message_author']),
515: strftime($GLOBALS['prefs']->getValue('date_format'), $message['message_timestamp']))
516: . "\n-------------------------------------------------------\n"
517: . $message['body'];
518: $message['body'] = "\n> " . Horde_String::wrap($message['body'], 60, "\n> ");
519:
520: return $message;
521: }
522:
523: 524: 525: 526: 527: 528: 529: 530: 531: 532:
533: public function deleteMessage($message_id)
534: {
535:
536: if (!$this->hasPermission(Horde_Perms::DELETE)) {
537: return PEAR::raiseError(sprintf(_("You don't have permission to delete messages in forum %s."), $this->_forum_id));
538: }
539:
540: $sql = 'SELECT message_thread FROM ' . $this->_threads_table . ' WHERE message_id = ?';
541: try {
542: $thread_id = $this->_db->selectValue($sql, array($message_id));
543: } catch (Horde_Db_Exception $e) {
544: throw new Agora_Exception($e->getMessage());
545: }
546:
547: $sql = 'DELETE FROM ' . $this->_threads_table . ' WHERE message_id = ' . (int)$message_id;
548: if ($thread_id == 0) {
549: $sql .= ' OR message_thread = ' . (int)$message_id;
550: }
551:
552: try {
553: $this->_db->execute($sql);
554: } catch (Horde_Db_Exception $e) {
555: throw new Agora_Exception($e->getMessage());
556: }
557:
558:
559:
560: $this->_forumSequence($this->_forum_id, 'message', '-');
561: if ($thread_id) {
562: $this->_sequence($thread_id, '-');
563: } else {
564: $this->_forumSequence($this->_forum_id, 'thread', '-');
565: }
566:
567: $this->_lastInForum($this->_forum_id);
568: $this->_lastInThread($thread_id);
569:
570:
571: $this->_updateCacheState($thread_id);
572:
573: return $thread_id;
574: }
575:
576: 577: 578: 579: 580: 581: 582: 583: 584: 585:
586: private function _lastInForum($forum_id, $message_id = 0, $message_author = '', $message_timestamp = 0)
587: {
588:
589: if ($message_id == 0) {
590: $sql = $this->_db->addLimitOffset('SELECT message_id, message_author, message_timestamp FROM ' . $this->_threads_table
591: . ' WHERE forum_id = ' . (int)$forum_id . ' ORDER BY message_id DESC', array('limit' => 1));
592: try {
593: $last = $this->_db->selectOne($sql);
594: } catch (Horde_Db_Execution $e) {
595: throw new Agora_Exception($e->getMessage());
596: }
597: if (!empty($last)) {
598: extract($last);
599: }
600: }
601:
602: $sql = 'UPDATE ' . $this->_forums_table
603: . ' SET last_message_id = ?, last_message_author = ?, last_message_timestamp = ? WHERE forum_id = ?';
604: $values = array($message_id, $message_author, $message_timestamp, $forum_id);
605:
606: try {
607: $this->_db->execute($sql, $values);
608: } catch (Horde_Db_Execution $e) {
609: throw new Agora_Exception($e->getMessage());
610: }
611:
612: $this->_cache->expire('agora_forum_' . $forum_id, $GLOBALS['conf']['cache']['default_lifetime']);
613: }
614:
615: 616: 617: 618: 619: 620: 621: 622: 623: 624:
625: private function _lastInThread($thread_id, $message_id = 0, $message_author = '', $message_timestamp = 0)
626: {
627:
628: if ($message_id == 0) {
629: $sql = $this->_db->addLimitOffset('SELECT message_id, message_author, message_timestamp FROM ' . $this->_threads_table
630: . ' WHERE message_thread = ' . (int)$thread_id . ' ORDER BY message_id DESC', array('limit' => 1));
631: try {
632: $last = $this->_db->selectOne($sql);
633: } catch (Horde_Db_Execution $e) {
634: throw new Agora_Exception($e->getMessage());
635: }
636: if (!empty($last)) {
637: extract($last);
638: }
639: }
640:
641: $sql = 'UPDATE ' . $this->_threads_table
642: . ' SET last_message_id = ?, last_message_author = ?, message_modifystamp = ? WHERE message_id = ?';
643: $values = array($message_id, $message_author, $message_timestamp, $thread_id);
644:
645: try {
646: $this->_db->execute($sql, $values);
647: } catch (Horde_Db_Execution $e) {
648: throw new Agora_Exception($e->getMessage());
649: }
650: }
651:
652: 653: 654: 655: 656: 657: 658: 659: 660:
661: public function _forumSequence($forum_id, $type = 'message', $diff = '+')
662: {
663: $t = $type . '_count';
664: $sql = 'UPDATE ' . $this->_forums_table . ' SET ' . $t . ' = ';
665:
666: switch ($diff) {
667: case '+':
668: case '-':
669: $sql .= $t . ' ' . $diff . ' 1';
670: break;
671:
672: default:
673: $sql .= (int)$diff;
674: break;
675: }
676:
677: $sql .= ' WHERE forum_id = ' . (int)$forum_id;
678:
679:
680: return $this->_db->execute($sql);
681: }
682:
683: 684: 685: 686: 687: 688: 689: 690:
691: private function _sequence($thread_id, $diff = '+')
692: {
693: $sql = 'UPDATE ' . $this->_threads_table . ' SET message_seq = ';
694:
695: switch ($diff) {
696: case '+':
697: case '-':
698: $sql .= 'message_seq ' . $diff . ' 1';
699: break;
700:
701: default:
702: $sql .= (int)$diff;
703: break;
704: }
705:
706: $sql .= ', message_modifystamp = ' . $_SERVER['REQUEST_TIME'] . ' WHERE message_id = ' . (int)$thread_id;
707:
708: return $this->_db->execute($sql);
709: }
710:
711: 712: 713: 714: 715: 716: 717: 718: 719:
720: public function deleteThread($thread_id = 0)
721: {
722:
723: if (!$this->hasPermission(Horde_Perms::DELETE)) {
724: return PEAR::raiseError(sprintf(_("You don't have permission to delete messages in forum %s."), $this->_forum_id));
725: }
726:
727: if ($thread_id > 0) {
728: $sql = 'DELETE FROM ' . $this->_threads_table . ' WHERE message_thread = ' . (int)$thread_id;
729: try {
730: $this->_db->execute($sql);
731: } catch (Horde_Db_Exception $e) {
732: throw new Agora_Exception($e->getMessage());
733: }
734:
735: $sql = 'SELECT COUNT(*) FROM ' . $this->_threads_table . ' WHERE forum_id = ' . (int)$this->_forum_id;
736: $messages = $this->_db->selectValue($sql);
737:
738: $this->_forumSequence($this->_forum_id, 'thread', '-');
739: $this->_forumSequence($this->_forum_id, 'message', $messages);
740:
741:
742: $this->_updateCacheState($thread_id);
743:
744: } else {
745: $sql = 'DELETE FROM ' . $this->_threads_table . ' WHERE forum_id = ' . (int)$this->_forum_id;
746: try {
747: $this->_db->execute($sql);
748: } catch (Horde_Db_Exception $e) {
749: throw new Agora_Exception($e->getMessage());
750: }
751:
752: $this->_forumSequence($this->_forum_id, 'thread', '0');
753: $this->_forumSequence($this->_forum_id, 'message', '0');
754: }
755:
756:
757: $this->_lastInForum($this->_forum_id);
758:
759: return true;
760: }
761:
762: 763: 764: 765: 766: 767: 768: 769: 770: 771: 772: 773: 774: 775: 776: 777: 778: 779: 780: 781: 782: 783: 784: 785:
786: public function getThreads($thread_root = 0,
787: $all_levels = false,
788: $sort_by = 'message_timestamp',
789: $sort_dir = 0,
790: $message_view = false,
791: $link_back = '',
792: $base_url = null,
793: $from = null,
794: $count = null,
795: $nofollow = false)
796: {
797:
798: if (!$this->hasPermission(Horde_Perms::SHOW)) {
799: return PEAR::raiseError(sprintf(_("You don't have permission to read messages in forum %s."), $this->_forum_id));
800: }
801:
802:
803: $messages = $this->_getThreads($thread_root, $all_levels, $sort_by, $sort_dir, $message_view, $from, $count);
804: if ($messages instanceof PEAR_Error || empty($messages)) {
805: return $messages;
806: }
807:
808:
809: if (isset($this->_forum['moderators'])) {
810: $moderators = array_flip($this->_forum['moderators']);
811: }
812:
813:
814: $view_url = Horde::url('messages/index.php');
815: if ($base_url) {
816: $edit_url = $base_url;
817: $del_url = Horde_Util::addParameter($base_url, 'delete', 'true');
818: } else {
819: $edit_url = Horde::url('messages/edit.php');
820: $del_url = Horde::url('messages/delete.php');
821: }
822:
823:
824: $per_page = $GLOBALS['prefs']->getValue('thread_per_page');
825: $view_bodies = $GLOBALS['prefs']->getValue('thread_view_bodies');
826: $abuse_url = Horde::url('messages/abuse.php');
827: $hot_img = Horde::img('hot.png', _("Hot thread"), array('title' => _("Hot thread")));
828: $new_img = Horde::img('required.png', _("New posts"), array('title' => _("New posts")));
829: $is_moderator = $this->hasPermission(Horde_Perms::DELETE);
830:
831:
832: foreach ($messages as &$message) {
833:
834:
835: if ($message['attachments']) {
836: $message['message_attachment'] = $this->getAttachmentLink($message['message_id']);
837: }
838:
839:
840: if ($thread_root == 0 && $message['last_message_id'] > 0) {
841: $url = Agora::setAgoraId($message['forum_id'], $message['last_message_id'], $view_url, $this->_scope);
842: $message['message_url'] = Horde::link($url);
843: $last_timestamp = $message['last_message_timestamp'];
844: } else {
845: $last_timestamp = $message['message_timestamp'];
846: }
847:
848:
849: if ($this->isHot($message['view_count'], $last_timestamp)) {
850: $message['hot'] = $hot_img;
851: }
852:
853:
854: if ($thread_root == 0 && $this->isNew($message['message_id'], $last_timestamp)) {
855: $message['new'] = $new_img;
856: }
857:
858:
859: if (isset($this->_forum['moderators']) && array_key_exists($message['message_author'], $moderators)) {
860: $message['message_author_moderator'] = 1;
861: }
862:
863:
864: $url = Agora::setAgoraId($message['forum_id'], $message['message_id'], $view_url, $this->_scope);
865: $message['link'] = Horde::link($url, $message['message_subject'], '', '', '', $message['message_subject']);
866:
867:
868: if ($sort_by != 'message_thread') {
869: unset($message['indent'], $message['parent']);
870:
871:
872: if ($thread_root == 0 && $message['message_seq'] > $per_page && $view_bodies == 2) {
873: $sub_pages = $message['message_seq'] / $per_page;
874: for ($i = 0; $i < $sub_pages; $i++) {
875: $page_title = sprintf(_("Page %d"), $i+1);
876: $message['pages'][] = Horde::link(Horde_Util::addParameter($url, 'thread_page', $i), $page_title, '', '', '', $page_title) . ($i+1) . '</a>';
877: }
878: }
879: }
880:
881:
882: if (!$message['locked']) {
883: if ($base_url) {
884: $url = $base_url;
885: if (strpos($url, '%p') !== false) {
886: $url = str_replace('%p', $message['message_id'], $url);
887: } else {
888: $url = Horde_Util::addParameter($url, 'message_parent_id', $message['message_id']);
889: }
890: if (!empty($link_back)) {
891: $url = Horde_Util::addParameter($url, 'url', $link_back);
892: }
893: } else {
894: $url = Agora::setAgoraId($message['forum_id'], $message['message_id'], $view_url, $this->_scope);
895: }
896: $url = Horde_Util::addParameter($url, 'reply_focus', 1) . '#messageform';
897: $message['reply'] = Horde::link($url, _("Reply to message"), '', '', '', _("Reply to message")) . _("Reply") . '</a>';
898: }
899:
900:
901: if ($thread_root > 0 && isset($this->_forum['moderators'])) {
902: $url = Agora::setAgoraId($message['forum_id'], $message['message_id'], $abuse_url);
903: $message['actions'][] = Horde::link($url, _("Report as abuse")) . _("Report as abuse") . '</a>';
904: }
905:
906: if ($is_moderator) {
907:
908: $url = Agora::setAgoraId($message['forum_id'], $message['message_id'], $edit_url, $this->_scope);
909: $message['actions'][] = Horde::link($url, _("Edit"), '', '', '', _("Edit message")) . _("Edit") . '</a>';
910:
911:
912: $url = Agora::setAgoraId($message['forum_id'], $message['message_id'], $del_url, $this->_scope);
913: $message['actions'][] = Horde::link($url, _("Delete"), '', '', '', _("Delete message")) . _("Delete") . '</a>';
914:
915:
916: $url = Agora::setAgoraId($this->_forum_id, $message['message_id'], Horde::url('messages/lock.php'), $this->_scope);
917: $label = ($message['locked']) ? _("Unlock") : _("Lock");
918: $message['actions'][] = Horde::link($url, $label, '', '', '', $label) . $label . '</a>';
919:
920:
921: if ($this->_scope == 'agora') {
922: if ($message['message_thread'] == $message['message_id']) {
923: $url = Agora::setAgoraId($this->_forum_id, $message['message_id'], Horde::url('messages/move.php'), $this->_scope);
924: $message['actions'][] = Horde::link($url, _("Move"), '', '', '', _("Move")) . _("Move") . '</a>';
925:
926:
927: $url = Agora::setAgoraId($this->_forum_id, $message['message_id'], Horde::url('messages/merge.php'), $this->_scope);
928: $message['actions'][] = Horde::link($url, _("Merge"), '', '', '', _("Merge")) . _("Merge") . '</a>';
929: } elseif ($message['message_thread'] != 0) {
930:
931:
932: $url = Agora::setAgoraId($this->_forum_id, $message['message_id'], Horde::url('messages/split.php'), $this->_scope);
933: $message['actions'][] = Horde::link($url, _("Split"), '', '', '', _("Split")) . _("Split") . '</a>';
934: }
935: }
936: }
937: }
938:
939: return $messages;
940: }
941:
942: 943: 944: 945: 946: 947: 948: 949:
950: protected function _formatThreads($messages, $sort_by = 'message_modifystamp',
951: $format = false, $thread_root = 0)
952: {
953:
954: foreach ($messages as &$message) {
955: $message['message_author'] = htmlspecialchars($message['message_author']);
956: $message['message_subject'] = htmlspecialchars($this->convertFromDriver($message['message_subject']));
957: $message['message_date'] = $this->dateFormat($message['message_timestamp']);
958: if ($format) {
959: $message['body'] = $this->formatBody($this->convertFromDriver($message['body']));
960: }
961:
962:
963: if ($message['message_thread'] == 0) {
964: $message['message_thread'] = $message['message_id'];
965: }
966:
967:
968: if ($thread_root == 0 && $message['last_message_id'] > 0) {
969: $message['last_message_date'] = $this->dateFormat($message['last_message_timestamp']);
970: }
971:
972:
973: if ($sort_by == 'message_thread') {
974: $indent = explode(':', $message['parents']);
975: $message['indent'] = count($indent) - 1;
976: $last = array_pop($indent);
977:
978: 979: 980: 981: 982: 983: 984: 985: 986: 987: 988: 989:
990: $message['parent'] = $last ? $last : null;
991: }
992: }
993:
994: return $messages;
995: }
996:
997: 998: 999: 1000: 1001:
1002: public function formatBody($body)
1003: {
1004: static $filters, $filters_params;
1005:
1006: if ($filters == null) {
1007: $filters = array('text2html', 'bbcode', 'highlightquotes', 'emoticons');
1008: $filters_params = array(array('parselevel' => Horde_Text_Filter_Text2html::MICRO),
1009: array(),
1010: array('citeblock' => true),
1011: array('entities' => true));
1012:
1013:
1014: $config_dir = $GLOBALS['registry']->get('fileroot', 'agora') . '/config/';
1015: $config_file = 'words.php';
1016: if (file_exists($config_dir . $config_file)) {
1017: if (!empty($GLOBALS['conf']['vhosts'])) {
1018: $v_file = substr($config_file, 0, -4) . '-' . $GLOBALS['conf']['server']['name'] . '.php';
1019: if (file_exists($config_dir . $config_file)) {
1020: $config_file = $v_file;
1021: }
1022: }
1023:
1024: $filters[] = 'words';
1025: $filters_params[] = array('words_file' => $config_dir . $config_file,
1026: 'replacement' => false);
1027: }
1028: }
1029:
1030: if (($hasBBcode = strpos($body, '[')) !== false &&
1031: strpos($body, '[/', $hasBBcode) !== false) {
1032: $filters_params[0]['parselevel'] = Horde_Text_Filter_Text2html::NOHTML;
1033: }
1034:
1035: return $GLOBALS['injector']->getInstance('Horde_Core_Factory_TextFilter')->filter($body, $filters, $filters_params);
1036: }
1037:
1038: 1039: 1040:
1041: public function isHot($views, $last_post)
1042: {
1043: if (!$GLOBALS['conf']['threads']['track_views']) {
1044: return false;
1045: }
1046:
1047: return ($views > $GLOBALS['prefs']->getValue('threads_hot')) && $last_post > ($_SERVER['REQUEST_TIME'] - 86400);
1048: }
1049:
1050: 1051: 1052:
1053: public function isNew($thread_id, $last_post)
1054: {
1055: if (!isset($_COOKIE['agora_viewed_threads']) ||
1056: ($pos1 = strpos($_COOKIE['agora_viewed_threads'], ':' . $thread_id . '|')) === false ||
1057: ($pos2 = strpos($_COOKIE['agora_viewed_threads'], '|', $pos1)) === false ||
1058: substr($_COOKIE['agora_viewed_threads'], $pos2+1, 10) > $last_post
1059: ) {
1060: return false;
1061: }
1062:
1063: return true;
1064: }
1065:
1066: 1067: 1068: 1069: 1070: 1071: 1072: 1073: 1074: 1075: 1076: 1077:
1078: public function getModerateList($sort_by, $sort_dir)
1079: {
1080: $sql = 'SELECT forum_id, forum_name FROM ' . $this->_forums_table . ' WHERE forum_moderated = ?';
1081: $values = array(1);
1082:
1083:
1084: if ($GLOBALS['registry']->isAdmin(array('permission' => 'agora:admin')) ||
1085: ($GLOBALS['injector']->getInstance('Horde_Perms')->exists('agora:forums:' . $this->_scope) &&
1086: $GLOBALS['injector']->getInstance('Horde_Perms')->hasPermission('agora:forums:' . $this->_scope, $GLOBALS['registry']->getAuth(), Horde_Perms::DELETE))) {
1087: $sql .= ' AND scope = ? ';
1088: $values[] = $this->_scope;
1089: } else {
1090:
1091: $sql .= ' AND scope = ? AND author = ?';
1092: $values[] = $this->_scope;
1093: $values[] = $GLOBALS['registry']->getAuth();
1094: }
1095:
1096:
1097: try {
1098: $forums_list = $this->_db->selectAssoc($sql, $values);
1099: } catch (Horde_Db_Exception $e) {
1100: throw new Agora_Exception($e->getMessage());
1101: }
1102: if (empty($forums_list)) {
1103: return $forums_list;
1104: }
1105:
1106:
1107: $sql = 'SELECT message_id, forum_id, message_subject, message_author, '
1108: . 'body, message_timestamp, attachments FROM ' . $this->_threads_table . ' WHERE forum_id IN ('
1109: . implode(',', array_keys($forums_list)) . ')'
1110: . ' AND approved = ? ORDER BY ' . $sort_by . ' '
1111: . ($sort_dir ? 'DESC' : 'ASC');
1112:
1113: try {
1114: $messages = $this->_db->selectAll($sql, array(0));
1115: } catch (Horde_Db_Exception $e) {
1116: throw new Agora_Exception($e->getMessage());
1117: }
1118:
1119:
1120: $approve_url = Horde_Util::addParameter(Horde::url('moderate.php'), 'approve', true);
1121: $del_url = Horde::url('messages/delete.php');
1122: foreach ($messages as &$message) {
1123: $message['forum_name'] = $this->convertFromDriver($forums_list[$message['forum_id']]);
1124: $message['message_author'] = htmlspecialchars($message['message_author']);
1125: $message['message_subject'] = htmlspecialchars($this->convertFromDriver($message['message_subject']));
1126: $message['message_body'] = $GLOBALS['injector']
1127: ->getInstance('Horde_Core_Factory_TextFilter')
1128: ->filter($this->convertFromDriver($message['body']), 'highlightquotes');
1129: if ($message['attachments']) {
1130: $message['message_attachment'] = $this->getAttachmentLink($message['message_id']);
1131: }
1132: $message['message_date'] = $this->dateFormat($message['message_timestamp']);
1133: }
1134:
1135: return $messages;
1136: }
1137:
1138: 1139: 1140:
1141: public function getBanned()
1142: {
1143: $perm_name = 'agora:forums:' . $this->_scope . ':' . $this->_forum_id;
1144: if (!$GLOBALS['injector']->getInstance('Horde_Perms')->exists($perm_name)) {
1145: return array();
1146: }
1147:
1148: $forum_perm = $GLOBALS['injector']->getInstance('Horde_Perms')->getPermission($perm_name);
1149: if (!($forum_perm instanceof Horde_Perms_Permission)) {
1150: return $forum_perm;
1151: }
1152:
1153: $permissions = $forum_perm->getUserPermissions();
1154: if (empty($permissions)) {
1155: return $permissions;
1156: }
1157:
1158:
1159: $filter = Horde_Perms::EDIT | Horde_Perms::DELETE;
1160: foreach ($permissions as $user => $level) {
1161: if ($level & $filter) {
1162: unset($permissions[$user]);
1163: }
1164: }
1165:
1166: return $permissions;
1167: }
1168:
1169: 1170: 1171: 1172: 1173: 1174: 1175:
1176: public function updateBan($user, $forum_id = null, $action = 'add')
1177: {
1178: if ($forum_id == null) {
1179: $forum_id = $this->_forum_id;
1180: }
1181:
1182: $perms = $GLOBALS['injector']->getInstance('Horde_Perms');
1183: $perm_name = 'agora:forums:' . $this->_scope . ':' . $forum_id;
1184: if (!$perms->exists($perm_name)) {
1185: $forum_perm = $GLOBALS['injector']
1186: ->getInstance('Horde_Core_Perms')
1187: ->newPermission($perm_name);
1188: $perms->addPermission($forum_perm);
1189: } else {
1190: $forum_perm = $perms->getPermission($perm_name);
1191: if ($forum_perm instanceof PEAR_Error) {
1192: return $forum_perm;
1193: }
1194: }
1195:
1196: if ($action == 'add') {
1197:
1198: $forum_perm->removeUserPermission($user, Horde_Perms::ALL, true);
1199: $forum_perm->addUserPermission($user, Horde_Perms::READ, true);
1200: } else {
1201:
1202: $forum_perm->removeUserPermission($user, Horde_Perms::ALL, true);
1203: }
1204:
1205: return true;
1206: }
1207:
1208: 1209: 1210: 1211: 1212: 1213: 1214: 1215: 1216:
1217: public function updateModerator($moderator, $forum_id = null, $action = 'add')
1218: {
1219: if ($forum_id == null) {
1220: $forum_id = $this->_forum_id;
1221: }
1222:
1223: switch ($action) {
1224: case 'add':
1225: $sql = 'INSERT INTO agora_moderators (forum_id, horde_uid) VALUES (?, ?)';
1226: break;
1227:
1228: case 'delete':
1229: $sql = 'DELETE FROM agora_moderators WHERE forum_id = ? AND horde_uid = ?';
1230: break;
1231: }
1232:
1233: try {
1234: $this->_db->execute($sql, array($forum_id, $moderator));
1235: } catch (Horde_Db_Exception $e) {
1236: throw new Agora_Exception($e->getMessage());
1237: }
1238:
1239:
1240: $perm_name = 'agora:forums:' . $this->_scope . ':' . $forum_id;
1241: $perms = $GLOBALS['injector']->getInstance('Horde_Perms');
1242: if (!$perms->exists($perm_name)) {
1243: $forum_perm = $GLOBALS['injector']
1244: ->getInstance('Horde_Core_Perms')
1245: ->newPermission($perm_name);
1246: $perms->addPermission($forum_perm);
1247: } else {
1248: $forum_perm = $perms->getPermission($perm_name);
1249: if ($forum_perm instanceof PEAR_Error) {
1250: return $forum_perm;
1251: }
1252: }
1253:
1254: switch ($action) {
1255: case 'add':
1256: $forum_perm->addUserPermission($moderator, Horde_Perms::DELETE, true);
1257: break;
1258:
1259: case 'delete':
1260: $forum_perm->removeUserPermission($moderator, Horde_Perms::DELETE, true);
1261: break;
1262: }
1263:
1264: $this->_cache->expire('agora_forum_' . $forum_id, $GLOBALS['conf']['cache']['default_lifetime']);
1265: }
1266:
1267: 1268: 1269: 1270: 1271: 1272: 1273: 1274:
1275: public function moderate($action, $ids)
1276: {
1277: switch ($action) {
1278: case 'approve':
1279:
1280:
1281: $sql = 'SELECT message_thread FROM ' . $this->_threads_table
1282: . ' WHERE message_id IN (' . implode(',', $ids) . ')';
1283: try {
1284: $threads = $this->_db->selectValues($sql);
1285: } catch (Horde_Db_Exception $e) {
1286: throw new Agora_Exception($e->getMessage());
1287: }
1288: $this->_updateCacheState($threads);
1289:
1290: $sql = 'UPDATE ' . $this->_threads_table . ' SET approved = 1'
1291: . ' WHERE message_id IN (' . implode(',', $ids) . ')';
1292: try {
1293: $this->_db->execute($sql);
1294: } catch (Horde_Db_Exception $e) {
1295: throw new Agora_Exception($e->getMessage());
1296: }
1297:
1298:
1299: $orig_forum_id = $this->_forum_id;
1300: foreach ($ids as $message_id) {
1301:
1302: $message = $this->getMessage($message_id);
1303: $this->_forum_id = $message['forum_id'];
1304:
1305:
1306: $this->_lastInForum($this->_forum_id);
1307: $this->_forumSequence($this->_forum_id, 'message', '+');
1308: if (!empty($message['parents'])) {
1309: $this->_sequence($message['message_thread'], '+');
1310: $this->_lastInThread($message['message_thread'], $message_id, $message['message_author'], $_SERVER['REQUEST_TIME']);
1311: } else {
1312: $this->_forumSequence($this->_forum_id, 'thread', '+');
1313: }
1314:
1315:
1316: Agora::distribute($message_id);
1317: }
1318:
1319:
1320: $this->_forum_id = $orig_forum_id;
1321: break;
1322:
1323: case 'delete':
1324: foreach ($ids as $id) {
1325: $this->deleteMessage($id);
1326: }
1327: break;
1328: }
1329: }
1330:
1331: 1332: 1333: 1334: 1335: 1336: 1337: 1338:
1339: public function countThreads($thread_root = 0)
1340: {
1341: $sql = 'SELECT COUNT(*) FROM ' . $this->_threads_table . ' WHERE message_thread = ?';
1342: if ($thread_root) {
1343: return $this->_db->selectValue($sql, array($thread_root));
1344: } else {
1345: return $this->_db->selectValue($sql . ' AND forum_id = ?', array(0, $this->_forum_id));
1346: }
1347: }
1348:
1349: 1350: 1351: 1352: 1353: 1354:
1355: public function countMessages()
1356: {
1357: $sql = 'SELECT COUNT(*) FROM ' . $this->_threads_table . ' WHERE forum_id = ?';
1358: return $this->_db->selectValue($sql, array($this->_forum_id));
1359: }
1360:
1361: 1362: 1363: 1364: 1365: 1366: 1367: 1368: 1369: 1370: 1371: 1372:
1373: public function getThreadsUi($threads, $col_headers, $bodies = false,
1374: $template_file = false)
1375: {
1376: if (!count($threads)) {
1377: return '';
1378: }
1379:
1380:
1381: if (!$template_file && isset($threads[0]['indent'])) {
1382: $tree = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Tree')->create('threads', 'Html', array(
1383: 'multiline' => $bodies,
1384: 'lines' => !$bodies
1385: ));
1386:
1387: $tree->setHeader(array(
1388: array(
1389: 'class' => $col_headers['message_thread_class_plain'],
1390: 'html' => '<strong>' . $col_headers['message_thread'] . '</strong>'
1391: ),
1392: array(
1393: 'class' => $col_headers['message_author_class_plain'],
1394: 'html' => '<strong>' . $col_headers['message_author'] . '</strong>'
1395: ),
1396: array(
1397: 'class' => $col_headers['message_timestamp_class_plain'],
1398: 'html' => '<strong>' . $col_headers['message_timestamp'] . '</strong>'
1399: )
1400: ));
1401:
1402: foreach ($threads as &$thread) {
1403: if ($bodies) {
1404: $text = '<strong>' . $thread['message_subject'] . '</strong><small>[';
1405: if (isset($thread['reply'])) {
1406: $text .= ' ' . $thread['reply'];
1407: }
1408: if (!empty($thread['actions'])) {
1409: $text .= ', ' . implode(', ', $thread['actions']);
1410: }
1411: $text .= ']</small><br />' .
1412: str_replace(array("\r", "\n"), '', $thread['body'] . ((isset($thread['message_attachment'])) ? $thread['message_attachment'] : ''));
1413: } else {
1414: $text = '<strong>' . $thread['link'] . $thread['message_subject'] . '</a></strong> ';
1415: if (isset($thread['actions'])) {
1416: $text .= '<small>[' . implode(', ', $thread['actions']) . ']</small>';
1417: }
1418: }
1419:
1420: $tree->addNode(
1421: $thread['message_id'],
1422: $thread['parent'],
1423: $text,
1424: $thread['indent'],
1425: true,
1426: array(
1427: 'class' => 'linedRow',
1428: 'icon' => false
1429: ),
1430: array(
1431: $thread['message_author'],
1432: $thread['message_date']
1433: )
1434: );
1435: }
1436:
1437: return $tree->getTree(true);
1438: }
1439:
1440:
1441: $view = new Agora_View();
1442: $view->threads_list = $threads;
1443: $view->col_headers = $col_headers;
1444: $view->thread_view_bodies = $bodies;
1445:
1446:
1447: if (!$template_file) {
1448: $template_file = 'messages/threads';
1449: }
1450:
1451: return $view->render($template_file);
1452: }
1453:
1454: 1455: 1456:
1457: public function getThreadRoot($message_id)
1458: {
1459: $sql = 'SELECT message_thread FROM ' . $this->_threads_table . ' WHERE message_id = ?';
1460: try {
1461: $thread_id = $this->_db->selectValue($sql, array($message_id));
1462: } catch (Horde_Db_Exception $e) {
1463: throw new Agora_Exception($e->getMessage());
1464: }
1465: return $thread_id ? $thread_id : $message_id;
1466: }
1467:
1468: 1469:
1470: public function setThreadLock($message_id, $lock)
1471: {
1472: $sql = 'UPDATE ' . $this->_threads_table . ' SET locked = ? WHERE message_id = ? OR message_thread = ?';
1473: $values = array($lock, $message_id, $message_id);
1474: return $this->_db->execute($sql, $values);
1475: }
1476:
1477: 1478: 1479:
1480: public function isThreadLocked($message_id)
1481: {
1482: $sql = 'SELECT message_thread FROM ' . $this->_threads_table . ' WHERE message_id = ?';
1483: $thread = $this->_db->selectValue($sql, array($message_id));
1484:
1485: return $this->_db->selectValue('SELECT locked FROM ' . $this->_threads_table . ' WHERE message_id = ?', array($thread));
1486: }
1487:
1488: 1489:
1490: public function getThreadActions()
1491: {
1492:
1493: $actions = array();
1494:
1495: $url = Agora::setAgoraId($this->_forum_id, null, Horde::url('messages/edit.php'));
1496: if ($this->hasPermission(Horde_Perms::EDIT)) {
1497: $actions[] = array('url' => $url, 'label' => _("Post message"));
1498: }
1499:
1500: if ($this->hasPermission(Horde_Perms::DELETE)) {
1501: if ($this->_scope == 'agora') {
1502: $url = Agora::setAgoraId($this->_forum_id, null, Horde::url('editforum.php'));
1503: $actions[] = array('url' => $url, 'label' => _("Edit Forum"));
1504: }
1505: $url = Agora::setAgoraId($this->_forum_id, null, Horde::url('deleteforum.php'), $this->_scope);
1506: $actions[] = array('url' => $url, 'label' => _("Delete Forum"));
1507: $url = Agora::setAgoraId($this->_forum_id, null, Horde::url('ban.php'), $this->_scope);
1508: $actions[] = array('url' => $url, 'label' => _("Ban"));
1509: }
1510:
1511: return $actions;
1512: }
1513:
1514: 1515:
1516: public function getForm($vars, $title, $editing = false, $new_forum = false)
1517: {
1518: global $conf;
1519:
1520: $form = new Agora_Form_Message($vars, $title);
1521: $form->setButtons($editing ? _("Save") : _("Post"));
1522: $form->addHidden('', 'url', 'text', false);
1523:
1524:
1525: if ($new_forum) {
1526: 1527:
1528: $form->addHidden('', 'new_forum', 'text', false);
1529: } else {
1530:
1531: $form->addHidden('', 'forum_id', 'int', false);
1532: }
1533:
1534: $form->addHidden('', 'scope', 'text', false);
1535: $form->addHidden('', 'message_id', 'int', false);
1536: $form->addHidden('', 'message_parent_id', 'int', false);
1537:
1538: if (!$GLOBALS['registry']->getAuth()) {
1539: $form->addVariable(_("From"), 'posted_by', 'text', true);
1540: }
1541:
1542:
1543: if ($vars->get('message_parent_id')) {
1544: $desc = '<input type="button" value="' . _("Quote") . '" class="button" '
1545: . 'onClick="this.form.message_body.value=this.form.message_body.value + this.form.message_body_old.value; this.form.message_body_old.value = \'\';" />';
1546: $form->addVariable(_("Subject"), 'message_subject', 'text', true, false, $desc);
1547: $form->addHidden('', 'message_body_old', 'longtext', false);
1548: } else {
1549: $form->addVariable(_("Subject"), 'message_subject', 'text', true);
1550: }
1551:
1552: $form->addVariable(_("Message"), 'message_body', 'longtext', true);
1553:
1554: 1555:
1556: if ($vars->get('attachment_preview')) {
1557: $form->addVariable(_("Delete the existing attachment?"), 'attachment_delete', 'boolean', false);
1558: $form->addVariable(_("Current attachment"), 'attachment_preview', 'html', false);
1559: }
1560:
1561: if ($this->allowAttachments()) {
1562: $form->addVariable(_("Attachment"), 'message_attachment', 'file', false);
1563: }
1564:
1565: if (!empty($conf['forums']['captcha']) && !$GLOBALS['registry']->getAuth()) {
1566: $form->addVariable(_("Spam protection"), 'captcha', 'figlet', true, null, null, array(Agora::getCAPTCHA(!$form->isSubmitted()), $conf['forums']['figlet_font']));
1567: }
1568:
1569: return $form;
1570: }
1571:
1572: 1573: 1574: 1575: 1576: 1577: 1578:
1579: public function dateFormat($timestamp)
1580: {
1581: return strftime($GLOBALS['prefs']->getValue('date_format'), $timestamp)
1582: . ' '
1583: . (date($GLOBALS['prefs']->getValue('twentyFour') ? 'G:i' : 'g:ia', $timestamp));
1584: }
1585:
1586: 1587: 1588: 1589: 1590: 1591:
1592: public function logView($thread_id)
1593: {
1594: if (!$GLOBALS['conf']['threads']['track_views']) {
1595: return false;
1596: }
1597:
1598: if ($GLOBALS['browser']->isRobot()) {
1599: return false;
1600: }
1601:
1602:
1603: if (isset($_COOKIE['agora_viewed_threads']) &&
1604: strpos($_COOKIE['agora_viewed_threads'], ':' . $thread_id . '|') !== false) {
1605: return false;
1606: }
1607:
1608:
1609: if (!isset($_COOKIE['agora_viewed_threads'])) {
1610: $_COOKIE['agora_viewed_threads'] = ':';
1611: }
1612: $_COOKIE['agora_viewed_threads'] .= $thread_id . '|' . $_SERVER['REQUEST_TIME'] . ':';
1613:
1614: setcookie('agora_viewed_threads', $_COOKIE['agora_viewed_threads'], $_SERVER['REQUEST_TIME']+22896000,
1615: $GLOBALS['conf']['cookie']['path'], $GLOBALS['conf']['cookie']['domain'],
1616: $GLOBALS['conf']['use_ssl'] == 1 ? 1 : 0);
1617:
1618:
1619: $sql = 'UPDATE ' . $this->_threads_table . ' SET view_count = view_count + 1 WHERE message_id = ?';
1620: try {
1621: $this->_db->execute($sql, array($thread_id));
1622: } catch (Horde_Db_Exception $e) {
1623: throw new Agora_Exception($e->getMessage());
1624: }
1625:
1626: return true;
1627: }
1628:
1629: 1630: 1631: 1632: 1633:
1634: public function getAttachmentLink($message_id)
1635: {
1636: if (!$this->allowAttachments()) {
1637: return '';
1638: }
1639:
1640: $sql = 'SELECT file_id, file_name, file_size, file_type FROM agora_files WHERE message_id = ?';
1641: try {
1642: $files = $this->_db->selectAll($sql, array($message_id));
1643: } catch (Horde_Db_Exception $e) {
1644: throw new Agora_Exception($e->getMessage());
1645: }
1646: if (empty($files)) {
1647: return $files;
1648: }
1649:
1650:
1651: $html = '<br />';
1652: $view_url = Horde::url('view.php');
1653: foreach ($files as $file) {
1654: $mime_icon = $GLOBALS['injector']->getInstance('Horde_Core_Factory_MimeViewer')->getIcon($file['file_type']);
1655: $title = _("download") . ': ' . $file['file_name'];
1656: $tooltip = $title . "\n" . sprintf(_("size: %s"), $this->formatSize($file['file_size'])) . "\n" . sprintf(_("type: %s"), $file['file_type']);
1657: $url = Horde_Util::addParameter($view_url, array('forum_id' => $this->_forum_id,
1658: 'message_id' => $message_id,
1659: 'file_id' => $file['file_id'],
1660: 'file_name' => $file['file_name'],
1661: 'file_type' => $file['file_type']));
1662: $html .= Horde::linkTooltip($url, $title, '', '', '', $tooltip) .
1663: Horde::img($mime_icon, $title, 'align="middle"', '') . ' ' . $file['file_name'] . '</a> <br />';
1664: }
1665:
1666: return $html;
1667: }
1668:
1669: 1670: 1671: 1672: 1673: 1674: 1675:
1676: public function formatSize($filesize)
1677: {
1678: $units = array('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
1679: $pass = 0;
1680: while($filesize >= 1024) {
1681: $filesize /= 1024;
1682: $pass++;
1683: }
1684:
1685: return round($filesize, 2) . ' ' . $units[$pass];
1686: }
1687:
1688:
1689: 1690: 1691: 1692: 1693: 1694: 1695: 1696: 1697:
1698: public function getForum($forum_id = 0)
1699: {
1700: if (!$forum_id) {
1701: $forum_id = $this->_forum_id;
1702: } elseif ($forum_id instanceof PEAR_Error) {
1703: return $forum_id;
1704: }
1705:
1706:
1707: $this->_forum_id = $forum_id;
1708:
1709:
1710: if (!$this->hasPermission(Horde_Perms::SHOW, $forum_id)) {
1711: return PEAR::raiseError(sprintf(_("You don't have permission to access messages in forum %s."), $forum_id));
1712: }
1713:
1714: $forum = $this->_cache->get('agora_forum_' . $forum_id, $GLOBALS['conf']['cache']['default_lifetime']);
1715: if ($forum) {
1716: return unserialize($forum);
1717: }
1718:
1719: $sql = 'SELECT forum_id, forum_name, scope, active, forum_description, '
1720: . 'forum_parent_id, forum_moderated, forum_attachments, '
1721: . 'forum_distribution_address, author, message_count, thread_count '
1722: . 'FROM ' . $this->_forums_table . ' WHERE forum_id = ?';
1723: try {
1724: $forum = $this->_db->selectOne($sql, array($forum_id));
1725: } catch (Horde_Db_Exception $e) {
1726: throw new Agora_Exception($e->getMessage());
1727: }
1728: if (empty($forum)) {
1729: throw new Horde_Exception_NotFound(sprintf(_("Forum %s does not exist."), $forum_id));
1730: }
1731:
1732: $forum['forum_name'] = $this->convertFromDriver($forum['forum_name']);
1733: $forum['forum_description'] = $this->convertFromDriver($forum['forum_description']);
1734: $forum['forum_distribution_address'] = $this->convertFromDriver($forum['forum_distribution_address']);
1735:
1736:
1737: $sql = 'SELECT horde_uid FROM agora_moderators WHERE forum_id = ?';
1738: try {
1739: $moderators = $this->_db->selectValues($sql, array($forum_id));
1740: } catch (Horde_Db_Exception $e) {
1741: throw new Agora_Exception($e->getMessage());
1742: }
1743: if (!empty($moderators)) {
1744: $forum['moderators'] = $moderators;
1745: }
1746:
1747: $this->_cache->set('agora_forum_' . $forum_id, serialize($forum));
1748:
1749: return $forum;
1750: }
1751:
1752: 1753: 1754:
1755: public function countForums()
1756: {
1757: $sql = 'SELECT COUNT(*) FROM ' . $this->_forums_table . ' WHERE active = ? AND scope = ?';
1758: return $this->_db->selectValue($sql, array(1, $this->_scope));
1759: }
1760:
1761: 1762: 1763: 1764: 1765: 1766: 1767: 1768: 1769: 1770: 1771: 1772: 1773: 1774: 1775: 1776: 1777: 1778: 1779: 1780: 1781:
1782: public function getForums($root_forum = 0, $formatted = true,
1783: $sort_by = 'forum_name', $sort_dir = 0,
1784: $add_scope = false, $from = 0, $count = 0)
1785: {
1786:
1787: $forums = $this->_getForums($root_forum, $formatted, $sort_by,
1788: $sort_dir, $add_scope, $from, $count);
1789: if ($forums instanceof PEAR_Error || empty($forums) || !$formatted) {
1790: return $forums;
1791: }
1792:
1793: $user = $GLOBALS['registry']->getAuth();
1794: $edit_url = Horde::url('messages/edit.php');
1795: $editforum_url = Horde::url('editforum.php');
1796: $delete_url = Horde::url('deleteforum.php');
1797:
1798: foreach ($forums as $key => &$forum) {
1799: if (!$this->hasPermission(Horde_Perms::SHOW, $forum['forum_id'], $forum['scope'])) {
1800: unset($forums[$key]);
1801: continue;
1802: }
1803:
1804: $forum['indentn'] = 0;
1805: $forum['indent'] = '';
1806: if (!$this->hasPermission(Horde_Perms::READ, $forum['forum_id'], $forum['scope'])) {
1807: continue;
1808: }
1809:
1810: $forum['url'] = Agora::setAgoraId($forum['forum_id'], null, Horde::url('threads.php'), $forum['scope'], true);
1811: $forum['message_count'] = number_format($forum['message_count']);
1812: $forum['thread_count'] = number_format($forum['thread_count']);
1813:
1814: if ($forum['last_message_id']) {
1815: $forum['last_message_date'] = $this->dateFormat($forum['last_message_timestamp']);
1816: $forum['last_message_url'] = Agora::setAgoraId($forum['forum_id'], $forum['last_message_id'], Horde::url('messages/index.php'), $forum['scope'], true);
1817: }
1818:
1819: $forum['actions'] = array();
1820:
1821:
1822: if ($this->hasPermission(Horde_Perms::EDIT, $forum['forum_id'], $forum['scope'])) {
1823:
1824: $url = Agora::setAgoraId($forum['forum_id'], null, $edit_url, $forum['scope'], true);
1825: $forum['actions'][] = Horde::link($url, _("Post message")) . _("New Post") . '</a>';
1826:
1827: if ($GLOBALS['registry']->isAdmin(array('permission' => 'agora:admin'))) {
1828:
1829: $url = Agora::setAgoraId($forum['forum_id'], null, $editforum_url, $forum['scope'], true);
1830: $forum['actions'][] = Horde::link($url, _("Edit forum")) . _("Edit") . '</a>';
1831: }
1832: }
1833:
1834: if ($GLOBALS['registry']->isAdmin(array('permission' => 'agora:admin'))) {
1835:
1836: $url = Agora::setAgoraId($forum['forum_id'], null, $delete_url, $forum['scope'], true);
1837: $forum['actions'][] = Horde::link($url, _("Delete forum")) . _("Delete") . '</a>';
1838: }
1839:
1840:
1841: if (isset($forum['moderators']) && in_array($user, $forum['moderators'])) {
1842: $sql = 'SELECT COUNT(forum_id) FROM ' . $this->_threads_table
1843: . ' WHERE forum_id = ? AND approved = ?'
1844: . ' GROUP BY forum_id';
1845: try {
1846: $unapproved = $this->_db->selectValue($sql, array($forum['forum_id'], 0));
1847: } catch (Horde_Db_Exception $e) {
1848: throw new Agora_Exception($e->getMessage());
1849: }
1850:
1851: $url = Horde::link(Horde::url('moderate.php', true), _("Moderate")) . _("Moderate") . '</a>';
1852: $forum['actions'][] = $url . ' (' . $unapproved . ')' ;
1853: }
1854: }
1855:
1856: return $forums;
1857: }
1858:
1859: 1860: 1861: 1862: 1863: 1864: 1865: 1866: 1867: 1868: 1869: 1870: 1871: 1872: 1873:
1874: protected function _getForums($root_forum = 0, $formatted = true,
1875: $sort_by = 'forum_name', $sort_dir = 0,
1876: $add_scope = false, $from = 0, $count = 0)
1877: {
1878: return array();
1879: }
1880:
1881: 1882: 1883: 1884: 1885: 1886: 1887: 1888:
1889: protected function _formatForums($forums)
1890: {
1891:
1892: foreach ($forums as $forum) {
1893: $forums_list[] = $forum['forum_id'];
1894: }
1895: $sql = 'SELECT forum_id, horde_uid'
1896: . ' FROM agora_moderators WHERE forum_id IN (' . implode(',', array_values($forums_list)) . ')';
1897: try {
1898: $moderators = $this->_db->selectAll($sql);
1899: } catch (Horde_Db_Exception $e) {
1900: throw new Agora_Exception($e->getMessage());
1901: }
1902:
1903: foreach ($forums as $key => $forum) {
1904: $forums[$key]['forum_name'] = $this->convertFromDriver($forums[$key]['forum_name']);
1905: $forums[$key]['forum_description'] = $this->convertFromDriver($forums[$key]['forum_description']);
1906: foreach ($moderators as $moderator) {
1907: if ($moderator['forum_id'] == $forum['forum_id']) {
1908: $forums[$key]['moderators'][] = $moderator['horde_uid'];
1909: }
1910: }
1911: }
1912:
1913: return $forums;
1914: }
1915:
1916: 1917: 1918: 1919: 1920:
1921: public function getBareForums()
1922: {
1923: return array();
1924: }
1925:
1926: 1927: 1928: 1929: 1930: 1931: 1932: 1933: 1934:
1935: public function newForum($forum_name, $owner)
1936: {
1937: if (empty($forum_name)) {
1938: throw new Agora_Exception(_("Cannot create a forum with an empty name."));
1939: }
1940:
1941: $sql = 'INSERT INTO ' . $this->_forums_table . ' (scope, forum_name, active, author) VALUES (?, ?, ?, ?)';
1942: $values = array($this->_scope, $this->convertToDriver($forum_name), 1, $owner);
1943: try {
1944: $forum_id = $this->_db->insert($sql, $values);
1945: } catch (Horde_Db_Exception $e) {
1946: throw new Agora_Exception($e->getMessage());
1947: }
1948:
1949: return $forum_id;
1950: }
1951:
1952: 1953: 1954: 1955: 1956: 1957: 1958: 1959: 1960: 1961: 1962: 1963: 1964: 1965: 1966: 1967:
1968: public function saveForum($info)
1969: {
1970: if (empty($info['forum_id'])) {
1971: if (empty($info['author'])) {
1972: $info['author'] = $GLOBALS['registry']->getAuth();
1973: }
1974: $info['forum_id'] = $this->newForum($info['forum_name'], $info['author']);
1975: }
1976:
1977: $sql = 'UPDATE ' . $this->_forums_table . ' SET forum_name = ?, forum_parent_id = ?, '
1978: . 'forum_description = ?, forum_moderated = ?, '
1979: . 'forum_attachments = ?, forum_distribution_address = ? '
1980: . 'WHERE forum_id = ?';
1981:
1982: $values = array($this->convertToDriver($info['forum_name']),
1983: (int)$info['forum_parent_id'],
1984: $this->convertToDriver($info['forum_description']),
1985: (int)$info['forum_moderated'],
1986: isset($info['forum_attachments']) ? (int)$info['forum_attachments'] : abs($GLOBALS['conf']['forums']['enable_attachments']),
1987: isset($info['forum_distribution_address']) ? $info['forum_distribution_address'] : '',
1988: $info['forum_id']);
1989:
1990: try {
1991: $this->_db->execute($sql, $values);
1992: } catch (Horde_Db_Exception $e) {
1993: throw new Agora_Exception($e->getMessage());
1994: }
1995:
1996: $this->_updateCacheState(0);
1997: $this->_cache->expire('agora_forum_' . $info['forum_id'], $GLOBALS['conf']['cache']['default_lifetime']);
1998:
1999: return $info['forum_id'];
2000: }
2001:
2002: 2003: 2004: 2005: 2006: 2007: 2008: 2009: 2010:
2011: public function deleteForum($forum_id)
2012: {
2013: $this->deleteThread();
2014:
2015:
2016: try {
2017: $this->_db->delete('DELETE FROM ' . $this->_forums_table . ' WHERE forum_id = ' . (int)$forum_id);
2018: } catch (Horde_Db_Exception $e) {
2019: throw new Agora_Exception($e->getMessage());
2020: }
2021:
2022: return true;
2023: }
2024:
2025: 2026: 2027: 2028: 2029: 2030: 2031: 2032: 2033: 2034: 2035: 2036: 2037: 2038: 2039: 2040: 2041: 2042: 2043: 2044: 2045: 2046: 2047: 2048: 2049: 2050: 2051: 2052: 2053:
2054: public function search($filter, $sort_by = 'message_subject', $sort_dir = 0,
2055: $from = 0, $count = 0)
2056: {
2057: if (!isset($filter['allkeywords'])) {
2058: $filter['allkeywords'] = false;
2059: }
2060: if (!isset($filter['searchsubjects'])) {
2061: $filter['searchsubjects'] = true;
2062: }
2063: if (!isset($filter['searchcontents'])) {
2064: $filter['searchcontents'] = false;
2065: }
2066:
2067:
2068: $sql = 'SELECT forum_id, forum_name FROM ' . $this->_forums_table . ' WHERE ';
2069: if (empty($filter['forums'])) {
2070: $sql .= ' active = ? AND scope = ?';
2071: $values = array(1, $this->_scope);
2072: } else {
2073: $sql .= ' forum_id IN (' . implode(',', $filter['forums']) . ')';
2074: $values = array();
2075: }
2076: try {
2077: $forums = $this->_db->selectAssoc($sql, $values);
2078: } catch (Horde_Db_Exception $e) {
2079: throw new Agora_Exception($e->getMessage());
2080: }
2081:
2082:
2083: $sql = ' FROM ' . $this->_threads_table . ' WHERE forum_id IN (' . implode(',', array_keys($forums)) . ')';
2084:
2085: if (!empty($filter['keywords'])) {
2086: $sql .= ' AND (';
2087: if ($filter['searchsubjects']) {
2088: $keywords = '';
2089: foreach ($filter['keywords'] as $keyword) {
2090: if (!empty($keywords)) {
2091: $keywords .= $filter['allkeywords'] ? ' AND ' : ' OR ';
2092: }
2093: $keywords .= 'message_subject LIKE ' . $this->_db->quote('%' . $keyword . '%');
2094: }
2095: $sql .= '(' . $keywords . ')';
2096: }
2097: if ($filter['searchcontents']) {
2098: if ($filter['searchsubjects']) {
2099: $sql .= ' OR ';
2100: }
2101: $keywords = '';
2102: foreach ($filter['keywords'] as $keyword) {
2103: if (!empty($keywords)) {
2104: $keywords .= $filter['allkeywords'] ? ' AND ' : ' OR ';
2105: }
2106: $keywords .= 'body LIKE ' . $this->_db->quote('%' . $keyword . '%');
2107: }
2108: $sql .= '(' . $keywords . ')';
2109: }
2110: $sql .= ')';
2111: }
2112:
2113: if (!empty($filter['author'])) {
2114: $sql .= ' AND message_author = ' . $this->_db->quote(Horde_String::lower($filter['author']));
2115: }
2116:
2117:
2118: $sql .= ' ORDER BY ' . $sort_by . ' ' . ($sort_dir ? 'DESC' : 'ASC');
2119:
2120:
2121: if ($count) {
2122: $total = $this->_db->selectValue('SELECT COUNT(*) ' . $sql);
2123: $sql = $this->_db->addLimitOffset($sql, array('limit' => $count, 'offset' => $from));
2124: }
2125:
2126: $sql = 'SELECT message_id, forum_id, message_subject, message_author, message_timestamp ' . $sql;
2127: try {
2128: $messages = $this->_db->select($sql);
2129: } catch (Horde_Db_Exception $e) {
2130: throw new Agora_Exception($e->getMessage());
2131: }
2132: if (empty($messages)) {
2133: return array('results' => array(), 'total' => 0);
2134: }
2135:
2136: $results = array();
2137: $msg_url = Horde::url('messages/index.php');
2138: $forum_url = Horde::url('threads.php');
2139: while ($message = $messages->fetch()) {
2140: if (!isset($results[$message['forum_id']])) {
2141: $index = array('agora' => $message['forum_id'], 'scope' => $this->_scope);
2142: $results[$message['forum_id']] = array('forum_id' => $message['forum_id'],
2143: 'forum_url' => Horde_Util::addParameter($forum_url, $index),
2144: 'forum_name' => $this->convertFromDriver($forums[$message['forum_id']]),
2145: 'messages' => array());
2146: }
2147: $index = array('agora' => $message['forum_id']. '.' . $message['message_id'], 'scope' => $this->_scope);
2148: $results[$message['forum_id']]['messages'][] = array(
2149: 'message_id' => $message['message_id'],
2150: 'message_subject' => htmlspecialchars($this->convertFromDriver($message['message_subject'])),
2151: 'message_author' => $message['message_author'],
2152: 'message_date' => $this->dateFormat($message['message_timestamp']),
2153: 'message_url' => Horde_Util::addParameter($msg_url, $index));
2154: }
2155:
2156: return array('results' => $results, 'total' => $total);
2157: }
2158:
2159: 2160: 2161: 2162: 2163: 2164: 2165: 2166: 2167:
2168: public function hasPermission($perm = Horde_Perms::READ, $forum_id = null, $scope = null)
2169: {
2170:
2171: if (($forum_id === null && isset($this->_forum['author']) && $this->_forum['author'] == $GLOBALS['registry']->getAuth()) ||
2172: $GLOBALS['registry']->isAdmin(array('permission' => 'agora:admin'))) {
2173: return true;
2174: }
2175:
2176:
2177: if ($forum_id === null) {
2178: $forum_id = $this->_forum_id;
2179: }
2180:
2181: if ($scope === null) {
2182: $scope = $this->_scope;
2183: }
2184:
2185: $perms = $GLOBALS['injector']->getInstance('Horde_Perms');
2186: if (!$perms->exists('agora:forums:' . $scope) &&
2187: !$perms->exists('agora:forums:' . $scope . ':' . $forum_id)) {
2188: return ($perm & Horde_Perms::DELETE) ? false : true;
2189: }
2190:
2191: return $perms->hasPermission('agora:forums:' . $scope, $GLOBALS['registry']->getAuth(), $perm) ||
2192: $perms->hasPermission('agora:forums:' . $scope . ':' . $forum_id, $GLOBALS['registry']->getAuth(), $perm);
2193: }
2194:
2195: 2196: 2197: 2198: 2199: 2200: 2201:
2202: public function convertFromDriver($value)
2203: {
2204: return Horde_String::convertCharset($value, $this->_charset, 'UTF-8');
2205: }
2206:
2207: 2208: 2209: 2210: 2211: 2212: 2213:
2214: public function convertToDriver($value)
2215: {
2216: return Horde_String::convertCharset($value, 'UTF-8', $this->_charset);
2217: }
2218:
2219: 2220: 2221:
2222: private function _updateCacheState($thread)
2223: {
2224: if (is_array($thread)) {
2225: foreach ($thread as $id) {
2226: $key = 'prefix_' . $this->_forum_id . '_' . $id;
2227: $prefix = $this->_cache->get($key, $GLOBALS['conf']['cache']['default_lifetime']);
2228: if ($prefix) {
2229: $this->_cache->set($key, $prefix + 1);
2230: }
2231: }
2232: } else {
2233: $key = 'prefix_' . $this->_forum_id . '_' . $thread;
2234: $prefix = $this->_cache->get($key, $GLOBALS['conf']['cache']['default_lifetime']);
2235: if ($prefix) {
2236: $this->_cache->set($key, $prefix + 1);
2237: } else {
2238: $this->_cache->set($key, 2);
2239: }
2240: }
2241: }
2242:
2243: 2244: 2245:
2246: private function _getCacheKey($key, $thread = 0)
2247: {
2248: static $prefix;
2249:
2250: if ($prefix == null) {
2251: $prefix = $this->_cache->get('prefix_' . $this->_forum_id . '_' . $thread,
2252: $GLOBALS['conf']['cache']['default_lifetime']);
2253: if (!$prefix) {
2254: $prefix = '1';
2255: }
2256: }
2257:
2258: return 's_' . $prefix . '_' . $thread . '_' . $key;
2259: }
2260:
2261: 2262: 2263:
2264: protected function _getCache($key, $thread = 0)
2265: {
2266: $key = $this->_getCacheKey($key, $thread);
2267:
2268: return $this->_cache->get($key, $GLOBALS['conf']['cache']['default_lifetime']);
2269: }
2270:
2271: 2272: 2273:
2274: protected function _setCache($key, $value, $thread = 0)
2275: {
2276: $key = $this->_getCacheKey($key, $thread);
2277:
2278: return $this->_cache->set($key, $value);
2279: }
2280: }
2281: