diff --git a/app/Controller/EventsController.php b/app/Controller/EventsController.php index 723b5db78..30bf98097 100755 --- a/app/Controller/EventsController.php +++ b/app/Controller/EventsController.php @@ -1,7 +1,6 @@ set('allPivots', $this->Session->read('pivot_thread')); // Show the discussion $this->loadModel('Thread'); - $thread = $this->Thread->find('first', array('conditions' => array('event_id' => $id))); + $params = array('conditions' => array('event_id' => $id), + 'contain' => array( + 'Post' => array( + 'User', + ), + ) + ); + $thread = $this->Thread->find('first', $params); + if (empty($thread)) { + $thread['Post'] = array(); + } $this->set('posts', $thread['Post']); $this->set('myuserid', $this->Auth->user('id')); } diff --git a/app/Controller/PostsController.php b/app/Controller/PostsController.php new file mode 100644 index 000000000..71f7ae53c --- /dev/null +++ b/app/Controller/PostsController.php @@ -0,0 +1,261 @@ + 60, + ); + + public function beforeFilter() { + parent::beforeFilter(); + } + + // Find the thread_id and post_id in advance. If a user clicks post comment on the event view, send the event's related thread's ID + // Usage: + // /posts/add : Creates new thread with the added post as the first post. Title set by user + // /posts/add/event/id : Checks if the event already has a thread, if no it creates one. The post is added to the event's thread + // /posts/add/thread/id : Adds a post to the thread specified + // /posts/add/post/id : Adds a post as a reply to another post. The system finds the appropriate thread, adds the post to the thread and links to the post that is being replied to. + public function add($target_type = null, $target_id = null) { + $this->loadModel('Thread'); + $this->Thread->recursive = -1; + $distribution = 1; + $event_id = 0; + $post_id = 0; + // we have a target type and a target id. The target id defines what type of object we want to attach this event to (is it a reply to another post, + // did someone add a post to a thread, does a thread for the event exist already, etc. + switch ($target_type) { + case 'event' : + $this->loadModel('Event'); + $this->Event->recursive = -1; + $this->Event->read(null, $target_id); + $eventDiscussionTitle = 'Discussion about Event #' . $this->Event->data['Event']['id'] . ' (' . $this->Event->data['Event']['info'] . ')'; + if (!$this->Event->exists()) { + throw new NotFoundException(__('Invalid event')); + } + if (!$this->_isSiteAdmin()) { + if ($this->Event->data['Event']['distribution'] == 0 && $this->Event->data['Event']['org'] != $this->Auth->user('org')) { + throw new MethodNotAllowedException('You don\'t have permission to do that.'); + } + } + $thread = $this->Thread->find('first', array('conditions' => array('event_id' => $target_id))); + $title = $eventDiscussionTitle; + if (isset($thread['Thread']['id'])) { + $target_thread_id = $thread['Thread']['id']; + } else { + $target_thread_id = null; + } + $distribution = $this->Event->data['Event']['distribution']; + $org = $this->Event->data['Event']['org']; + break; + case 'thread' : + $target_thread_id = $target_id; + if ($target_id != null) { + $thread = $this->Thread->read(null, $target_thread_id); + if ($thread == null) { + throw new NotFoundException(__('Invalid thread')); + } + if (!$this->_isSiteAdmin()) { + if ($thread['Thread']['distribution'] == 0 && $this->Auth->user('org') != $thread['Thread']['org']) { + throw new MethodNotAllowedException('You don\'t have permission to do that.'); + } + } + $title = $this->Thread->data['Thread']['title']; + } + break; + case 'post' : + $this->Post->read(null, $target_id); + $target_thread_id = $this->Post->data['Post']['thread_id']; + $thread = $this->Thread->read(null, $target_thread_id); + if (!$this->_isSiteAdmin()) { + if ($thread['Thread']['distribution'] == 0 && $this->Auth->user('org') != $thread['Thread']['org']) { + throw new MethodNotAllowedException('You don\'t have permission to do that.'); + } + } + $title = $this->Thread->data['Thread']['title']; + $previousPost = $this->_grabPreviousPost($target_id); + $distribution = $previousPost['Thread']['distribution']; + $event_id = $previousPost['Thread']['event_id']; + $post_id = $target_id; + $target_thread_id = $previousPost['Thread']['id']; + break; + default: + $target_thread_id = null; + break; + } + + if ($this->request->is('post')) { + // Set the default values that we'll alter before actually saving data. These are the default values unless specifically modified. + // By default, all discussions will be visibile to everyone on the platform + $org = $this->Auth->user('org'); + // Set the title if it is setable in the add view. + if (empty($thread_id) && empty($target_type)) { + $title = $this->request->data['Post']['title']; + } + + if ($target_thread_id == null) { + // We have a post that was posted in a new thread. This could also mean that someone created the first post related to an event! + $this->Thread->create(); + // Take the title from above and the id of the event as event_id if we are adding a post to an event. + debug($this->request->data); + if ($target_type === 'event') { + $title = $eventDiscussionTitle; + $event_id = $this->Event->data['Event']['id']; + } + $newThread = array( + 'date_created' => date('Y/m/d h:i:s'), + 'date_modified' => date('Y/m/d h:i:s'), + 'user_id' => $this->Auth->user('id'), + 'event_id' => $event_id, + 'title' => $title, + 'distribution' => $distribution, + 'post_count' => 1, + 'org' => $org + ); + $this->Thread->save($newThread); + $target_thread_id = $this->Thread->getId(); + } else { + // In this case, we have a post that was posted in an already existing thread. Update the thread! + $this->Thread->read(null, $target_thread_id); + $this->Thread->data['Thread']['date_modified'] = date('Y/m/d h:i:s'); + $this->Thread->save(); + } + + + // Time to create our post! + $this->Post->create(); + $newPost = array( + 'date_created' => date('Y/m/d h:i:s'), + 'date_modified' => date('Y/m/d h:i:s'), + 'user_id' => $this->Auth->user('id'), + 'contents' => $this->request->data['Post']['message'], + 'post_id' => $post_id, + 'thread_id' => $target_thread_id, + ); + if ($this->Post->save($newPost)) { + $this->Thread->recursive = 0; + $this->Thread->contain('Post'); + $this->Thread->read(null, $target_thread_id); + $this->Thread->updateAfterPostChange(true); + $this->Session->setFlash(__('Post added')); + $this->redirect(array('action' => 'view', $this->Post->getId())); + } else { + $this->Session->setFlash('The post could not be added.'); + } + } + if ($target_type === 'post') { + $this->set('previous', $previousPost['Post']['contents']); + } + $this->set('thread_id', $target_thread_id); + $this->set('target_type', $target_type); + $this->set('target_id', $target_id); + if (isset($title)) { + $this->set('title', $title); + } + } + + public function edit($post_id) { + $this->Post->id = $post_id; + if (!$this->Post->exists()) { + throw new NotFoundException(__('Invalid post')); + } + $this->Post->recursive = 1; + $this->Post->read(null, $post_id); + if (!$this->_isSiteAdmin() && $this->Auth->user('id') != $this->Post->data['Post']['user_id']) { + throw new MethodNotAllowedException('This is not your event.'); + } + if ($this->request->is('post') || $this->request->is('put')) { + $this->request->data['Post']['date_modified'] = date('Y/m/d h:i:s'); + $fieldList = array('date_modified', 'contents'); + if ($this->Post->save($this->request->data, true, $fieldList)) { + $this->Session->setFlash('Post edited'); + $this->loadModel('Thread'); + $this->Thread->recursive = 0; + $this->Thread->contain('Post'); + $this->Thread->read(null, $this->Post->data['Post']['thread_id']); + $this->Thread->updateAfterPostChange(); + $this->redirect(array('action' => 'view', $post_id)); + } else { + $this->Session->setFlash('The Post could not be edited. Please, try again.'); + } + } + $this->set('title', $this->Post->data['Thread']['title']); + $this->set('contents', $this->Post->data['Post']['contents']); + $this->set('id', $post_id); + } + + public function delete($post_id) { + if (!$this->request->is('post')) { + throw new MethodNotAllowedException(); + } + $this->Post->id = $post_id; + if (!$this->Post->exists()) { + throw new NotFoundException(__('Invalid post')); + } + $this->Post->read(); + $temp = $this->Post->data; + if ($this->Auth->user('id') != $this->Post->data['Post']['user_id'] && !$this->_isSiteAdmin()) { + throw new MethodNotAllowedException('This post doesn\'t belong to you, so you cannot delete it.'); + } + if ($this->Post->delete()) { + $this->loadModel('Thread'); + $this->Thread->recursive = 0; + $this->Thread->contain('Post'); + $this->Thread->read(null, $this->Post->data['Thread']['id']); + $thread = $this->Thread->data['Thread']['id']; + if (!$this->Thread->updateAfterPostChange()) { + $this->Session->setFlash('Post and thread deleted'); + $this->redirect(array('controller' => 'threads', 'action' => 'index')); + } else { + $this->Session->setFlash('Post deleted'); + } + } + $this->redirect(array('controller' => 'threads', 'action' => 'view', $thread)); + + } + + public function index($thread_id) { + $this->loadModel('Thread'); + $this->Thread->id = $thread_id; + if (!$this->Thread->exists()) { + throw new NotFoundException(__('Invalid thread')); + } + } + + // Views the proper context for the post + public function view($post_id) { + $this->Post->id = $post_id; + if (!$this->Post->exists()) { + throw new NotFoundException(__('Invalid post')); + } + $this->Post->read(); + // We don't know what the context was, so let's try to guess what the user wants to see! + // If the post belongs to an event's discussion thread, redirect the user to the event's view + if ($this->Post->data['Thread']['event_id'] != 0) { + $this->redirect(array('controller' => 'events', 'action' => 'view', $this->Post->data['Thread']['event_id'])); + } else { + //Otherwise send the user to the thread's index. + $this->redirect(array('controller' => 'threads', 'action' => 'view', $this->Post->data['Thread']['id'])); + } + } + + private function _grabPreviousPost($post_id) { + $this->Post->id = $post_id; + $this->Post->read(); + return $this->Post->data; + } + +} +?> + \ No newline at end of file diff --git a/app/Controller/ThreadsController.php b/app/Controller/ThreadsController.php new file mode 100644 index 000000000..f0fbe5c85 --- /dev/null +++ b/app/Controller/ThreadsController.php @@ -0,0 +1,76 @@ + 60, + ); + + public function beforeFilter() { + parent::beforeFilter(); + } + + + public function view($thread_id = null) { + if ($thread_id != null) { + $this->Thread->id = $thread_id; + if (!$this->Thread->exists()) { + throw new NotFoundException(__('Invalid thread')); + } + $params = array('conditions' => array('id' => $thread_id), + 'contain' => array( + 'Post' => array( + 'User', + ), + ) + ); + $thread = $this->Thread->find('first', $params); + $this->set('thread_id', $thread_id); + $this->set('posts', $thread['Post']); + $this->set('myuserid', $this->Auth->user('id')); + $this->set('context', 'threads'); + $this->set('thread_title', $thread['Thread']['title']); + } + } + + public function index() { + $conditions = null; + + //if (!$this->_isSiteAdmin()) { + $conditions['OR'] = array( + 'Thread.distribution >' => 0, + 'Thread.org' => $this->Auth->user('org'), + ); + //$conditions[] = array('Thread.event_id' => 0); + //} + $this->paginate = array( + 'conditions' => array($conditions), + 'fields' => array('date_modified', 'date_created', 'org', 'distribution', 'title', 'post_count'), + 'contain' => array( + 'Post' =>array( + 'fields' => array(), + 'User' => array( + 'fields' => array('email', 'org') + ) + ), + ), + 'order' => array('Thread.date_modified' => 'desc'), + 'recursive' => 1 + ); + $this->set('threads', $this->paginate()); + $this->loadModel('Event'); + $this->set('distributionLevels', $this->Event->distributionLevels); + } +} +?> diff --git a/app/Model/Post.php b/app/Model/Post.php new file mode 100644 index 000000000..6da910676 --- /dev/null +++ b/app/Model/Post.php @@ -0,0 +1,19 @@ + array( + 'fields' => array('email', 'org'), + + ) + ); +} diff --git a/app/Model/Thread.php b/app/Model/Thread.php new file mode 100644 index 000000000..dd0fe591c --- /dev/null +++ b/app/Model/Thread.php @@ -0,0 +1,29 @@ +data['Post']); + // If we have 0 posts left, delete the thread! + if ($count == 0) { + $this->delete(); + return false; + } else { + $this->data['Thread']['post_count'] = $count; + if ($add) { + $this->data['Thread']['date_modified'] = date('Y/m/d h:i:s'); + } + $this->save($this->data); + return true; + } + } +} diff --git a/app/Model/User.php b/app/Model/User.php index 487baf314..852c49d2d 100755 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -227,6 +227,8 @@ class User extends AppModel { 'exclusive' => '', 'finderQuery' => '', 'counterQuery' => '' + ), + 'Post' => array( ) ); diff --git a/app/View/Elements/eventdiscussion.ctp b/app/View/Elements/eventdiscussion.ctp new file mode 100644 index 000000000..bd8aac56d --- /dev/null +++ b/app/View/Elements/eventdiscussion.ctp @@ -0,0 +1,90 @@ +
+ + > + + + + + + + + + + +
+
+ + + + + +
+ + + Top | + class = "whitelink"># +
+
+
+ Html->image('orgs/' . h($post['User']['org']) . '.png', array('alt' => h($post['User']['org']), 'title' => h($post['User']['org']), 'style' => 'width:48px; height:48px')); + ?> + + Command->convertQuotes(nl2br(h($post['contents']))); + if ($post['post_id'] !=0 || ($post['date_created'] != $post['date_modified'])) { + ?> +

+ + + In reply to post + ># + + Message edited at ' . h($post['date_modified']) . ''; + } + ?> +
+ + + + + +
+ + + Html->link('', array('controller' => 'posts', 'action' => 'edit', h($post['id'])), array('class' => 'icon-edit', 'title' => 'Edit')); + echo $this->Form->postLink('', array('controller' => 'posts', 'action' => 'delete', h($post['id'])), array('class' => 'icon-trash', 'title' => 'Delete'), __('Are you sure you want to delete this post?')); + } else { + ?> + + Html->link('', array('controller' => 'posts', 'action' => 'edit', h($post['id'])), array('class' => 'icon-edit', 'title' => 'Edit')); + echo $this->Form->postLink('', array('controller' => 'posts', 'action' => 'delete', h($post['id'])), array('class' => 'icon-trash', 'title' => 'Delete'), __('Are you sure you want to delete this post?')); + ?> + + +
+
+
+ +
\ No newline at end of file diff --git a/app/View/Elements/global_menu.ctp b/app/View/Elements/global_menu.ctp index 60736aca2..a55e14831 100755 --- a/app/View/Elements/global_menu.ctp +++ b/app/View/Elements/global_menu.ctp @@ -110,7 +110,16 @@ - + - element('eventdiscussion'); - ?> +

Discussion

+
+ element('eventdiscussion'); + ?> + >Add comment +
diff --git a/app/View/Helper/CommandHelper.php b/app/View/Helper/CommandHelper.php new file mode 100644 index 000000000..9ca4eca70 --- /dev/null +++ b/app/View/Helper/CommandHelper.php @@ -0,0 +1,13 @@ +', $string); + $string = str_ireplace('[/QUOTE]', '', $string); + return $string; + } + } +?> \ No newline at end of file diff --git a/app/View/Posts/add.ctp b/app/View/Posts/add.ctp new file mode 100644 index 000000000..3ace83fd0 --- /dev/null +++ b/app/View/Posts/add.ctp @@ -0,0 +1,58 @@ +
+Form->create('Post');?> +
+ Add Post +

You can quote something in your message by enclosing the quote between [QUOTE] and [/QUOTE] tags.

+ Form->input('title', array( + 'label' => 'Thread Subject', + 'class' => 'input-xxlarge' + )); + } else { + echo $this->Form->input('title', array( + 'label' => 'Thread Subject', + 'class' => 'input-xxlarge', + 'disabled' => 'true', + 'default' => $title + )); + } + if ($target_type === 'post') { + echo $this->Form->input('responseTo', array( + 'label' => 'In response to', + 'type' => 'textarea', + 'div' => 'input clear', + 'class' => 'input-xxlarge', + 'disabled' => 'true', + 'default' => h($previous) + )); + $quote = '[QUOTE]' . $previous . '[/QUOTE]' . "\n"; + } + echo $this->Form->input('message', array( + 'type' => 'textarea', + 'div' => 'input clear', + 'class' => 'input-xxlarge', + 'default' => h($quote) + )); + ?> +
+Form->button('Submit', array('class' => 'btn btn-primary')); +echo $this->Form->end(); +?> +
+
+ +
diff --git a/app/View/Posts/edit.ctp b/app/View/Posts/edit.ctp new file mode 100644 index 000000000..f151cd4f3 --- /dev/null +++ b/app/View/Posts/edit.ctp @@ -0,0 +1,32 @@ +
+Form->create('Post');?> +
+ Edit Post + Form->input('title', array( + 'label' => 'Thread Subject', + 'class' => 'input-xxlarge', + 'disabled' => 'true', + 'default' => $title + )); + echo $this->Form->input('contents', array( + 'type' => 'textarea', + 'div' => 'input clear', + 'class' => 'input-xxlarge', + 'default' => $contents + )); + ?> +
+Form->button('Submit', array('class' => 'btn btn-primary')); +echo $this->Form->end(); +?> +
+
+ +
\ No newline at end of file diff --git a/app/View/Threads/index.ctp b/app/View/Threads/index.ctp new file mode 100644 index 000000000..59fbd7b77 --- /dev/null +++ b/app/View/Threads/index.ctp @@ -0,0 +1,104 @@ +
+

Discussions

+ + + + + + + + + + + + + + + + + + + + + + + +
Paginator->sort('org');?>TitlePaginator->sort('date_modified', 'Last Post On');?>Last Post ByPaginator->sort('date_created', 'Thread started On');?>PostsDistribution
+ Html->image('orgs/' . h($thread['Thread']['org']) . '.png', array('alt' => h($thread['Thread']['org']), 'title' => h($thread['Thread']['org']), 'style' => 'width:24px; height:24px')); + else echo $this->Html->tag('span', h($thread['Thread']['org']), array('class' => 'welcome', 'style' => 'float:left;')); + ?> +   + + + + +   + + +   + + + + + + +
+

+ Paginator->counter(array( + 'format' => __('Page {:page} of {:pages}, showing {:current} records out of {:count} total, starting on record {:start}, ending on {:end}') + )); + ?> +

+ + +
+
+ +
diff --git a/app/View/Threads/view.ctp b/app/View/Threads/view.ctp new file mode 100644 index 000000000..8ef64c1a7 --- /dev/null +++ b/app/View/Threads/view.ctp @@ -0,0 +1,18 @@ +
+

+element('eventdiscussion'); +?> +
+ >Add comment +
+
+
+ +
\ No newline at end of file diff --git a/app/webroot/css/main.css b/app/webroot/css/main.css index 05b9e95d6..c219945da 100755 --- a/app/webroot/css/main.css +++ b/app/webroot/css/main.css @@ -466,41 +466,61 @@ dd { } .discussionBox { - border-collapse: separate; + box-shadow: 5px 5px 5px #888888; + width:100%; + max-width:900px; } .discussionBoxTD { - line-height: 20px; padding-right: 8px; padding-left: 8px; - padding-bottom: 4px; - border-color: #b2b2b2; border-style: solid; - border-width: 1px; + border-width: 0px; } .discussionBoxTDtop { - padding-top: 8px; - border-top-left-radius:7px; - border-top-right-radius:7px; - background-color: #F0F0F0; -} - -.discussionBoxTDMid { - padding-top: 8px; - border-top: 0px; - border-bottom: 0px; + color: #ffffff; + background-color: #cccccc; } .discussionBoxTDbottom { - border-bottom-left-radius:7px; - border-bottom-right-radius:7px; - background-color: #F0F0F0; + background-color: #eeeeee; padding-left: 8px; padding-bottom: 4px; padding-top: 4px; } +.discussionBoxTDMid { + padding-top: 8px; + height: 100px; + vertical-align: top; +} + +.discussionBoxTDMidLeft { + background-color: #eeeeee; + width: 80px; + text-align:center; +} + +.discussionBoxTDMidRight { +} + +.whitelink { + color:#ffffff; +} + +.quote { + margin: 0 10px; + margin-bottom: 10px; + -moz-border-radius: 0px; + -webkit-border-radius: 0px; + background: #f2f6f8 none; + border-radius: 0px; + border: 1px solid #417394; + position: relative; + top: 0; + padding: 4px; +} \ No newline at end of file