]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch 'testing' into privategroup
authorEvan Prodromou <evan@status.net>
Thu, 3 Feb 2011 17:56:55 +0000 (12:56 -0500)
committerEvan Prodromou <evan@status.net>
Thu, 3 Feb 2011 17:56:55 +0000 (12:56 -0500)
Conflicts:
lib/groupeditform.php

EVENTS.txt
actions/editgroup.php
classes/User_group.php
lib/groupeditform.php
plugins/PrivateGroup/Group_message.php [new file with mode: 0644]
plugins/PrivateGroup/Group_privacy_settings.php [new file with mode: 0644]
plugins/PrivateGroup/PrivateGroupPlugin.php [new file with mode: 0644]
plugins/PrivateGroup/groupinbox.php [new file with mode: 0644]

index 6719ba737a19b4a9a4449270ab8d635e87ee8626..d26c576e19a3e42db2d3a354f0d4eb9c6a69814e 100644 (file)
@@ -1057,3 +1057,15 @@ StartCloseNoticeListItemElement: Before the closing </li> of a notice list eleme
 
 EndCloseNoticeListItemElement: After the closing </li> of a notice list element
 - $nli: The notice list item being shown
+
+StartGroupEditFormData: Beginning the group edit form entries
+- $form: The form widget being shown
+
+EndGroupEditFormData: Ending the group edit form entries
+- $form: The form widget being shown
+
+StartGroupSave: After initializing but before saving a group
+- &$group: group about to be saved
+
+EndGroupSave: After saving a group, aliases, and first member
+- $group: group that was saved
index ab4dbb28360e30278d8515a06a3c61b7fef85f09..0e041700511360d29b493541dee9b3d521978367 100644 (file)
@@ -177,115 +177,120 @@ class EditgroupAction extends GroupDesignAction
             return;
         }
 
-        $nickname    = Nickname::normalize($this->trimmed('nickname'));
-        $fullname    = $this->trimmed('fullname');
-        $homepage    = $this->trimmed('homepage');
-        $description = $this->trimmed('description');
-        $location    = $this->trimmed('location');
-        $aliasstring = $this->trimmed('aliases');
-
-        if ($this->nicknameExists($nickname)) {
-            // TRANS: Group edit form validation error.
-            $this->showForm(_('Nickname already in use. Try another one.'));
-            return;
-        } else if (!User_group::allowedNickname($nickname)) {
-            // TRANS: Group edit form validation error.
-            $this->showForm(_('Not a valid nickname.'));
-            return;
-        } else if (!is_null($homepage) && (strlen($homepage) > 0) &&
-                   !Validate::uri($homepage,
-                                  array('allowed_schemes' =>
-                                        array('http', 'https')))) {
-            // TRANS: Group edit form validation error.
-            $this->showForm(_('Homepage is not a valid URL.'));
-            return;
-        } else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
-            // TRANS: Group edit form validation error.
-            $this->showForm(_('Full name is too long (maximum 255 characters).'));
-            return;
-        } else if (User_group::descriptionTooLong($description)) {
-            $this->showForm(sprintf(
-                // TRANS: Group edit form validation error.
-                _m('Description is too long (maximum %d character).',
-                  'Description is too long (maximum %d characters).',
-                  User_group::maxDescription()),
-                                    User_group::maxDescription()));
-            return;
-        } else if (!is_null($location) && mb_strlen($location) > 255) {
-            // TRANS: Group edit form validation error.
-            $this->showForm(_('Location is too long (maximum 255 characters).'));
-            return;
-        }
+        if (Event::handle('StartGroupSaveForm', array($this))) {
 
-        if (!empty($aliasstring)) {
-            $aliases = array_map('common_canonical_nickname', array_unique(preg_split('/[\s,]+/', $aliasstring)));
-        } else {
-            $aliases = array();
-        }
+            $nickname    = Nickname::normalize($this->trimmed('nickname'));
+            $fullname    = $this->trimmed('fullname');
+            $homepage    = $this->trimmed('homepage');
+            $description = $this->trimmed('description');
+            $location    = $this->trimmed('location');
+            $aliasstring = $this->trimmed('aliases');
 
-        if (count($aliases) > common_config('group', 'maxaliases')) {
-            // TRANS: Group edit form validation error.
-            // TRANS: %d is the maximum number of allowed aliases.
-            $this->showForm(sprintf(_m('Too many aliases! Maximum %d allowed.',
-                                       'Too many aliases! Maximum %d allowed.',
-                                       common_config('group', 'maxaliases')),
-                                    common_config('group', 'maxaliases')));
-            return;
-        }
-
-        foreach ($aliases as $alias) {
-            if (!Nickname::isValid($alias)) {
+            if ($this->nicknameExists($nickname)) {
                 // TRANS: Group edit form validation error.
-                $this->showForm(sprintf(_('Invalid alias: "%s"'), $alias));
+                $this->showForm(_('Nickname already in use. Try another one.'));
                 return;
-            }
-            if ($this->nicknameExists($alias)) {
+            } else if (!User_group::allowedNickname($nickname)) {
+                // TRANS: Group edit form validation error.
+                $this->showForm(_('Not a valid nickname.'));
+                return;
+            } else if (!is_null($homepage) && (strlen($homepage) > 0) &&
+                       !Validate::uri($homepage,
+                                      array('allowed_schemes' =>
+                                            array('http', 'https')))) {
+                // TRANS: Group edit form validation error.
+                $this->showForm(_('Homepage is not a valid URL.'));
+                return;
+            } else if (!is_null($fullname) && mb_strlen($fullname) > 255) {
                 // TRANS: Group edit form validation error.
-                $this->showForm(sprintf(_('Alias "%s" already in use. Try another one.'),
-                                        $alias));
+                $this->showForm(_('Full name is too long (maximum 255 characters).'));
+                return;
+            } else if (User_group::descriptionTooLong($description)) {
+                $this->showForm(sprintf(
+                                    // TRANS: Group edit form validation error.
+                                    _m('Description is too long (maximum %d character).',
+                                       'Description is too long (maximum %d characters).',
+                                       User_group::maxDescription()),
+                                    User_group::maxDescription()));
+                return;
+            } else if (!is_null($location) && mb_strlen($location) > 255) {
+                // TRANS: Group edit form validation error.
+                $this->showForm(_('Location is too long (maximum 255 characters).'));
                 return;
             }
-            // XXX assumes alphanum nicknames
-            if (strcmp($alias, $nickname) == 0) {
+
+            if (!empty($aliasstring)) {
+                $aliases = array_map('common_canonical_nickname', array_unique(preg_split('/[\s,]+/', $aliasstring)));
+            } else {
+                $aliases = array();
+            }
+
+            if (count($aliases) > common_config('group', 'maxaliases')) {
                 // TRANS: Group edit form validation error.
-                $this->showForm(_('Alias can\'t be the same as nickname.'));
+                // TRANS: %d is the maximum number of allowed aliases.
+                $this->showForm(sprintf(_m('Too many aliases! Maximum %d allowed.',
+                                           'Too many aliases! Maximum %d allowed.',
+                                           common_config('group', 'maxaliases')),
+                                        common_config('group', 'maxaliases')));
                 return;
             }
-        }
 
-        $this->group->query('BEGIN');
+            foreach ($aliases as $alias) {
+                if (!Nickname::isValid($alias)) {
+                    // TRANS: Group edit form validation error.
+                    $this->showForm(sprintf(_('Invalid alias: "%s"'), $alias));
+                    return;
+                }
+                if ($this->nicknameExists($alias)) {
+                    // TRANS: Group edit form validation error.
+                    $this->showForm(sprintf(_('Alias "%s" already in use. Try another one.'),
+                                            $alias));
+                    return;
+                }
+                // XXX assumes alphanum nicknames
+                if (strcmp($alias, $nickname) == 0) {
+                    // TRANS: Group edit form validation error.
+                    $this->showForm(_('Alias can\'t be the same as nickname.'));
+                    return;
+                }
+            }
+
+            $this->group->query('BEGIN');
 
-        $orig = clone($this->group);
+            $orig = clone($this->group);
 
-        $this->group->nickname    = $nickname;
-        $this->group->fullname    = $fullname;
-        $this->group->homepage    = $homepage;
-        $this->group->description = $description;
-        $this->group->location    = $location;
-        $this->group->mainpage    = common_local_url('showgroup', array('nickname' => $nickname));
+            $this->group->nickname    = $nickname;
+            $this->group->fullname    = $fullname;
+            $this->group->homepage    = $homepage;
+            $this->group->description = $description;
+            $this->group->location    = $location;
+            $this->group->mainpage    = common_local_url('showgroup', array('nickname' => $nickname));
 
-        $result = $this->group->update($orig);
+            $result = $this->group->update($orig);
 
-        if (!$result) {
-            common_log_db_error($this->group, 'UPDATE', __FILE__);
-            // TRANS: Server error displayed when editing a group fails.
-            $this->serverError(_('Could not update group.'));
-        }
+            if (!$result) {
+                common_log_db_error($this->group, 'UPDATE', __FILE__);
+                // TRANS: Server error displayed when editing a group fails.
+                $this->serverError(_('Could not update group.'));
+            }
 
-        $result = $this->group->setAliases($aliases);
+            $result = $this->group->setAliases($aliases);
 
-        if (!$result) {
-            // TRANS: Server error displayed when group aliases could not be added.
-            $this->serverError(_('Could not create aliases.'));
-        }
+            if (!$result) {
+                // TRANS: Server error displayed when group aliases could not be added.
+                $this->serverError(_('Could not create aliases.'));
+            }
 
-        if ($nickname != $orig->nickname) {
-            common_log(LOG_INFO, "Saving local group info.");
-            $local = Local_group::staticGet('group_id', $this->group->id);
-            $local->setNickname($nickname);
-        }
+            if ($nickname != $orig->nickname) {
+                common_log(LOG_INFO, "Saving local group info.");
+                $local = Local_group::staticGet('group_id', $this->group->id);
+                $local->setNickname($nickname);
+            }
 
-        $this->group->query('COMMIT');
+            $this->group->query('COMMIT');
+
+            Event::handle('EndGroupSaveForm', array($this));
+        }
 
         if ($this->group->nickname != $orig->nickname) {
             common_redirect(common_local_url('editgroup',
index d402ed47733e3b631bca94c153bac8a28ed1dbbf..5a9991fe9e3fd936221b64ef69b5d7f1362e2994 100644 (file)
@@ -512,64 +512,70 @@ class User_group extends Memcached_DataObject
         $group->mainpage    = $mainpage;
         $group->created     = common_sql_now();
 
-        $result = $group->insert();
+        if (Event::handle('StartGroupSave', array(&$group))) {
 
-        if (!$result) {
-            common_log_db_error($group, 'INSERT', __FILE__);
-            // TRANS: Server exception thrown when creating a group failed.
-            throw new ServerException(_('Could not create group.'));
-        }
+            $result = $group->insert();
 
-        if (!isset($uri) || empty($uri)) {
-            $orig = clone($group);
-            $group->uri = common_local_url('groupbyid', array('id' => $group->id));
-            $result = $group->update($orig);
             if (!$result) {
-                common_log_db_error($group, 'UPDATE', __FILE__);
-                // TRANS: Server exception thrown when updating a group URI failed.
-                throw new ServerException(_('Could not set group URI.'));
+                common_log_db_error($group, 'INSERT', __FILE__);
+                // TRANS: Server exception thrown when creating a group failed.
+                throw new ServerException(_('Could not create group.'));
             }
-        }
 
-        $result = $group->setAliases($aliases);
+            if (!isset($uri) || empty($uri)) {
+                $orig = clone($group);
+                $group->uri = common_local_url('groupbyid', array('id' => $group->id));
+                $result = $group->update($orig);
+                if (!$result) {
+                    common_log_db_error($group, 'UPDATE', __FILE__);
+                    // TRANS: Server exception thrown when updating a group URI failed.
+                    throw new ServerException(_('Could not set group URI.'));
+                }
+            }
 
-        if (!$result) {
-            // TRANS: Server exception thrown when creating group aliases failed.
-            throw new ServerException(_('Could not create aliases.'));
-        }
+            $result = $group->setAliases($aliases);
 
-        $member = new Group_member();
+            if (!$result) {
+                // TRANS: Server exception thrown when creating group aliases failed.
+                throw new ServerException(_('Could not create aliases.'));
+            }
 
-        $member->group_id   = $group->id;
-        $member->profile_id = $userid;
-        $member->is_admin   = 1;
-        $member->created    = $group->created;
+            $member = new Group_member();
 
-        $result = $member->insert();
+            $member->group_id   = $group->id;
+            $member->profile_id = $userid;
+            $member->is_admin   = 1;
+            $member->created    = $group->created;
 
-        if (!$result) {
-            common_log_db_error($member, 'INSERT', __FILE__);
-            // TRANS: Server exception thrown when setting group membership failed.
-            throw new ServerException(_('Could not set group membership.'));
-        }
+            $result = $member->insert();
 
-        if ($local) {
-            $local_group = new Local_group();
+            if (!$result) {
+                common_log_db_error($member, 'INSERT', __FILE__);
+                // TRANS: Server exception thrown when setting group membership failed.
+                throw new ServerException(_('Could not set group membership.'));
+            }
 
-            $local_group->group_id = $group->id;
-            $local_group->nickname = $nickname;
-            $local_group->created  = common_sql_now();
+            if ($local) {
+                $local_group = new Local_group();
 
-            $result = $local_group->insert();
+                $local_group->group_id = $group->id;
+                $local_group->nickname = $nickname;
+                $local_group->created  = common_sql_now();
 
-            if (!$result) {
-                common_log_db_error($local_group, 'INSERT', __FILE__);
-                // TRANS: Server exception thrown when saving local group information failed.
-                throw new ServerException(_('Could not save local group info.'));
+                $result = $local_group->insert();
+
+                if (!$result) {
+                    common_log_db_error($local_group, 'INSERT', __FILE__);
+                    // TRANS: Server exception thrown when saving local group information failed.
+                    throw new ServerException(_('Could not save local group info.'));
+                }
             }
+
+            $group->query('COMMIT');
+
+            Event::handle('EndGroupSave', array($group));
         }
 
-        $group->query('COMMIT');
         return $group;
     }
 
index 8a111b46429c4d5cbbefb1daed71a738ee21e70f..3a2cf6bf4ab244eb28962a30566916a625768524 100644 (file)
@@ -139,51 +139,54 @@ class GroupEditForm extends Form
         }
 
         $this->out->elementStart('ul', 'form_data');
-        $this->out->elementStart('li');
-        $this->out->hidden('groupid', $id);
-        $this->out->input('nickname', _('Nickname'),
-                          ($this->out->arg('nickname')) ? $this->out->arg('nickname') : $nickname,
-                          _('1-64 lowercase letters or numbers, no punctuation or spaces.'));
-        $this->out->elementEnd('li');
-        $this->out->elementStart('li');
-        $this->out->input('fullname', _('Full name'),
-                          ($this->out->arg('fullname')) ? $this->out->arg('fullname') : $fullname);
-        $this->out->elementEnd('li');
-        $this->out->elementStart('li');
-        $this->out->input('homepage', _('Homepage'),
-                          ($this->out->arg('homepage')) ? $this->out->arg('homepage') : $homepage,
-                          _('URL of the homepage or blog of the group or topic.'));
-        $this->out->elementEnd('li');
-        $this->out->elementStart('li');
-        $desclimit = User_group::maxDescription();
-        if ($desclimit == 0) {
-            $descinstr = _('Describe the group or topic');
-        } else {
-            $descinstr = sprintf(_m('Describe the group or topic in %d character or less.',
-                                    'Describe the group or topic in %d characters or less.',
-                                    $desclimit),
-                                 $desclimit);
-        }
-        $this->out->textarea('description', _('Description'),
-                             ($this->out->arg('description')) ? $this->out->arg('description') : $description,
-                             $descinstr);
-        $this->out->elementEnd('li');
-        $this->out->elementStart('li');
-        $this->out->input('location', _('Location'),
-                          ($this->out->arg('location')) ? $this->out->arg('location') : $location,
-                          _('Location for the group, if any, like "City, State (or Region), Country".'));
-        $this->out->elementEnd('li');
-        if (common_config('group', 'maxaliases') > 0) {
-            $aliases = (empty($this->group)) ? array() : $this->group->getAliases();
+        if (Event::handle('StartGroupEditFormData', array($this))) {
+            $this->out->elementStart('li');
+            $this->out->hidden('groupid', $id);
+            $this->out->input('nickname', _('Nickname'),
+                              ($this->out->arg('nickname')) ? $this->out->arg('nickname') : $nickname,
+                              _('1-64 lowercase letters or numbers, no punctuation or spaces'));
+            $this->out->elementEnd('li');
+            $this->out->elementStart('li');
+            $this->out->input('fullname', _('Full name'),
+                              ($this->out->arg('fullname')) ? $this->out->arg('fullname') : $fullname);
+            $this->out->elementEnd('li');
+            $this->out->elementStart('li');
+            $this->out->input('homepage', _('Homepage'),
+                              ($this->out->arg('homepage')) ? $this->out->arg('homepage') : $homepage,
+                              _('URL of the homepage or blog of the group or topic.'));
+            $this->out->elementEnd('li');
+            $this->out->elementStart('li');
+            $desclimit = User_group::maxDescription();
+            if ($desclimit == 0) {
+                $descinstr = _('Describe the group or topic');
+            } else {
+                $descinstr = sprintf(_m('Describe the group or topic in %d character or less',
+                                        'Describe the group or topic in %d characters or less',
+                                        $desclimit),
+                                     $desclimit);
+            }
+            $this->out->textarea('description', _('Description'),
+                                 ($this->out->arg('description')) ? $this->out->arg('description') : $description,
+                                 $descinstr);
+            $this->out->elementEnd('li');
             $this->out->elementStart('li');
-            $this->out->input('aliases', _('Aliases'),
-                              ($this->out->arg('aliases')) ? $this->out->arg('aliases') :
-                              (!empty($aliases)) ? implode(' ', $aliases) : '',
-                              sprintf(_m('Extra nicknames for the group, separated with commas or spaces. Maximum %d alias allowed.',
-                                         'Extra nicknames for the group, separated with commas or spaces. Maximum %d aliases allowed.',
-                                         common_config('group', 'maxaliases')),
-                                      common_config('group', 'maxaliases')));;
+            $this->out->input('location', _('Location'),
+                              ($this->out->arg('location')) ? $this->out->arg('location') : $location,
+                              _('Location for the group, if any, like "City, State (or Region), Country".'));
             $this->out->elementEnd('li');
+            if (common_config('group', 'maxaliases') > 0) {
+                $aliases = (empty($this->group)) ? array() : $this->group->getAliases();
+                $this->out->elementStart('li');
+                $this->out->input('aliases', _('Aliases'),
+                                  ($this->out->arg('aliases')) ? $this->out->arg('aliases') :
+                                  (!empty($aliases)) ? implode(' ', $aliases) : '',
+                                  sprintf(_m('Extra nicknames for the group, separated with commas or spaces. Maximum %d alias allowed.',
+                                             'Extra nicknames for the group, separated with commas or spaces. Maximum %d aliases allowed.',
+                                             common_config('group', 'maxaliases')),
+                                          common_config('group', 'maxaliases')));;
+                $this->out->elementEnd('li');
+            }
+            Event::handle('EndGroupEditFormData', array($this));
         }
         $this->out->elementEnd('ul');
     }
diff --git a/plugins/PrivateGroup/Group_message.php b/plugins/PrivateGroup/Group_message.php
new file mode 100644 (file)
index 0000000..404f663
--- /dev/null
@@ -0,0 +1,184 @@
+<?php
+/**
+ * Data class for group inbox info
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+require_once INSTALLDIR . '/classes/Memcached_DataObject.php';
+
+/**
+ * Data class for counting greetings
+ *
+ * We use the DB_DataObject framework for data classes in StatusNet. Each
+ * table maps to a particular data class, making it easier to manipulate
+ * data.
+ *
+ * Data classes should extend Memcached_DataObject, the (slightly misnamed)
+ * extension of DB_DataObject that provides caching, internationalization,
+ * and other bits of good functionality to StatusNet-specific data classes.
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+
+class Group_message extends Memcached_DataObject
+{
+    public $__table = 'user_greeting_count'; // table name
+    public $user_id;                         // int(4)  primary_key not_null
+    public $greeting_count;                  // int(4)
+
+    /**
+     * Get an instance by key
+     *
+     * This is a utility method to get a single instance with a given key value.
+     *
+     * @param string $k Key to use to lookup (usually 'user_id' for this class)
+     * @param mixed  $v Value to lookup
+     *
+     * @return Group_message object found, or null for no hits
+     *
+     */
+    function staticGet($k, $v=null)
+    {
+        return Memcached_DataObject::staticGet('Group_message', $k, $v);
+    }
+
+    /**
+     * return table definition for DB_DataObject
+     *
+     * DB_DataObject needs to know something about the table to manipulate
+     * instances. This method provides all the DB_DataObject needs to know.
+     *
+     * @return array array of column definitions
+     */
+    function table()
+    {
+        return array('user_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
+                     'greeting_count' => DB_DATAOBJECT_INT);
+    }
+
+    /**
+     * return key definitions for DB_DataObject
+     *
+     * DB_DataObject needs to know about keys that the table has, since it
+     * won't appear in StatusNet's own keys list. In most cases, this will
+     * simply reference your keyTypes() function.
+     *
+     * @return array list of key field names
+     */
+    function keys()
+    {
+        return array_keys($this->keyTypes());
+    }
+
+    /**
+     * return key definitions for Memcached_DataObject
+     *
+     * Our caching system uses the same key definitions, but uses a different
+     * method to get them. This key information is used to store and clear
+     * cached data, so be sure to list any key that will be used for static
+     * lookups.
+     *
+     * @return array associative array of key definitions, field name to type:
+     *         'K' for primary key: for compound keys, add an entry for each component;
+     *         'U' for unique keys: compound keys are not well supported here.
+     */
+    function keyTypes()
+    {
+        return array('user_id' => 'K');
+    }
+
+    /**
+     * Magic formula for non-autoincrementing integer primary keys
+     *
+     * If a table has a single integer column as its primary key, DB_DataObject
+     * assumes that the column is auto-incrementing and makes a sequence table
+     * to do this incrementation. Since we don't need this for our class, we
+     * overload this method and return the magic formula that DB_DataObject needs.
+     *
+     * @return array magic three-false array that stops auto-incrementing.
+     */
+    function sequenceKey()
+    {
+        return array(false, false, false);
+    }
+
+    /**
+     * Increment a user's greeting count and return instance
+     *
+     * This method handles the ins and outs of creating a new greeting_count for a
+     * user or fetching the existing greeting count and incrementing its value.
+     *
+     * @param integer $user_id ID of the user to get a count for
+     *
+     * @return Group_message instance for this user, with count already incremented.
+     */
+    static function inc($user_id)
+    {
+        $gc = Group_message::staticGet('user_id', $user_id);
+
+        if (empty($gc)) {
+
+            $gc = new Group_message();
+
+            $gc->user_id        = $user_id;
+            $gc->greeting_count = 1;
+
+            $result = $gc->insert();
+
+            if (!$result) {
+                // TRANS: Exception thrown when the user greeting count could not be saved in the database.
+                // TRANS: %d is a user ID (number).
+                throw Exception(sprintf(_m("Could not save new greeting count for %d."),
+                                        $user_id));
+            }
+        } else {
+            $orig = clone($gc);
+
+            $gc->greeting_count++;
+
+            $result = $gc->update($orig);
+
+            if (!$result) {
+                // TRANS: Exception thrown when the user greeting count could not be saved in the database.
+                // TRANS: %d is a user ID (number).
+                throw Exception(sprintf(_m("Could not increment greeting count for %d."),
+                                        $user_id));
+            }
+        }
+
+        return $gc;
+    }
+}
diff --git a/plugins/PrivateGroup/Group_privacy_settings.php b/plugins/PrivateGroup/Group_privacy_settings.php
new file mode 100644 (file)
index 0000000..7861571
--- /dev/null
@@ -0,0 +1,147 @@
+<?php
+/**
+ * Data class for group privacy settings
+ *
+ * PHP version 5
+ *
+ * @category Data
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2011, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Data class for group privacy
+ *
+ * Stores admin preferences about the group.
+ *
+ * @category Action
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * @see      DB_DataObject
+ */
+
+class Group_privacy_settings extends Memcached_DataObject
+{
+    public $__table = 'group_privacy_settings';
+    /** ID of the group. */
+    public $group_id;      
+    /** When to allow privacy: always, sometimes, or never. */
+    public $allow_privacy;
+    /** Who can send private messages: everyone, member, admin */
+    public $allow_sender; 
+    /** row creation timestamp */
+    public $created;
+    /** Last-modified timestamp */
+    public $modified;
+
+    /** NEVER is */
+
+    const SOMETIMES = -1;
+    const NEVER  = 0;
+    const ALWAYS = 1;
+
+    /** These are bit-mappy, as a hedge against the future. */
+
+    const EVERYONE = 1;
+    const MEMBER   = 2;
+    const ADMIN    = 4;
+
+    /**
+     * Get an instance by key
+     *
+     * This is a utility method to get a single instance with a given key value.
+     *
+     * @param string $k Key to use to lookup (usually 'user_id' for this class)
+     * @param mixed  $v Value to lookup
+     *
+     * @return User_greeting_count object found, or null for no hits
+     */
+
+    function staticGet($k, $v=null)
+    {
+        return Memcached_DataObject::staticGet('Group_privacy_settings', $k, $v);
+    }
+
+    /**
+     * return table definition for DB_DataObject
+     *
+     * DB_DataObject needs to know something about the table to manipulate
+     * instances. This method provides all the DB_DataObject needs to know.
+     *
+     * @return array array of column definitions
+     */
+
+    function table()
+    {
+        return array('group_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
+                     'allow_privacy' => DB_DATAOBJECT_INT,
+                     'allow_sender' => DB_DATAOBJECT_INT,
+                     'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL,
+                     'modified' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL);
+                     
+    }
+
+    /**
+     * return key definitions for DB_DataObject
+     *
+     * DB_DataObject needs to know about keys that the table has, since it
+     * won't appear in StatusNet's own keys list. In most cases, this will
+     * simply reference your keyTypes() function.
+     *
+     * @return array list of key field names
+     */
+
+    function keys()
+    {
+        return array_keys($this->keyTypes());
+    }
+
+    /**
+     * return key definitions for Memcached_DataObject
+     *
+     * @return array associative array of key definitions, field name to type:
+     *         'K' for primary key: for compound keys, add an entry for each component;
+     *         'U' for unique keys: compound keys are not well supported here.
+     */
+
+    function keyTypes()
+    {
+        return array('group_id' => 'K');
+    }
+
+    /**
+     * Magic formula for non-autoincrementing integer primary keys
+     *
+     * @return array magic three-false array that stops auto-incrementing.
+     */
+
+    function sequenceKey()
+    {
+        return array(false, false, false);
+    }
+}
diff --git a/plugins/PrivateGroup/PrivateGroupPlugin.php b/plugins/PrivateGroup/PrivateGroupPlugin.php
new file mode 100644 (file)
index 0000000..9851313
--- /dev/null
@@ -0,0 +1,240 @@
+<?php
+/**
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2010, StatusNet, Inc.
+ *
+ * Private groups for StatusNet 0.9.x
+ *
+ * PHP version 5
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @category  Privacy
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+if (!defined('STATUSNET')) {
+    // This check helps protect against security problems;
+    // your code file can't be executed directly from the web.
+    exit(1);
+}
+
+/**
+ * Private groups
+ *
+ * This plugin allows users to send private messages to a group.
+ *
+ * @category  Privacy
+ * @package   StatusNet
+ * @author    Evan Prodromou <evan@status.net>
+ * @copyright 2010 StatusNet, Inc.
+ * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
+ * @link      http://status.net/
+ */
+
+class PrivateGroupPlugin extends Plugin
+{
+    /**
+     * Database schema setup
+     *
+     * @see Schema
+     * @see ColumnDef
+     *
+     * @return boolean hook value
+     */
+
+    function onCheckSchema()
+    {
+        $schema = Schema::get();
+
+        // For storing user-submitted flags on profiles
+
+        $schema->ensureTable('group_privacy_settings',
+                             array(new ColumnDef('group_id',
+                                                 'integer',
+                                                 null,
+                                                 false,
+                                                 'PRI'),
+                                   new ColumnDef('allow_privacy',
+                                                 'integer'),
+                                   new ColumnDef('allow_sender',
+                                                 'integer'),
+                                   new ColumnDef('created',
+                                                 'datetime'),
+                                   new ColumnDef('modified',
+                                                 'timestamp')));
+                             
+        $schema->ensureTable('group_message',
+                             array(new ColumnDef('id',
+                                                 'char',
+                                                 36,
+                                                 false,
+                                                 'PRI'),
+                                   new ColumnDef('uri',
+                                                 'varchar',
+                                                 255,
+                                                 false,
+                                                 'UNI'),
+                                   new ColumnDef('from_profile',
+                                                 'integer',
+                                                 null,
+                                                 false,
+                                                 'MUL'),
+                                   new ColumnDef('to_group',
+                                                 'integer',
+                                                 null,
+                                                 false,
+                                                 'MUL'),
+                                   new ColumnDef('content',
+                                                 'text'),
+                                   new ColumnDef('rendered',
+                                                 'text'),
+                                   new ColumnDef('url',
+                                                 'varchar',
+                                                 255,
+                                                 false,
+                                                 'UNI'),
+                                   new ColumnDef('created',
+                                                 'datetime'),
+                                   new ColumnDef('modified',
+                                                 'timestamp')));
+
+
+        $schema->ensureTable('group_message_copy',
+                             array(new ColumnDef('group_message_id',
+                                                 'char',
+                                                 36,
+                                                 false,
+                                                 'PRI'),
+                                   new ColumnDef('message_uri',
+                                                 'varchar',
+                                                 255,
+                                                 false,
+                                                 'PRI'),
+                                   new ColumnDef('created',
+                                                 'datetime'),
+                                   new ColumnDef('modified',
+                                                 'timestamp')));
+
+        return true;
+    }
+
+    /**
+     * Load related modules when needed
+     *
+     * @param string $cls Name of the class to be loaded
+     *
+     * @return boolean hook value
+     */
+
+    function onAutoload($cls)
+    {
+        $dir = dirname(__FILE__);
+
+        switch ($cls)
+        {
+        case 'GroupinboxAction':
+            include_once $dir . '/' . strtolower(mb_substr($cls, 0, -6)) . '.php';
+            return false;
+        case 'Group_privacy_settings':
+        case 'Group_private_inbox':
+            include_once $dir . '/'.$cls.'.php';
+            return false;
+        default:
+            return true;
+        }
+    }
+
+    /**
+     * Map URLs to actions
+     *
+     * @param Net_URL_Mapper $m path-to-action mapper
+     *
+     * @return boolean hook value
+     */
+
+    function onRouterInitialized($m)
+    {
+        $m->connect('group/:nickname/inbox',
+                    array('action' => 'groupinbox'),
+                    array('nickname' => Nickname::DISPLAY_FMT));
+
+        return true;
+    }
+
+    /**
+     * Add group inbox to the menu
+     *
+     * @param Action $action The current action handler. Use this to
+     *                       do any output.
+     *
+     * @return boolean hook value; true means continue processing, false means stop.
+     *
+     * @see Action
+     */
+
+    function onEndGroupGroupNav($groupnav)
+    {
+        $action = $groupnav->action;
+        $group  = $groupnav->group;
+
+        $action->menuItem(common_local_url('groupinbox',
+                                           array('nickname' => $group->nickname)),
+                          _m('Inbox'),
+                          _m('Private messages for this group'),
+                          $action->trimmed('action') == 'groupinbox',
+                          'nav_group_inbox');
+        return true;
+    }
+
+    /**
+     * Create default group privacy settings at group create time
+     *
+     * @param $group Group that was just created
+     *
+     * @result boolean hook value
+     */
+
+    function onEndGroupSave($group)
+    {
+        $gps = new Group_privacy_settings();
+
+        $gps->group_id      = $group->id;
+        $gps->allow_privacy = Group_privacy_settings::SOMETIMES;
+        $gps->allow_sender  = Group_privacy_settings::MEMBER;
+        $gps->created       = common_sql_now();
+        $gps->modified      = $gps->created;
+
+        // This will throw an exception on error
+
+        $gps->insert();
+
+        return true;
+    }
+
+    function onPluginVersion(&$versions)
+    {
+        $versions[] = array('name' => 'PrivateGroup',
+                            'version' => STATUSNET_VERSION,
+                            'author' => 'Evan Prodromou',
+                            'homepage' => 'http://status.net/wiki/Plugin:PrivateGroup',
+                            'rawdescription' =>
+                            _m('Allow posting DMs to a group.'));
+        return true;
+    }
+}
diff --git a/plugins/PrivateGroup/groupinbox.php b/plugins/PrivateGroup/groupinbox.php
new file mode 100644 (file)
index 0000000..a793ac6
--- /dev/null
@@ -0,0 +1,164 @@
+<?php
+/**
+ * Give a warm greeting to our friendly user
+ *
+ * PHP version 5
+ *
+ * @category Sample
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ *
+ * StatusNet - the distributed open-source microblogging tool
+ * Copyright (C) 2009, StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+if (!defined('STATUSNET')) {
+    exit(1);
+}
+
+/**
+ * Give a warm greeting to our friendly user
+ *
+ * This sample action shows some basic ways of doing output in an action
+ * class.
+ *
+ * Action classes have several output methods that they override from
+ * the parent class.
+ *
+ * @category Sample
+ * @package  StatusNet
+ * @author   Evan Prodromou <evan@status.net>
+ * @license  http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
+ * @link     http://status.net/
+ */
+class HelloAction extends Action
+{
+    var $user = null;
+    var $gc   = null;
+
+    /**
+     * Take arguments for running
+     *
+     * This method is called first, and it lets the action class get
+     * all its arguments and validate them. It's also the time
+     * to fetch any relevant data from the database.
+     *
+     * Action classes should run parent::prepare($args) as the first
+     * line of this method to make sure the default argument-processing
+     * happens.
+     *
+     * @param array $args $_REQUEST args
+     *
+     * @return boolean success flag
+     */
+    function prepare($args)
+    {
+        parent::prepare($args);
+
+        $this->user = common_current_user();
+
+        if (!empty($this->user)) {
+            $this->gc = User_greeting_count::inc($this->user->id);
+        }
+
+        return true;
+    }
+
+    /**
+     * Handle request
+     *
+     * This is the main method for handling a request. Note that
+     * most preparation should be done in the prepare() method;
+     * by the time handle() is called the action should be
+     * more or less ready to go.
+     *
+     * @param array $args $_REQUEST args; handled in prepare()
+     *
+     * @return void
+     */
+    function handle($args)
+    {
+        parent::handle($args);
+
+        $this->showPage();
+    }
+
+    /**
+     * Title of this page
+     *
+     * Override this method to show a custom title.
+     *
+     * @return string Title of the page
+     */
+    function title()
+    {
+        if (empty($this->user)) {
+            return _m('Hello');
+        } else {
+            return sprintf(_m('Hello, %s!'), $this->user->nickname);
+        }
+    }
+
+    /**
+     * Show content in the content area
+     *
+     * The default StatusNet page has a lot of decorations: menus,
+     * logos, tabs, all that jazz. This method is used to show
+     * content in the content area of the page; it's the main
+     * thing you want to overload.
+     *
+     * This method also demonstrates use of a plural localized string.
+     *
+     * @return void
+     */
+    function showContent()
+    {
+        if (empty($this->user)) {
+            $this->element('p', array('class' => 'greeting'),
+                           _m('Hello, stranger!'));
+        } else {
+            $this->element('p', array('class' => 'greeting'),
+                           sprintf(_m('Hello, %s'), $this->user->nickname));
+            $this->element('p', array('class' => 'greeting_count'),
+                           sprintf(_m('I have greeted you %d time.',
+                                      'I have greeted you %d times.',
+                                      $this->gc->greeting_count),
+                                   $this->gc->greeting_count));
+        }
+    }
+
+    /**
+     * Return true if read only.
+     *
+     * Some actions only read from the database; others read and write.
+     * The simple database load-balancer built into StatusNet will
+     * direct read-only actions to database mirrors (if they are configured),
+     * and read-write actions to the master database.
+     *
+     * This defaults to false to avoid data integrity issues, but you
+     * should make sure to overload it for performance gains.
+     *
+     * @param array $args other arguments, if RO/RW status depends on them.
+     *
+     * @return boolean is read only action?
+     */
+    function isReadOnly($args)
+    {
+        return false;
+    }
+}