Skip to content
Browse files

first commit

  • Loading branch information...
0 parents commit 474d00d3dadb287ae0b3ca4da2177745c32c4c3e mark committed
Showing with 9,768 additions and 0 deletions.
  1. +6 −0 .gitignore
  2. +6 −0 .htaccess
  3. +5 −0 INSTALL.txt
  4. +340 −0 LICENSE.txt
  5. +49 −0 applications/conversations/controllers/appcontroller.php
  6. +294 −0 applications/conversations/controllers/messages.php
  7. +47 −0 applications/conversations/controllers/setup.php
  8. +204 −0 applications/conversations/design/conversations.screen.css
  9. BIN applications/conversations/design/star_hover.png
  10. BIN applications/conversations/design/star_off.png
  11. BIN applications/conversations/design/star_on.png
  12. +138 −0 applications/conversations/js/conversations.js
  13. +11 −0 applications/conversations/locale/en-CA/definitions.php
  14. +156 −0 applications/conversations/models/class.conversationmessagemodel.php
  15. +327 −0 applications/conversations/models/class.conversationmodel.php
  16. +63 −0 applications/conversations/modules/class.addpeoplemodule.php
  17. +30 −0 applications/conversations/modules/class.clearhistorymodule.php
  18. +42 −0 applications/conversations/modules/class.inthisconversationmodule.php
  19. +24 −0 applications/conversations/modules/class.newconversationmodule.php
  20. +15 −0 applications/conversations/settings/about.php
  21. +8 −0 applications/conversations/settings/configuration.php
  22. +34 −0 applications/conversations/settings/hooks.php
  23. +80 −0 applications/conversations/settings/structure.php
  24. +13 −0 applications/conversations/views/messages/add.php
  25. +14 −0 applications/conversations/views/messages/addmessage.php
  26. +17 −0 applications/conversations/views/messages/all.php
  27. +36 −0 applications/conversations/views/messages/conversations.php
  28. +46 −0 applications/conversations/views/messages/index.php
  29. +30 −0 applications/conversations/views/messages/messages.php
  30. +9 −0 applications/conversations/views/modules/addpeople.php
  31. +3 −0 applications/conversations/views/modules/clearhistory.php
  32. +20 −0 applications/conversations/views/modules/inthisconversation.php
  33. +2 −0 applications/conversations/views/modules/newconversation.php
  34. +5 −0 applications/conversations/views/setup/index.php
  35. +91 −0 applications/garden/controllers/activity.php
  36. +47 −0 applications/garden/controllers/appcontroller.php
  37. +269 −0 applications/garden/controllers/entry.php
  38. +240 −0 applications/garden/controllers/gardensetup.php
  39. +50 −0 applications/garden/controllers/home.php
  40. +450 −0 applications/garden/controllers/import.php
  41. +23 −0 applications/garden/controllers/plugin.php
  42. +504 −0 applications/garden/controllers/profile.php
  43. +148 −0 applications/garden/controllers/role.php
  44. +122 −0 applications/garden/controllers/routes.php
  45. +37 −0 applications/garden/controllers/search.php
  46. +379 −0 applications/garden/controllers/settings.php
  47. +245 −0 applications/garden/controllers/user.php
  48. +212 −0 applications/garden/controllers/utility.php
  49. +16 −0 applications/garden/design/README.txt
  50. BIN applications/garden/design/button_bg.png
  51. BIN applications/garden/design/button_bg2.png
  52. BIN applications/garden/design/button_bg3.png
  53. +3 −0 applications/garden/design/default.screen.css
  54. +92 −0 applications/garden/design/error.screen.css
  55. BIN applications/garden/design/five.png
  56. +240 −0 applications/garden/design/form.screen.css
  57. BIN applications/garden/design/four.png
  58. BIN applications/garden/design/install-vanilla.png
  59. BIN applications/garden/design/jcrop.gif
  60. +721 −0 applications/garden/design/layout.screen.css
  61. BIN applications/garden/design/logo.vanilla.png
  62. +199 −0 applications/garden/design/menu.screen.css
  63. BIN applications/garden/design/one.png
  64. BIN applications/garden/design/parent_divider.gif
  65. +49 −0 applications/garden/design/popup.screen.css
  66. +205 −0 applications/garden/design/profile.screen.css
  67. BIN applications/garden/design/progress.gif
  68. +46 −0 applications/garden/design/reset.css
  69. BIN applications/garden/design/setup-vanilla.png
  70. +210 −0 applications/garden/design/setup.screen.css
  71. BIN applications/garden/design/star_blue.png
  72. BIN applications/garden/design/star_grey.png
  73. BIN applications/garden/design/star_white.png
  74. BIN applications/garden/design/star_yellow.png
  75. BIN applications/garden/design/three.png
  76. BIN applications/garden/design/two.png
  77. +89 −0 applications/garden/design/typography.screen.css
  78. +107 −0 applications/garden/js/activity.js
  79. +45 −0 applications/garden/js/applications.js
  80. +19 −0 applications/garden/js/email.js
  81. +37 −0 applications/garden/js/entry.js
  82. +153 −0 applications/garden/js/profile.js
  83. +50 −0 applications/garden/js/registration.js
  84. +21 −0 applications/garden/js/routes.js
  85. +57 −0 applications/garden/js/user.js
  86. +90 −0 applications/garden/locale/en-CA/definitions.php
  87. +171 −0 applications/garden/models/class.activitymodel.php
  88. +184 −0 applications/garden/models/class.invitationmodel.php
  89. +262 −0 applications/garden/models/class.permissionmodel.php
  90. +260 −0 applications/garden/models/class.rolemodel.php
  91. +212 −0 applications/garden/models/class.searchmodel.php
  92. +1,083 −0 applications/garden/models/class.usermodel.php
  93. +34 −0 applications/garden/modules/class.guestmodule.php
  94. +222 −0 applications/garden/modules/class.headmodule.php
Sorry, we could not display the entire diff because too many files (581) changed.
6 .gitignore
@@ -0,0 +1,6 @@
+/cache/*/
+/cache/*.php
+/uploads/*/
+/uploads/*.*
+/conf/config.php
+.DS_Store
6 .htaccess
@@ -0,0 +1,6 @@
+<IfModule mod_rewrite.c>
+ RewriteEngine On
+ RewriteCond %{REQUEST_FILENAME} !-d
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteRule ^(.*)$ index.php/$1 [QSA,L]
+</IfModule>
5 INSTALL.txt
@@ -0,0 +1,5 @@
+1. Upload this entire file structure up to your web server.
+2. Navigate to that folder in your web browser.
+3. Follow the instructions on screen.
+
+Troubleshooting Help: http://gardenplatform.com/docs
340 LICENSE.txt
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
49 applications/conversations/controllers/appcontroller.php
@@ -0,0 +1,49 @@
+<?php if (!defined('APPLICATION')) exit();
+/*
+Copyright 2008, 2009 Mark O'Sullivan
+This file is part of Garden.
+Garden is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+Garden is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+You should have received a copy of the GNU General Public License along with Garden. If not, see <http://www.gnu.org/licenses/>.
+Contact Mark O'Sullivan at mark [at] lussumo [dot] com
+*/
+
+class ConversationsController extends Controller {
+
+ /// <summary>
+ /// Returns an array of pages that contain settings information for this
+ /// application.
+ /// </summary>
+ public function GetSettingsPages(&$Menu) {
+ // There are no configuration pages for Conversations
+ }
+
+ public function __construct() {
+ parent::__construct();
+ }
+
+ public function Initialize() {
+ $this->Permission('Garden.SignIn.Allow');
+ if ($this->DeliveryType() == DELIVERY_TYPE_ALL) {
+ $this->Head = new HeadModule($this);
+ $this->Head->AddScript('js/library/jquery.js');
+ $this->Head->AddScript('js/library/jquery.livequery.js');
+ $this->Head->AddScript('js/library/jquery.form.js');
+ $this->Head->AddScript('js/library/jquery.popup.js');
+ $this->Head->AddScript('js/library/jquery.menu.js');
+ $this->Head->AddScript('js/library/jquery.gardenhandleajaxform.js');
+ $this->Head->AddScript('js/library/jquery.gardenmorepager.js');
+ $this->Head->AddScript('js/library/jquery.autogrow.js');
+ $this->Head->AddScript('js/library/jquery.autocomplete.js');
+ $this->Head->AddScript('js/global.js');
+ $this->Head->AddScript('/applications/conversations/js/conversations.js');
+ }
+
+ $this->AddCssFile('default.screen.css');
+ $this->AddCssFile('menu.screen.css');
+ $this->AddCssFile('popup.screen.css');
+ $this->AddCssFile('vanilla.screen.css');
+ $this->AddCssFile('conversations.screen.css');
+ parent::Initialize();
+ }
+}
294 applications/conversations/controllers/messages.php
@@ -0,0 +1,294 @@
+<?php if (!defined('APPLICATION')) exit();
+/*
+Copyright 2008, 2009 Mark O'Sullivan
+This file is part of Garden.
+Garden is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+Garden is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+You should have received a copy of the GNU General Public License along with Garden. If not, see <http://www.gnu.org/licenses/>.
+Contact Mark O'Sullivan at mark [at] lussumo [dot] com
+*/
+
+/**
+ * MessagesController handles displaying lists of conversations and conversation messages.
+ */
+class MessagesController extends ConversationsController {
+
+ public $Uses = array('Form', 'ConversationModel', 'ConversationMessageModel');
+
+ public function Initialize() {
+ parent::Initialize();
+ $this->Menu->HighlightRoute('/messages/all');
+ }
+
+ /**
+ * Add a new conversations.
+ */
+ public function Add() {
+ $this->AddCssFile('form.screen.css');
+ $this->Form->SetModel($this->ConversationModel);
+
+ if ($this->Form->AuthenticatedPostBack()) {
+ $RecipientUserIDs = array();
+ $To = explode(',', $this->Form->GetFormValue('To', ''));
+ $UserModel = new Gdn_UserModel();
+ foreach ($To as $Name) {
+ if (trim($Name) != '') {
+ $User = $UserModel->Get(trim($Name));
+ if (is_object($User))
+ $RecipientUserIDs[] = $User->UserID;
+ }
+ }
+ $this->Form->SetFormValue('RecipientUserID', $RecipientUserIDs);
+ $ConversationID = $this->Form->Save($this->ConversationMessageModel);
+ if ($ConversationID !== FALSE)
+ Redirect('messages/'.$ConversationID);
+ }
+ $this->Render();
+ }
+
+ /**
+ * Add a message to a conversation.
+ */
+ public function AddMessage($ConversationID = '') {
+ $this->AddCssFile('form.screen.css');
+ $this->Form->SetModel($this->ConversationMessageModel);
+ if (is_numeric($ConversationID) && $ConversationID > 0)
+ $this->Form->AddHidden('ConversationID', $ConversationID);
+
+ if ($this->Form->AuthenticatedPostBack()) {
+ $ConversationID = $this->Form->GetFormValue('ConversationID', '');
+ $NewMessageID = $this->Form->Save();
+ if ($NewMessageID) {
+ if ($this->DeliveryType() == DELIVERY_TYPE_ALL)
+ Redirect('messages/'.$ConversationID.'/#'.$NewMessageID);
+
+ $this->SetJson('MessageID', $NewMessageID);
+ // If this was not a full-page delivery type, return the partial response
+ // Load all new messages that the user hasn't seen yet (including theirs)
+ $LastMessageID = $this->Form->GetFormValue('LastMessageID');
+ if (!is_numeric($LastMessageID))
+ $LastMessageID = $NewMessageID - 1;
+
+ $Session = Gdn::Session();
+ $this->Conversation = $this->ConversationModel->GetID($ConversationID, $Session->UserID);
+ $this->MessageData = $this->ConversationMessageModel->GetNew($ConversationID, $LastMessageID);
+ $this->View = 'messages';
+ } else {
+ // Handle ajax based errors...
+ if ($this->DeliveryType() != DELIVERY_TYPE_ALL)
+ $this->StatusMessage = $this->Form->Errors();
+ }
+ }
+ $this->Render();
+ }
+
+ /**
+ * Show all conversations for the currently authenticated user.
+ */
+ public function All($Offset = 0, $Limit = '', $BookmarkedOnly = FALSE) {
+ $this->Offset = $Offset;
+ $this->AddCssFile('form.screen.css');
+ $Session = Gdn::Session();
+ if (!is_numeric($this->Offset) || $this->Offset < 0)
+ $this->Offset = 0;
+
+ if ($Limit == '' || !is_numeric($Limit) || $Limit < 0)
+ $Limit = Gdn::Config('Conversations.Conversations.PerPage', 50);
+
+ $Wheres = array();
+ if ($BookmarkedOnly !== FALSE)
+ $Wheres['Bookmarked'] = '1';
+
+ $this->ConversationData = $this->ConversationModel->Get(
+ $Session->UserID,
+ $this->Offset,
+ $Limit,
+ $Wheres
+ );
+
+ $CountConversations = $this->ConversationModel->GetCount($Session->UserID, $Wheres);
+
+ // Build a pager
+ $PagerFactory = new PagerFactory();
+ $this->Pager = $PagerFactory->GetPager('MorePager', $this);
+ $this->Pager->MoreCode = 'Older Conversations';
+ $this->Pager->LessCode = 'Newer Conversations';
+ $this->Pager->ClientID = 'Pager';
+ $this->Pager->Configure(
+ $this->Offset,
+ $Limit,
+ $CountConversations,
+ 'messages/all/%1$s/%2$s/'
+ );
+
+ // Deliver json data if necessary
+ if ($this->_DeliveryType != DELIVERY_TYPE_ALL) {
+ $this->SetJson('LessRow', $this->Pager->ToString('less'));
+ $this->SetJson('MoreRow', $this->Pager->ToString('more'));
+ $this->View = 'conversations';
+ }
+
+ $this->AddModule('NewConversationModule');
+ /*
+ // TODO: ADD THE DISMISS MODULE ABILITY
+ $DismissModule = new DismissModule($this);
+ // The first arg is the ID of the help module - used to identify if the user has dismissed it or not.
+ // The second arg is the translation code for the help message
+ $DismissModule->Define('ConversationHelp', 'ConversationHelp');
+ $this->AddModule($DismissModule);
+
+ // TODO: ADD THE BOOKMARKING ABILITY
+ $BookmarkedConversationsModule = new BookmarkedConversationsModule($this);
+ $BookmarkedConversationsModule->GetData($Session->UserID);
+ $this->AddModule($BookmarkedConversationsModule);
+
+ // TODO: ADD THE DRAFTING ABILITY
+ $ConversationDraftsModule = new ConversationDraftsModule($this);
+ $ConversationDraftsModule->GetData($Session->UserID);
+ $this->AddModule($ConversationDraftsModule);
+ */
+ $this->Render();
+ }
+
+ /**
+ * Clear the message history for a specific conversation & user.
+ */
+ public function Clear($ConversationID = FALSE) {
+ $Session = Gdn::Session();
+ $this->_DeliveryType = DELIVERY_TYPE_BOOL;
+ if (is_numeric($ConversationID) && $ConversationID > 0 && $Session->IsValid())
+ $this->ConversationModel->Clear($ConversationID, $Session->UserID);
+
+ $this->StatusMessage = Gdn::Translate('The conversation has been cleared.');
+ $this->RedirectUrl = Url('/messages/all');
+ $this->Render();
+ }
+
+ /**
+ * A dataset of users taking part in this discussion. Used by $this->Index.
+ */
+ public $RecipientData;
+
+ /**
+ * The current offset of the paged data set. Defined and used by $this->Index and $this->All.
+ */
+ public $Offset;
+
+ /**
+ * Shows all uncleared messages within a conversation for the viewing user
+ *
+ * @todo ENFORCE PERMISSIONS SO THAT PEOPLE CAN'T READ OTHER PEOPLE'S MESSAGES
+ */
+ public function Index($ConversationID = FALSE, $Offset = -1, $Limit = '') {
+ $this->Offset = $Offset;
+ $Session = Gdn::Session();
+ if (!is_numeric($ConversationID) || $ConversationID < 0)
+ $ConversationID = 0;
+
+ $this->Form->SetModel($this->ConversationMessageModel);
+ $this->Form->AddHidden('ConversationID', $ConversationID);
+
+ $this->RecipientData = $this->ConversationModel->GetRecipients($ConversationID);
+ $this->Conversation = $this->ConversationModel->GetID($ConversationID, $Session->UserID);
+
+ if ($this->Conversation === FALSE)
+ return $this->ReDispatch('garden/home/filenotfound');
+
+ if ($Limit == '' || !is_numeric($Limit) || $Limit < 0)
+ $Limit = Gdn::Config('Conversations.Messages.PerPage', 50);
+
+ if (!is_numeric($this->Offset) || $this->Offset < 0) {
+ // Round down to the appropriate offset based on the user's read messages & messages per page
+ $CountReadMessages = $this->Conversation->CountMessages - $this->Conversation->CountNewMessages;
+ if ($CountReadMessages < 0)
+ $CountReadMessages = 0;
+
+ if ($CountReadMessages > $this->Conversation->CountMessages)
+ $CountReadMessages = $this->Conversation->CountMessages;
+
+ // (((67 comments / 10 perpage) = 6.7) rounded down = 6) * 10 perpage = offset 60;
+ $this->Offset = floor($CountReadMessages / $Limit) * $Limit;
+ }
+
+ $this->MessageData = $this->ConversationMessageModel->Get(
+ $ConversationID,
+ $Session->UserID,
+ $this->Offset,
+ $Limit
+ );
+ // $CountMessages = $this->ConversationMessageModel->GetCount($ConversationID, $Session->UserID);
+
+ // Build a pager
+ $PagerFactory = new PagerFactory();
+ $this->Pager = $PagerFactory->GetPager('MorePager', $this);
+ $this->Pager->MoreCode = 'Newer Messages';
+ $this->Pager->LessCode = 'Older Messages';
+ $this->Pager->ClientID = 'Pager';
+ $this->Pager->Configure(
+ $this->Offset,
+ $Limit,
+ $this->Conversation->CountMessages,
+ 'messages/'.$ConversationID.'/%1$s/%2$s/'
+ );
+
+ // Mark the conversation as ready by this user.
+ $this->ConversationModel->MarkRead($ConversationID, $Session->UserID);
+
+ // Deliver json data if necessary
+ if ($this->_DeliveryType != DELIVERY_TYPE_ALL) {
+ $this->SetJson('LessRow', $this->Pager->ToString('less'));
+ $this->SetJson('MoreRow', $this->Pager->ToString('more'));
+ $this->View = 'messages';
+ }
+
+ $this->AddModule('NewConversationModule');
+
+ $ClearHistoryModule = new ClearHistoryModule($this);
+ $ClearHistoryModule->ConversationID($ConversationID);
+ $this->AddModule($ClearHistoryModule);
+
+ $InThisConversationModule = new InThisConversationModule($this);
+ $InThisConversationModule->SetData($this->RecipientData);
+ $this->AddModule($InThisConversationModule);
+
+ $this->AddModule('AddPeopleModule');
+
+ $this->Render();
+ }
+
+ /**
+ * Allows users to bookmark conversations.
+ */
+ public function Bookmark($ConversationID = '', $TransientKey = '') {
+ $Session = Gdn::Session();
+ $Success = FALSE;
+ $Star = FALSE;
+ if (
+ is_numeric($ConversationID)
+ && $ConversationID > 0
+ && $Session->UserID > 0
+ && $Session->ValidateTransientKey($TransientKey)
+ ) {
+ $Bookmark = $this->ConversationModel->Bookmark($ConversationID, $Session->UserID);
+ }
+
+ if ($Bookmark === FALSE)
+ $this->Form->AddError('ErrorBool');
+ else
+ $this->SetJson('Bookmark', $Bookmark);
+
+ // Redirect back where the user came from if necessary
+ if ($this->_DeliveryType == DELIVERY_TYPE_ALL)
+ Redirect($_SERVER['HTTP_REFERER']);
+ else
+ $this->Render();
+ }
+
+ /// <summary>
+ /// Show bookmarked conversations for the current user.
+ /// </summary>
+ public function Bookmarked($Offset = 0, $Limit = '') {
+ $this->View = 'All';
+ $this->All($Offset, $Limit, TRUE);
+ }
+}
47 applications/conversations/controllers/setup.php
@@ -0,0 +1,47 @@
+<?php if (!defined('APPLICATION')) exit();
+/*
+Copyright 2008, 2009 Mark O'Sullivan
+This file is part of Garden.
+Garden is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+Garden is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+You should have received a copy of the GNU General Public License along with Garden. If not, see <http://www.gnu.org/licenses/>.
+Contact Mark O'Sullivan at mark [at] lussumo [dot] com
+*/
+
+/// <summary>
+/// Conversations Setup Controller
+/// </summary>
+class SetupController extends Controller {
+
+ public $Uses = array('Form');
+
+ /// <summary>
+ /// The methods in setup controllers should not call "Render". Rendering will
+ /// be handled by the controller that initiated the setup. This method should
+ /// return a boolean value indicating success.
+ /// </summary>
+ public function Index() {
+ $Database = Gdn::Database();
+ $Config = Gdn::Factory(Gdn::AliasConfig);
+ $Drop = Gdn::Config('Conversations.Version') === FALSE ? TRUE : FALSE;
+ $Explicit = TRUE;
+ $Construct = $Database->Structure();
+ $Validation = new Validation(); // This is going to be needed by structure.php to validate permission names
+ try {
+ include(PATH_APPLICATIONS . DS . 'conversations' . DS . 'settings' . DS . 'structure.php');
+ } catch (Exception $ex) {
+ $this->Form->AddError(strip_tags($ex->getMessage()));
+ }
+
+ if ($this->Form->ErrorCount() == 0) {
+ $ApplicationInfo = array();
+ include(CombinePaths(array(PATH_APPLICATIONS . DS . 'conversations' . DS . 'settings' . DS . 'about.php')));
+ $Version = ArrayValue('Version', ArrayValue('Conversations', $ApplicationInfo, array()), 'Undefined');
+ $Config->Load(PATH_CONF . DS . 'config.php', 'Save');
+ $Config->Set('Conversations.Version', $Version);
+ $Config->Save();
+ }
+
+ return $this->Form->ErrorCount() > 0 ? FALSE : TRUE;
+ }
+}
204 applications/conversations/design/conversations.screen.css
@@ -0,0 +1,204 @@
+fieldset {
+ padding: 0px !important;
+}
+.Conversations h2 a {
+ font-weight: normal;
+}
+h2.NewConversation {
+ float: right;
+}
+h2.NewConversation a {
+ font-weight: bold;
+}
+#Conversation,
+#Conversation ul,
+#Conversations,
+#Conversations ul {
+ list-style: none;
+ margin: 0px;
+ padding: 0px;
+}
+#Conversation,
+#Conversations {
+ border-top: 1px solid #eee;
+}
+#Conversation li,
+#Conversations li {
+ border-bottom: 1px solid #eee;
+ padding: 0px 0px 8px;
+}
+#Conversation li {
+ padding: 0px;
+}
+#Conversations li.Empty {
+ margin: 0px;
+ padding: 12px;
+ border: 0px;
+ background: #ffc;
+ -moz-border-radius-bottomleft: 2px;
+ -moz-border-radius-bottomright: 2px;
+ -webkit-border-bottom-left-radius: 2px;
+ -webkit-border-bottom-right-radius: 2px;
+}
+#Conversations li.Active {
+ cursor: pointer;
+}
+#Conversation li ul li,
+#Conversations li ul li {
+ display: inline;
+ border: 0px;
+}
+#Conversation li ul.Info,
+#Conversations li ul.Info {
+ font-size: 11px;
+ color: #777;
+ padding: 8px 0px 0px 8px;
+}
+#Conversation li ul.Info {
+ line-height: 200%;
+}
+#Conversation li ul.Info li,
+#Conversations li ul.Info li {
+ display: inline;
+}
+#Conversation li ul.Info li.Inserted,
+#Conversations li ul.Info li.Updated,
+#Conversations li ul.Info li.MessageCount {
+ padding-left: 12px;
+}
+#Conversation li ul.Info li.Author a,
+#Conversations li ul.Info li.Authors a {
+ font-size: 15px;
+ font-weight: bold;
+}
+#Conversation li.Author img,
+#Conversations li.Authors img {
+ float: left;
+ height: 42px;
+ width: 42px;
+ margin: 0px 8px 0px 0px;
+}
+#Conversations li ul li.MessageCount span {
+ background: #fffa22;
+ color: #000;
+ font-weight: bold;
+ margin-left: 20px;
+ padding: 0px 2px 2px 2px;
+}
+#Conversations li a.Link {
+ display: block;
+ margin: 0px 8px;
+ line-height: 140%;
+ color: #aaa;
+}
+#Conversations li.Active a.Link {
+ color: #555;
+}
+#Conversation div.Message {
+ padding: 0px 8px 5px;
+}
+
+
+/* Big Buttons */
+a.ClearConversation,
+a.NewConversation {
+ padding: 6px 0px;
+ display: block;
+ text-align: center;
+ font-size: 16px;
+ font-weight: bold;
+ -moz-border-radius: 2px;
+ -webkit-border-radius: 2px;
+ background: #44c7f4;
+ color: #fff;
+ text-decoration: none;
+ margin: 0px 0px 10px 0px;
+}
+a.ClearConversation:hover,
+a.NewConversation:hover {
+ background: #11e420;
+ color: #fff;
+}
+
+a.ClearConversation {
+ background: #f06;
+}
+a.ClearConversation:hover {
+ background: #c36;
+}
+
+/* Add Message Form */
+body.Index #Content form {
+ background: #e9f9ff;
+ padding: 4px 8px 8px;
+ margin: 1px 0 0;
+ -moz-border-radius-bottomleft: 2px;
+ -moz-border-radius-bottomright: 2px;
+ -webkit-border-bottom-left-radius: 2px;
+ -webkit-border-bottom-right-radius: 2px;
+}
+body.Index #Content form h3 {
+ font-size: 115%;
+ color: #2786C2;
+ font-weight: bold;
+}
+body.Index #Content form textarea {
+ width: 654px;
+ height: 100px;
+ min-height: 100px;
+ margin-bottom: 2px;
+}
+
+/* New Conversation Form */
+body.add #Panel {
+ display: none;
+}
+body.add #Content {
+ width: auto;
+}
+#ConversationForm {
+ border: 0px;
+ background: #daf4fd;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ padding: 8px 10px 10px;
+}
+#ConversationForm label {
+ float: left;
+ display: block;
+ width: 30px;
+ margin: 0 10px 0 0;
+}
+#ConversationForm label.Heading {
+ font-weight: bold;
+ font-size: 120%;
+ color: #2786c2;
+ line-height: 100%;
+ display: block;
+ width: auto;
+ float: none;
+ margin: 0 0px 8px 0;
+}
+#ConversationForm textarea.MultiComplete {
+ width: 896px;
+}
+#ConversationForm .TextBox {
+ width: 930px;
+ height: 100px;
+ min-height: 100px;
+ margin: 8px 0px 6px;
+}
+a.Cancel {
+ margin-left: 20px;
+}
+
+
+/* Panel Forms */
+div.AddPeople h4 {
+ border-bottom: 0px;
+}
+#Panel input.InputBox,
+div.AddPeople textarea.MultiComplete {
+ width: 225px;
+ margin-bottom: 2px;
+}
BIN applications/conversations/design/star_hover.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN applications/conversations/design/star_off.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN applications/conversations/design/star_on.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
138 applications/conversations/js/conversations.js
@@ -0,0 +1,138 @@
+// This file contains javascript that is specific to the garden/profile controller.
+jQuery(document).ready(function($) {
+
+ $('a.ClearConversation').popup({
+ confirm: true,
+ followConfirm: false
+ });
+
+ $('textarea.MessageBox, textarea.TextBox').autogrow();
+
+ // Make the entire row clickable on the conversation list.
+ $.fn.hoverRow = function() {
+ return this.each(function() {
+ var row = this;
+ var anchor = $(row).find('a.Link');
+ if (anchor.length > 0) {
+ $(row).hover(function() {
+ $(row).addClass('Active');
+ }, function() {
+ $(row).removeClass('Active');
+ }).click(function() {
+ document.location = $(anchor).attr('href');
+ });
+ }
+ });
+ }
+ $('#Conversations li').hoverRow();
+
+ // Hijack "add message" clicks and handle via ajax...
+ $.fn.handleMessageForm = function() {
+ this.click(function() {
+ var button = this;
+ var frm = $(button).parents('form').get(0);
+ var textbox = $(frm).find('textarea');
+ // Post the form, and append the results to #Discussion, and erase the textbox
+ var postValues = $(frm).serialize();
+ postValues += '&DeliveryType=3&DeliveryMethod=2'; // DELIVERY_TYPE_VIEW
+ postValues += '&'+button.name+'='+button.value;
+ var prefix = textbox.attr('name').replace('Message', '');
+ // Get the last message id on the page
+ var messages = $('#Conversation li');
+ var lastMessage = $(messages).get(messages.length - 1);
+ var lastMessageID = $(lastMessage).attr('id');
+ postValues += '&' + prefix + 'LastMessageID=' + lastMessageID;
+ $.ajax({
+ type: "POST",
+ url: $(frm).attr('action'),
+ data: postValues,
+ dataType: 'json',
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ $('.Popup').remove();
+ $.popup({}, $('#Definitions #TransportError').html().replace('%s', textStatus));
+ },
+ success: function(json) {
+ // Remove any old errors from the form
+ $(frm).find('div.Errors').remove();
+
+ if (json.FormSaved == false) {
+ $(frm).prepend(json.StatusMessage);
+ json.StatusMessage = null;
+ }
+ // Clean up the form
+ clearMessageForm();
+
+ // And show the new comments
+ $('#Conversation').append(json.Data);
+
+ // Remove any "More" pager links
+ $('#PagerMore').remove();
+
+ // And scroll to them
+ var target = $('#' + json.MessageID);
+ if (target.offset()) {
+ $('html,body').animate({scrollTop: target.offset().top}, 'fast');
+ }
+ inform(json.StatusMessage);
+ }
+ });
+ return false;
+
+ });
+ }
+ $('#Form_ConversationMessage :submit').handleMessageForm();
+
+ // Utility function to clear out the message form
+ function clearMessageForm() {
+ $('.Popup').remove();
+ var frm = $('#Form_ConversationMessage');
+ frm.find('textarea').val('');
+ frm.find('div.Errors').remove();
+ $('div.Information').fadeOut('fast', function() { $(this).remove(); });
+ }
+
+ // Enable multicomplete on selected inputs
+ $('.MultiComplete').autocomplete(
+ definition('WebRoot') + '/garden/user/autocomplete/',
+ {
+ minChars: 1,
+ multiple: true,
+ scrollHeight: 220,
+ selectFirst: true
+ }
+ ).autogrow();
+
+ // Set up paging
+ $('.MorePager').morepager({
+ pageContainerSelector: '#Conversations, #Conversation'
+ });
+
+ $('#Form_AddPeople :submit').click(function() {
+ var btn = this;
+ $(btn).hide();
+ $(btn).after('<span class="Progress">&nbsp;</span>');
+
+ var frm = $(btn).parents('form');
+ var textbox = $(frm).find('textarea');
+
+ // Post the form, show the status and then redirect.
+ $.ajax({
+ type: "POST",
+ url: $(frm).attr('action'),
+ data: $(frm).serialize() + '&DeliveryType=3&DeliveryMethod=2',
+ dataType: 'json',
+ error: function(XMLHttpRequest, textStatus, errorThrown) {
+ $('span.Progress').remove();
+ $(btn).show();
+ $.popup({}, $('#Definitions #TransportError').html().replace('%s', textStatus));
+ },
+ success: function(json) {
+ inform(json.StatusMessage);
+ if (json.RedirectUrl)
+ setTimeout("document.location='" + json.RedirectUrl + "';", 300);
+ }
+ });
+ return false;
+ });
+
+});
11 applications/conversations/locale/en-CA/definitions.php
@@ -0,0 +1,11 @@
+<?php if (!defined('APPLICATION')) exit();
+/*
+Copyright 2008, 2009 Mark O'Sullivan
+This file is part of Garden.
+Garden is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+Garden is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+You should have received a copy of the GNU General Public License along with Garden. If not, see <http://www.gnu.org/licenses/>.
+Contact Mark O'Sullivan at mark [at] lussumo [dot] com
+*/
+
+$Definition['RecipientUserID'] = 'recipient';
156 applications/conversations/models/class.conversationmessagemodel.php
@@ -0,0 +1,156 @@
+<?php if (!defined('APPLICATION')) exit();
+/*
+Copyright 2008, 2009 Mark O'Sullivan
+This file is part of Garden.
+Garden is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+Garden is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+You should have received a copy of the GNU General Public License along with Garden. If not, see <http://www.gnu.org/licenses/>.
+Contact Mark O'Sullivan at mark [at] lussumo [dot] com
+*/
+
+class ConversationMessageModel extends Model {
+ /// <summary>
+ /// Class constructor.
+ /// </summary>
+ public function __construct() {
+ parent::__construct('ConversationMessage');
+ }
+
+ public function Get($ConversationID, $ViewingUserID, $Offset = '0', $Limit = '', $Wheres = '') {
+ if ($Limit == '')
+ $Limit = Gdn::Config('Conversations.Messages.PerPage', 50);
+
+ $Offset = !is_numeric($Offset) || $Offset < 0 ? 0 : $Offset;
+ if (is_array($Wheres))
+ $this->SQL->Where($Wheres);
+
+ return $this->SQL
+ ->Select('cm.*')
+ ->Select('iu.Name', '', 'InsertName')
+ ->Select('iup.Name', '', 'InsertPhoto')
+ ->From('ConversationMessage cm')
+ ->Join('Conversation c', 'cm.ConversationID = c.ConversationID')
+ ->Join('UserConversation uc', 'c.ConversationID = uc.ConversationID and uc.UserID = '.$ViewingUserID)
+ ->Join('User iu', 'cm.InsertUserID = iu.UserID')
+ ->Join('Photo iup', 'iu.PhotoID = iup.PhotoID', 'left')
+ ->BeginWhereGroup()
+ ->Where('uc.DateCleared is null')
+ ->OrWhere('uc.DateCleared <', 'cm.DateInserted', TRUE, FALSE) // Make sure that cleared conversations do not show up unless they have new messages added.
+ ->EndWhereGroup()
+ ->Where('cm.ConversationID', $ConversationID)
+ ->OrderBy('cm.DateInserted', 'asc')
+ ->Limit($Limit, $Offset)
+ ->Get();
+ }
+
+ public function GetNew($ConversationID, $LastMessageID) {
+ $Session = Gdn::Session();
+ $this->SQL->Where('MessageID > ', $LastMessageID);
+ return $this->Get($ConversationID, $Session->UserID);
+ }
+
+ public function GetCount($ConversationID, $ViewingUserID, $Wheres = '') {
+ if (is_array($Wheres))
+ $this->SQL->Where($Wheres);
+
+ $Data = $this->SQL
+ ->Select('cm.MessageID', 'count', 'Count')
+ ->From('ConversationMessage cm')
+ ->Join('Conversation c', 'cm.ConversationID = c.ConversationID')
+ ->Join('UserConversation uc', 'c.ConversationID = uc.ConversationID and uc.UserID = '.$ViewingUserID)
+ ->BeginWhereGroup()
+ ->Where('uc.DateCleared is null')
+ ->OrWhere('uc.DateCleared >', 'c.DateUpdated', TRUE, FALSE) // Make sure that cleared conversations do not show up unless they have new messages added.
+ ->EndWhereGroup()
+ ->GroupBy('cm.ConversationID')
+ ->Where('cm.ConversationID', $ConversationID)
+ ->Get();
+ if ($Data->NumRows() > 0)
+ return $Data->FirstRow()->Count;
+
+ return 0;
+ }
+
+ public function Save($FormPostValues) {
+ $Session = Gdn::Session();
+
+ // Define the primary key in this model's table.
+ $this->DefineSchema();
+
+ // Add & apply any extra validation rules:
+ $this->Validation->ApplyRule('Body', 'Required');
+ $this->AddInsertFields($FormPostValues);
+
+ // Validate the form posted values
+ $MessageID = FALSE;
+ if($this->Validate($FormPostValues)) {
+ $Fields = $this->Validation->SchemaValidationFields(); // All fields on the form that relate to the schema
+ $MessageID = $this->SQL->Insert($this->Name, $Fields);
+ $ConversationID = ArrayValue('ConversationID', $Fields, 0);
+
+ // Update the conversation's DateUpdated field
+ $this->SQL
+ ->Update('Conversation')
+ ->Set('DateUpdated', Format::ToDateTime())
+ ->Set('UpdateUserID', $Session->UserID)
+ ->Where('ConversationID', $ConversationID)
+ ->Put();
+
+ // NOTE: INCREMENTING COUNTS INSTEAD OF GETTING ACTUAL COUNTS COULD
+ // BECOME A PROBLEM. WATCH FOR IT.
+ // Update the message counts for all users in the conversation
+ $this->SQL
+ ->Update('UserConversation')
+ ->Set('CountMessages', 'CountMessages + 1', FALSE)
+ ->Where('ConversationID', $ConversationID)
+ ->Put();
+
+ $this->SQL
+ ->Update('UserConversation')
+ ->Set('CountNewMessages', 'CountNewMessages + 1', FALSE)
+ ->Where('ConversationID', $ConversationID)
+ ->Where('UserID <>', $Session->UserID)
+ ->Put();
+
+ // Update the CountUnreadConversations count on each user related to the discussion.
+ $this->UpdateCountUnreadConversations($ConversationID, $Session->UserID);
+
+ // Update the userconversation records to reflect the most recently
+ // added message for all users other than the one that added the
+ // message (otherwise they would see their name/message on the
+ // conversation list instead of the person they are conversing with).
+ $this->SQL
+ ->Update('UserConversation')
+ ->Set('LastMessageID', $MessageID)
+ ->Where('ConversationID', $ConversationID)
+ ->Where('UserID <>', $Session->UserID)
+ ->Put();
+ }
+ return $MessageID;
+ }
+
+ /// </summary>
+ /// Update the CountUnreadConversations attribute on all users related to a
+ /// specific conversation.
+ /// </summary>
+ public function UpdateCountUnreadConversations($ConversationID, $MessageUserID) {
+ $UnreadData = $this->SQL
+ ->Select('c.UserID')
+ ->Select('c2.CountNewMessages', 'count', 'CountUnreadConversations')
+ ->From('UserConversation c')
+ ->Join('UserConversation c2', 'c.UserID = c2.UserID')
+ ->Where('c2.CountNewMessages >', 0)
+ ->Where('c.ConversationID', $ConversationID)
+ ->Where('c.UserID <>', $MessageUserID)
+ ->GroupBy('c.UserID')
+ ->Get();
+
+ foreach ($UnreadData->Result() as $User) {
+ $this->SQL
+ ->Update('User')
+ ->Set('CountUnreadConversations', $User->CountUnreadConversations)
+ ->Where('UserID', $User->UserID)
+ ->Put();
+ }
+ }
+}
327 applications/conversations/models/class.conversationmodel.php
@@ -0,0 +1,327 @@
+<?php if (!defined('APPLICATION')) exit();
+/*
+Copyright 2008, 2009 Mark O'Sullivan
+This file is part of Garden.
+Garden is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+Garden is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+You should have received a copy of the GNU General Public License along with Garden. If not, see <http://www.gnu.org/licenses/>.
+Contact Mark O'Sullivan at mark [at] lussumo [dot] com
+*/
+
+class ConversationModel extends Model {
+ /// <summary>
+ /// Class constructor.
+ /// </summary>
+ public function __construct() {
+ parent::__construct('Conversation');
+ }
+
+ public function ConversationQuery($ViewingUserID) {
+ $this->SQL
+ ->Select('c.*')
+ ->Select('uc.LastMessageID, uc.CountMessages, uc.CountNewMessages, uc.DateLastViewed, uc.DateCleared, uc.Bookmarked')
+ ->Select('lm.InsertUserID', '', 'LastMessageUserID')
+ ->Select('lm.DateInserted', '', 'DateLastMessage')
+ ->Select('lm.Body', '', 'LastMessage')
+ ->Select('lmu.Name', '', 'LastMessageName')
+ ->Select('lmup.Name', '', 'LastMessagePhoto')
+ ->Select('iu.Name', '', 'InsertName')
+ // ->Select('uu.Name', '', 'UpdateName')
+ ->From('Conversation c')
+ ->Join('User iu', 'c.InsertUserID = iu.UserID')
+ ->Join('UserConversation uc', 'c.ConversationID = uc.ConversationID and uc.UserID = '.$ViewingUserID)
+ ->Join('ConversationMessage lm', 'uc.LastMessageID = lm.MessageID')
+ ->Join('User lmu', 'lm.InsertUserID = lmu.UserID')
+ ->Join('Photo lmup', 'lmu.PhotoID = lmup.PhotoID', 'left')
+ ->BeginWhereGroup()
+ ->Where('uc.DateCleared is null')
+ ->OrWhere('c.DateUpdated >', 'uc.DateCleared', TRUE, FALSE) // Make sure that cleared conversations do not show up unless they have new messages added.
+ ->EndWhereGroup();
+ }
+
+ public function Get($ViewingUserID, $Offset = '0', $Limit = '', $Wheres = '') {
+ if ($Limit == '')
+ $Limit = Gdn::Config('Conversations.Conversations.PerPage', 50);
+
+ $Offset = !is_numeric($Offset) || $Offset < 0 ? 0 : $Offset;
+ $this->ConversationQuery($ViewingUserID);
+ if (is_array($Wheres))
+ $this->SQL->Where($Wheres);
+
+ return $this->SQL
+ ->OrderBy('c.DateUpdated', 'desc')
+ ->Limit($Limit, $Offset)
+ ->Get();
+ }
+
+ public function GetCount($ViewingUserID, $Wheres = '') {
+ if (is_array($Wheres))
+ $this->SQL->Where($Wheres);
+
+ $Data = $this->SQL
+ ->Select('c.ConversationID', 'count', 'Count')
+ ->From('Conversation c')
+ ->Join('UserConversation uc', 'c.ConversationID = uc.ConversationID and uc.UserID = '.$ViewingUserID)
+ ->BeginWhereGroup()
+ ->Where('uc.DateCleared is null')
+ ->OrWhere('c.DateUpdated >', 'uc.DateCleared', TRUE, FALSE) // Make sure that cleared conversations do not show up unless they have new messages added.
+ ->EndWhereGroup()
+ ->Get();
+
+ if ($Data->NumRows() > 0)
+ return $Data->FirstRow()->Count;
+
+ return 0;
+ }
+
+ public function GetID($ConversationID, $ViewingUserID) {
+ $this->ConversationQuery($ViewingUserID);
+ return $this->SQL
+ ->Where('c.ConversationID', $ConversationID)
+ ->Get()
+ ->FirstRow();
+ }
+
+ public function GetRecipients($ConversationID, $IgnoreUserID = '0') {
+ return $this->SQL
+ ->Select('uc.UserID, u.Name')
+ ->Select('cm.DateInserted', 'max', 'DateLastActive')
+ ->From('UserConversation uc')
+ ->Join('User u', 'uc.UserID = u.UserID')
+ ->Join('ConversationMessage cm', 'uc.ConversationID = cm.ConversationID and uc.UserID = cm.InsertUserID', 'left')
+ ->Where('uc.ConversationID', $ConversationID)
+ // ->Where('uc.UserID <>', $IgnoreUserID)
+ ->GroupBy('uc.UserID, u.Name')
+ ->Get();
+ }
+
+ public function Save($FormPostValues, $MessageModel) {
+ $Session = Gdn::Session();
+
+ // Define the primary key in this model's table.
+ $this->DefineSchema();
+ $MessageModel->DefineSchema();
+
+ // Add & apply any extra validation rules:
+ $this->Validation->ApplyRule('Body', 'Required');
+ $MessageModel->Validation->ApplyRule('Body', 'Required');
+ // Make sure that there is at least one recipient
+ $this->Validation->AddRule('OneOrMoreArrayItemRequired', 'function:ValidateOneOrMoreArrayItemRequired');
+ $this->Validation->ApplyRule('RecipientUserID', 'OneOrMoreArrayItemRequired');
+
+ // Add insert/update fields
+ $this->AddInsertFields($FormPostValues);
+ $this->AddUpdateFields($FormPostValues);
+
+ // Validate the form posted values
+ $ConversationID = FALSE;
+ if (
+ $this->Validate($FormPostValues)
+ && $MessageModel->Validate($FormPostValues)
+ ) {
+ $Fields = $this->Validation->ValidationFields(); // All fields on the form that relate to the schema
+
+ // Define the recipients, and make sure that the sender is in the list
+ $RecipientUserIDs = ArrayValue('RecipientUserID', $Fields, 0);
+ if (!in_array($Session->UserID, $RecipientUserIDs))
+ $RecipientUserIDs[] = $Session->UserID;
+
+ // Also make sure there are no duplicates in the recipient list
+ $RecipientUserIDs = array_unique($RecipientUserIDs);
+ sort($RecipientUserIDs);
+ $Fields = $this->Validation->SchemaValidationFields(); // All fields on the form that relate to the schema
+
+ // FIRST LOOK TO SEE IF THERE IS AN EXISTING CONVERSATION BETWEEN THESE
+ // RECIPIENTS THAT SHOULD BE USED INSTEAD OF CREATING A NEW ONE.
+ $Data = $this->SQL
+ ->Select('ConversationID')
+ ->From('Conversation')
+ ->Where('Contributors', Format::Serialize($RecipientUserIDs))
+ ->Get();
+
+ if ($Data->NumRows() > 0) {
+ $ConversationID = $Data->FirstRow()->ConversationID;
+ } else {
+ $Fields['Contributors'] = Format::Serialize($RecipientUserIDs);
+ $ConversationID = $this->SQL->Insert($this->Name, $Fields);
+ }
+
+ $FormPostValues['ConversationID'] = $ConversationID;
+ $MessageID = $MessageModel->Save($FormPostValues);
+ if ($Data->NumRows() == 0)
+ $this->SQL
+ ->Update('Conversation')
+ ->Set('FirstMessageID', $MessageID)
+ ->Where('ConversationID', $ConversationID)
+ ->Put();
+
+ // Now that the message & conversation have been inserted, insert all of the recipients
+ if ($Data->NumRows() == 0) {
+ foreach ($RecipientUserIDs as $UserID) {
+ $CountNewMessages = $UserID == $Session->UserID ? 0 : 1;
+ $this->SQL->Insert('UserConversation', array(
+ 'UserID' => $UserID,
+ 'ConversationID' => $ConversationID,
+ 'LastMessageID' => $MessageID,
+ 'CountMessages' => '1',
+ 'CountNewMessages' => $CountNewMessages
+ ));
+ }
+
+ // And update the CountUnreadConversations count on each user related to the discussion.
+ $this->SQL
+ ->Update('User')
+ ->Set('CountUnreadConversations', 'CountUnreadConversations + 1', FALSE)
+ ->WhereIn('UserID', $RecipientUserIDs)
+ ->Where('UserID <>', $Session->UserID)
+ ->Put();
+ }
+
+ // NOTIFY ALL NEWLY ADDED USERS THAT THEY WERE ADDED TO THE CONVERSATION
+ foreach ($RecipientUserIDs as $UserID) {
+ if ($UserID != $Session->UserID)
+ AddActivity(
+ $Session->UserID,
+ 'ConversationMessage',
+ '',
+ $UserID,
+ '/messages/'.$ConversationID
+ );
+ }
+
+ } else {
+ // Make sure that all of the validation results from both validations are present for view by the form
+ foreach ($MessageModel->ValidationResults() as $FieldName => $Results) {
+ foreach ($Results as $Result) {
+ $this->Validation->AddValidationResult($FieldName, $Result);
+ }
+ }
+ }
+
+// TODO: NOTIFY ALL USERS (BESIDES THE PERSON WHO ADDED THE MESSAGE) OF THE NEW MESSAGE.
+
+ return $ConversationID;
+ }
+
+ /// <summary>
+ /// Clear a conversation for a specific user id.
+ /// </summary>
+ public function Clear($ConversationID, $ClearingUserID) {
+ $this->SQL->Update('UserConversation')
+ ->Set('CountNewMessages', 0)
+ ->Set('CountMessages', 0)
+ ->Set('DateLastViewed', Format::ToDateTime())
+ ->Set('DateCleared', Format::ToDateTime())
+ ->Where('ConversationID', $ConversationID)
+ ->Where('UserID', $ClearingUserID)
+ ->Put();
+ }
+
+ /// <summary>
+ /// Update a conversation as read for a specific user id.
+ /// </summary>
+ public function MarkRead($ConversationID, $ReadingUserID) {
+ $this->SQL->Update('UserConversation')
+ ->Set('CountNewMessages', 0)
+ ->Set('DateLastViewed', Format::ToDateTime())
+ ->Where('ConversationID', $ConversationID)
+ ->Where('UserID', $ReadingUserID)
+ ->Put();
+
+ // Also update the unread conversation count for this user
+ $UnreadData = $this->SQL
+ ->Select('CountNewMessages', 'count', 'CountUnreadConversations')
+ ->From('UserConversation c')
+ ->Where('CountNewMessages >', 0)
+ ->Where('UserID', $ReadingUserID)
+ ->GroupBy('UserID')
+ ->Get();
+
+ $this->SQL
+ ->Update('User')
+ ->Set('CountUnreadConversations', $UnreadData->NumRows() > 0 ? $UnreadData->FirstRow()->CountUnreadConversations : 0)
+ ->Where('UserID', $ReadingUserID)
+ ->Put();
+ }
+
+ /// <summary>
+ /// Bookmark (or unbookmark) a conversation for a specific user id.
+ /// </summary>
+ public function Bookmark($ConversationID, $UserID) {
+ $Bookmark = FALSE;
+ $Discussion = $this->GetID($ConversationID, $UserID);
+ if (is_object($Discussion)) {
+ $Bookmark = $Discussion->Bookmark == '0' ? '1' : '0';
+ $this->SQL->Update('UserConversation')
+ ->Set('Bookmark', $Bookmark)
+ ->Where('ConversationID', $ConversationID)
+ ->Where('UserID', $UserID)
+ ->Put();
+ $Bookmark == '1' ? TRUE : FALSE;
+ }
+ return $Bookmark;
+ }
+
+ public function AddUserToConversation($ConversationID, $UserID) {
+ if (!is_array($UserID))
+ $UserID = array($UserID);
+
+ // First define the current users in the conversation
+ $OldContributorData = $this->GetRecipients($ConversationID);
+ $OldContributorUserIDs = ConsolidateArrayValuesByKey($OldContributorData->ResultArray(), 'UserID');
+ $AddedUserIDs = array();
+
+ // Get some information about this conversation
+ $ConversationData = $this->SQL
+ ->Select('MessageID', 'max', 'LastMessageID')
+ ->Select('MessageID', 'count', 'CountMessages')
+ ->From('ConversationMessage')
+ ->Where('ConversationID', $ConversationID)
+ ->Get()
+ ->FirstRow();
+
+ // Add the user(s) if they are not already in the conversation
+ foreach ($UserID as $NewUserID) {
+ if (!in_array($NewUserID, $OldContributorUserIDs)) {
+ $AddedUserIDs[] = $NewUserID;
+ $this->SQL->Insert('UserConversation', array(
+ 'UserID' => $NewUserID,
+ 'ConversationID' => $ConversationID,
+ 'LastMessageID' => $ConversationData->LastMessageID,
+ 'CountMessages' => $ConversationData->CountMessages,
+ 'CountNewMessages' => $ConversationData->CountMessages
+ ));
+ }
+ }
+ if (count($AddedUserIDs) > 0) {
+ $Session = Gdn::Session();
+
+ // Update the Contributors field on the conversation
+ $Contributors = array_unique(array_merge($AddedUserIDs, $OldContributorUserIDs));
+ sort($Contributors);
+ $this->SQL
+ ->Update('Conversation')
+ ->Set('Contributors', Format::Serialize($Contributors))
+ ->Where('ConversationID', $ConversationID)
+ ->Put();
+
+ // NOTIFY ALL NEWLY ADDED USERS THAT THEY WERE ADDED TO THE CONVERSATION
+ foreach ($AddedUserIDs as $AddedUserID) {
+ AddActivity(
+ $Session->UserID,
+ 'AddedToConversation',
+ '',
+ $AddedUserID,
+ '/messages/'.$ConversationID
+ );
+ }
+
+ // Update the unread conversation count for each affected user
+ $this->SQL
+ ->Update('User')
+ ->Set('CountUnreadConversations', 'CountUnreadConversations + 1', FALSE)
+ ->WhereIn('UserID', $AddedUserIDs)
+ ->Put();
+ }
+ }
+}
63 applications/conversations/modules/class.addpeoplemodule.php
@@ -0,0 +1,63 @@
+<?php if (!defined('APPLICATION')) exit();
+/*
+Copyright 2008, 2009 Mark O'Sullivan
+This file is part of Garden.
+Garden is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+Garden is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+You should have received a copy of the GNU General Public License along with Garden. If not, see <http://www.gnu.org/licenses/>.
+Contact Mark O'Sullivan at mark [at] lussumo [dot] com
+*/
+
+/// <namespace>
+/// Lussumo.Garden.Modules
+/// </namespace>
+
+/// <summary>
+/// Renders a form that allows people to be added to conversations.
+/// </summary>
+class AddPeopleModule extends Module {
+
+ public $Conversation;
+ public $Form;
+
+ public function __construct(&$Sender = '') {
+ $Session = Gdn::Session();
+ if (property_exists($Sender, 'Conversation'))
+ $this->Conversation = $Sender->Conversation;
+
+ $this->Form = new Form('AddPeople');
+ // $this->Form->Action = $Sender->SelfUrl;
+ // If the form was posted back, check for people to add to the conversation
+ if ($this->Form->AuthenticatedPostBack()) {
+ $NewRecipientUserIDs = array();
+ $NewRecipients = explode(',', $this->Form->GetFormValue('AddPeople', ''));
+ $UserModel = Gdn::Factory("UserModel");
+ foreach ($NewRecipients as $Name) {
+ if (trim($Name) != '') {
+ $User = $UserModel->Get(trim($Name));
+ if (is_object($User))
+ $NewRecipientUserIDs[] = $User->UserID;
+ }
+ }
+ $Sender->ConversationModel->AddUserToConversation($this->Conversation->ConversationID, $NewRecipientUserIDs);
+ // if ($Sender->DeliveryType() == DELIVERY_TYPE_ALL)
+ // Redirect('/messages/'.$this->Conversation->ConversationID);
+
+ $Sender->StatusMessage = Gdn::Translate('Your changes were saved.');
+ $Sender->RedirectUrl = Url('/messages/'.$this->Conversation->ConversationID);
+ }
+ $this->_ApplicationFolder = $Sender->Application;
+ $this->_ThemeFolder = $Sender->Theme;
+ }
+
+ public function AssetTarget() {
+ return 'Panel';
+ }
+
+ public function ToString() {
+ if (is_object($this->Conversation) && $this->Conversation->ConversationID > 0)
+ return parent::ToString();
+
+ return '';
+ }
+}
30 applications/conversations/modules/class.clearhistorymodule.php
@@ -0,0 +1,30 @@
+<?php if (!defined('APPLICATION')) exit();
+/*
+Copyright 2008, 2009 Mark O'Sullivan
+This file is part of Garden.
+Garden is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+Garden is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+You should have received a copy of the GNU General Public License along with Garden. If not, see <http://www.gnu.org/licenses/>.
+Contact Mark O'Sullivan at mark [at] lussumo [dot] com
+*/
+
+/// <namespace>
+/// Lussumo.Garden.Modules
+/// </namespace>
+
+/// <summary>
+/// Renders the "Clear Conversation History" button.
+/// </summary>
+class ClearHistoryModule extends Module {
+
+ protected $ConversationID;
+
+ public function ConversationID($ConversationID) {
+ $this->ConversationID = $ConversationID;
+ }
+
+ public function AssetTarget() {
+ return 'Panel';
+ }
+
+}
42 applications/conversations/modules/class.inthisconversationmodule.php
@@ -0,0 +1,42 @@
+<?php if (!defined('APPLICATION')) exit();
+/*
+Copyright 2008, 2009 Mark O'Sullivan
+This file is part of Garden.
+Garden is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+Garden is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+You should have received a copy of the GNU General Public License along with Garden. If not, see <http://www.gnu.org/licenses/>.
+Contact Mark O'Sullivan at mark [at] lussumo [dot] com
+*/
+
+/// <namespace>
+/// Lussumo.Garden.Modules
+/// </namespace>
+
+/// <summary>
+/// Renders a list of people in the specified conversation.
+/// </summary>
+class InThisConversationModule extends Module {
+
+ protected $_UserData;
+
+ public function __construct(&$Sender = '') {
+ $this->_UserData = FALSE;
+ parent::__construct($Sender);
+ }
+
+ public function SetData($Data) {
+ $this->_UserData = $Data;
+ }
+
+ public function AssetTarget() {
+ return 'Panel';
+ }
+
+ public function ToString() {
+ if ($this->_UserData !== FALSE && $this->_UserData->NumRows() > 0)
+ return parent::ToString();
+
+ return '';
+ }
+
+}
24 applications/conversations/modules/class.newconversationmodule.php
@@ -0,0 +1,24 @@
+<?php if (!defined('APPLICATION')) exit();
+/*
+Copyright 2008, 2009 Mark O'Sullivan
+This file is part of Garden.
+Garden is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+Garden is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+You should have received a copy of the GNU General Public License along with Garden. If not, see <http://www.gnu.org/licenses/>.
+Contact Mark O'Sullivan at mark [at] lussumo [dot] com
+*/
+
+/// <namespace>
+/// Lussumo.Garden.Modules
+/// </namespace>
+
+/// <summary>
+/// Renders the "New Conversation" button.
+/// </summary>
+class NewConversationModule extends Module {
+
+ public function AssetTarget() {
+ return 'Panel';
+ }
+
+}
15 applications/conversations/settings/about.php
@@ -0,0 +1,15 @@
+<?php
+/// <summary>
+/// An associative array of information about this application.
+/// </summary>
+$ApplicationInfo['Conversations'] = array(
+ 'Description' => "Conversations allows users to take part in private conversations.",
+ 'Version' => '1.0',
+ // 'RequiredApplications' => array('Garden' => '1.0'),
+ 'SetupController' => 'setup',
+ 'Url' => 'http://weconversate.com',
+ 'Author' => "Mark O'Sullivan",
+ 'AuthorEmail' => 'mark@lussumo.com',
+ 'AuthorUrl' => 'http://lussumo.com',
+ 'License' => 'MIT License (http://lussumo.com/license)'
+);
8 applications/conversations/settings/configuration.php
@@ -0,0 +1,8 @@
+<?php if (!defined('APPLICATION')) exit();
+// DO NOT EDIT THIS FILE
+// All of the settings defined here can be overridden in the /conf/configuration.php file.
+
+$Configuration['Conversations']['Installed'] = '0';
+$Configuration['Conversations']['Conversations']['PerPage'] = '50';
+$Configuration['Conversations']['Messages']['PerPage'] = '50';
+$Configuration['Conversations']['Message']['MaxLength'] = '2000';
34 applications/conversations/settings/hooks.php
@@ -0,0 +1,34 @@
+<?php if (!defined('APPLICATION')) exit();
+/*
+Copyright 2008, 2009 Mark O'Sullivan
+This file is part of Garden.
+Garden is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+Garden is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+You should have received a copy of the GNU General Public License along with Garden. If not, see <http://www.gnu.org/licenses/>.
+Contact Mark O'Sullivan at mark [at] lussumo [dot] com
+*/
+class ConversationsHooks implements IPlugin {
+
+ public function Gdn_UserModel_SessionQuery_Handler(&$Sender) {
+ // Add some extra fields to the session query
+ $Sender->SQL->Select('u.CountUnreadConversations');
+ }
+
+ public function Base_Render_Before(&$Sender) {
+ // Add the menu options for conversations
+ $Session = Gdn::Session();
+ if ($Sender->Menu && $Session->IsValid()) {
+ $Inbox = Gdn::Translate('Inbox');
+ $CountUnreadConversations = $Session->User->CountUnreadConversations;
+ if (is_numeric($CountUnreadConversations) && $CountUnreadConversations > 0)
+ $Inbox .= '<span>'.$CountUnreadConversations.'</span>';
+
+ $Sender->Menu->AddLink('Conversations', $Inbox, '/messages/all', FALSE);
+ $Sender->Menu->AddLink('Conversations', 'New Conversation', '/messages/add', FALSE);
+ }
+ }
+
+ public function Setup() {
+ return TRUE;
+ }
+}
80 applications/conversations/settings/structure.php
@@ -0,0 +1,80 @@
+<?php if (!defined('APPLICATION')) exit();
+/*
+Copyright 2008, 2009 Mark O'Sullivan
+This file is part of Garden.
+Garden is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+Garden is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+You should have received a copy of the GNU General Public License along with Garden. If not, see <http://www.gnu.org/licenses/>.
+Contact Mark O'Sullivan at mark [at] lussumo [dot] com
+*/
+
+if (!isset($Drop))
+ $Drop = FALSE;
+
+if (!isset($Explicit))
+ $Explicit = TRUE;
+
+// Contains all conversations. A conversation takes place between X number of
+// ppl. This table keeps track of the unique id of the conversation, the person
+// who started the conversation (and when), and the last person to contribute to
+// the conversation (and when).
+// Column($Name, $Type, $Length = '', $Null = FALSE, $Default = NULL, $KeyType = FALSE, $AutoIncrement = FALSE)
+$Construct->Table('Conversation')
+ ->Column('ConversationID', 'int', 11, FALSE, NULL, 'primary', TRUE)
+ ->Column('Contributors', 'varchar', 255)
+ ->Column('FirstMessageID', 'int', 11, TRUE, NULL, 'key')
+ ->Column('InsertUserID', 'int', 10, FALSE, NULL, 'key')
+ ->Column('DateInserted', 'datetime')
+ ->Column('UpdateUserID', 'int', 10, FALSE, NULL, 'key')
+ ->Column('DateUpdated', 'datetime')
+ ->Set($Explicit, $Drop);
+
+// Contains the user/conversation relationship. Keeps track of all users who are
+// taking part in the conversation. It also keeps DateCleared, which is a
+// per-user date relating to when each users last cleared the conversation
+// history, and
+$Construct->Table('UserConversation')
+ ->Column('UserID', 'int', 11, FALSE, NULL, 'primary')
+ ->Column('ConversationID', 'int', 11, FALSE, NULL, 'primary')
+ ->Column('CountNewMessages', 'int', 11, FALSE, '0') // # of unread messages
+ ->Column('CountMessages', 'int', 11, FALSE, '0') // # of uncleared messages
+ ->Column('LastMessageID', 'int', 11, TRUE, NULL, 'key') // The last message posted by a user other than this one, unless this user is the only person who has added a message
+ ->Column('DateLastViewed', 'datetime', '', TRUE)
+ ->Column('DateCleared', 'datetime', '', TRUE)
+ ->Column('Bookmarked', array('1', '0'), '', FALSE, '0')
+ ->Set($Explicit, $Drop);
+
+// Contains messages for each conversation, as well as who inserted the message
+// and when it was inserted. Users cannot edit or delete their messages once
+// they have been sent.
+$Construct->Table('ConversationMessage')
+ ->Column('MessageID', 'int', 11, FALSE, NULL, 'primary', TRUE)
+ ->Column('ConversationID', 'int', 11)
+ ->Column('Body', 'text')
+ ->Column('Format', 'varchar', 20, TRUE)
+ ->Column('InsertUserID', 'int', 10, FALSE, NULL, 'key')
+ ->Column('DateInserted', 'datetime')
+ ->Set($Explicit, $Drop);
+
+// Add extra columns to user table for tracking discussions, comments & replies
+$Construct->Table('User')
+ ->Column('CountUnreadConversations', 'int', 11, FALSE, '0')
+ ->Set();
+
+// Insert some activity types
+/// %1 = ActivityName
+/// %2 = ActivityName Possessive
+/// %3 = RegardingName
+/// %4 = RegardingName Possessive
+/// %5 = Link to RegardingName's Wall
+/// %6 = his/her
+/// %7 = he/she
+/// %8 = RouteCode & Route (will be changed to <a href="route">routecode</a>)
+
+// X sent you a message
+$SQL = $Database->SQL();
+if ($SQL->GetWhere('ActivityType', array('Name' => 'ConversationMessage'))->NumRows() == 0)
+ $SQL->Insert('ActivityType', array('AllowComments' => '0', 'Name' => 'ConversationMessage', 'FullHeadline' => '%1$s sent you a %8$s.', 'ProfileHeadline' => '%1$s sent you a %8$s.', 'RouteCode' => 'message', 'Notify' => '1', 'Public' => '0'));
+
+if ($SQL->GetWhere('ActivityType', array('Name' => 'AddedToConversation'))->NumRows() == 0)
+ $SQL->Insert('ActivityType', array('AllowComments' => '0', 'Name' => 'AddedToConversation', 'FullHeadline' => '%1$s added you to a %8$s.', 'ProfileHeadline' => '%1$s added you to a %8$s.', 'RouteCode' => 'conversation', 'Notify' => '1', 'Public' => '0'));
13 applications/conversations/views/messages/add.php
@@ -0,0 +1,13 @@
+<?php if (!defined('APPLICATION')) exit(); ?>
+
+<div id="ConversationForm">
+ <?php
+ echo $this->Form->Open();
+ echo $this->Form->Label('Start a New Conversation', 'Body', array('class' => 'Heading'));
+ echo $this->Form->Errors();
+ echo $this->Form->Label('To', 'To');
+ echo $this->Form->TextBox('To', array('MultiLine' => TRUE, 'class' => 'MultiComplete'));
+ echo $this->Form->TextBox('Body', array('MultiLine' => TRUE));
+ echo $this->Form->Close('Send');
+ ?>
+</div>
14 applications/conversations/views/messages/addmessage.php
@@ -0,0 +1,14 @@
+<?php if (!defined('APPLICATION')) exit();
+echo