We are no longer offering accounts on this server. Consider https://gitlab.freedesktop.org/ as a place to host projects.

GroupPrivateMessagePlugin.php 14.8 KB
Newer Older
1 2 3
<?php
/**
 * StatusNet - the distributed open-source microblogging tool
4
 * Copyright (C) 2011, StatusNet, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 *
 * 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>
26
 * @copyright 2011 StatusNet, Inc.
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
 * @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>
45
 * @copyright 2011 StatusNet, Inc.
46 47 48
 * @license   http://www.fsf.org/licensing/licenses/agpl-3.0.html AGPL 3.0
 * @link      http://status.net/
 */
49
class GroupPrivateMessagePlugin extends Plugin
50 51 52 53 54 55 56 57 58 59 60 61 62 63
{
    /**
     * Database schema setup
     *
     * @see Schema
     * @see ColumnDef
     *
     * @return boolean hook value
     */
    function onCheckSchema()
    {
        $schema = Schema::get();

        // For storing user-submitted flags on profiles
64 65 66
        $schema->ensureTable('group_privacy_settings', Group_privacy_settings::schemaDef());
        $schema->ensureTable('group_message', Group_message::schemaDef());
        $schema->ensureTable('group_message_profile', Group_message_profile::schemaDef());
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
        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));

83 84 85 86
        $m->connect('group/message/:id',
                    array('action' => 'showgroupmessage'),
                    array('id' => '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'));

87 88 89 90
        $m->connect('group/:nickname/message/new',
                    array('action' => 'newgroupmessage'),
                    array('nickname' => Nickname::DISPLAY_FMT));

91 92 93 94 95 96 97 98 99 100 101 102 103
        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
     */
104
    function onEndGroupGroupNav(Menu $groupnav)
105 106 107 108 109 110
    {
        $action = $groupnav->action;
        $group  = $groupnav->group;

        $action->menuItem(common_local_url('groupinbox',
                                           array('nickname' => $group->nickname)),
111
                          // TRANS: Menu item in group page.
Siebrand Mazeland's avatar
Siebrand Mazeland committed
112
                          _m('MENU','Inbox'),
113
                          // TRANS: Menu title in group page.
Siebrand Mazeland's avatar
Siebrand Mazeland committed
114
                          _m('Private messages for this group.'),
115 116 117 118 119
                          $action->trimmed('action') == 'groupinbox',
                          'nav_group_inbox');
        return true;
    }

120 121 122
    /**
     * Create default group privacy settings at group create time
     *
123
     * @param User_group $group Group that was just created
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
     *
     * @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;
    }

144 145 146
    /**
     * Show group privacy controls on group edit form
     *
Evan Prodromou's avatar
Evan Prodromou committed
147
     * @param GroupEditForm $form form being shown
148
     */
149
    function onEndGroupEditFormData(GroupEditForm $form)
Evan Prodromou's avatar
Evan Prodromou committed
150 151 152 153
    {
        $gps = null;

        if (!empty($form->group)) {
154
            $gps = Group_privacy_settings::getKV('group_id', $form->group->id);
Evan Prodromou's avatar
Evan Prodromou committed
155 156 157 158
        }

        $form->out->elementStart('li');
        $form->out->dropdown('allow_privacy',
159
                             // TRANS: Dropdown label in group settings page for if group allows private messages.
160
                             _m('Private messages'),
161
                             // TRANS: Dropdown option in group settings page for allowing private messages.
162
                             array(Group_privacy_settings::SOMETIMES => _m('Sometimes'),
163
                                   // TRANS: Dropdown option in group settings page for allowing private messages.
164
                                   Group_privacy_settings::ALWAYS => _m('Always'),
165
                                   // TRANS: Dropdown option in group settings page for allowing private messages.
166
                                   Group_privacy_settings::NEVER => _m('Never')),
167
                             // TRANS: Dropdown title in group settings page for if group allows private messages.
Siebrand Mazeland's avatar
Siebrand Mazeland committed
168
                             _m('Whether to allow private messages to this group.'),
Evan Prodromou's avatar
Evan Prodromou committed
169 170 171 172 173
                             false,
                             (empty($gps)) ? Group_privacy_settings::SOMETIMES : $gps->allow_privacy);
        $form->out->elementEnd('li');
        $form->out->elementStart('li');
        $form->out->dropdown('allow_sender',
174
                             // TRANS: Dropdown label in group settings page for who can send private messages to the group.
Siebrand Mazeland's avatar
Siebrand Mazeland committed
175
                             _m('Private senders'),
176
                             // TRANS: Dropdown option in group settings page for who can send private messages.
177
                             array(Group_privacy_settings::EVERYONE => _m('Everyone'),
178
                                   // TRANS: Dropdown option in group settings page for who can send private messages.
179
                                   Group_privacy_settings::MEMBER => _m('Member'),
180
                                   // TRANS: Dropdown option in group settings page for who can send private messages.
181
                                   Group_privacy_settings::ADMIN => _m('Admin')),
182
                             // TRANS: Dropdown title in group settings page for who can send private messages to the group.
Siebrand Mazeland's avatar
Siebrand Mazeland committed
183
                             _m('Who can send private messages to the group.'),
Evan Prodromou's avatar
Evan Prodromou committed
184 185 186 187 188 189
                             false,
                             (empty($gps)) ? Group_privacy_settings::MEMBER : $gps->allow_sender);
        $form->out->elementEnd('li');
        return true;
    }

190
    function onEndGroupSaveForm(Action $action)
Evan Prodromou's avatar
Evan Prodromou committed
191
    {
192
        // The Action class must contain this method
Roland Haeder's avatar
Roland Haeder committed
193
        assert(is_callable(array($action, 'getGroup')));
194

Evan Prodromou's avatar
Evan Prodromou committed
195 196
        $gps = null;

mattl's avatar
mattl committed
197
        if ($action->getGroup() instanceof User_group) {
198
            $gps = Group_privacy_settings::getKV('group_id', $action->getGroup()->id);
Evan Prodromou's avatar
Evan Prodromou committed
199 200 201 202 203 204
        }

        $orig = null;

        if (empty($gps)) {
            $gps = new Group_privacy_settings();
205
            $gps->group_id = $action->getGroup()->id;
Evan Prodromou's avatar
Evan Prodromou committed
206 207 208
        } else {
            $orig = clone($gps);
        }
209

Evan Prodromou's avatar
Evan Prodromou committed
210 211 212 213 214 215 216 217 218
        $gps->allow_privacy = $action->trimmed('allow_privacy');
        $gps->allow_sender  = $action->trimmed('allow_sender');

        if (empty($orig)) {
            $gps->created = common_sql_now();
            $gps->insert();
        } else {
            $gps->update($orig);
        }
219

Evan Prodromou's avatar
Evan Prodromou committed
220 221
        return true;
    }
222 223 224

    /**
     * Overload 'd' command to send private messages to groups.
225
     *
226 227
     * 'd !group word word word' will send the private message
     * 'word word word' to the group 'group'.
228
     *
229 230 231 232
     * @param string  $cmd     Command being run
     * @param string  $arg     Rest of the message (including address)
     * @param User    $user    User sending the message
     * @param Command &$result The resulting command object to be run.
233
     *
234 235
     * @return boolean hook value
     */
236
    function onStartInterpretCommand($cmd, $arg, User $user, &$result)
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
    {
        if ($cmd == 'd' || $cmd == 'dm') {

            $this->debug('Got a d command');

            // Break off the first word as the address

            $pieces = explode(' ', $arg, 2);

            if (count($pieces) == 1) {
                $pieces[] = null;
            }

            list($addr, $msg) = $pieces;

            if (!empty($addr) && $addr[0] == '!') {
                $result = new GroupMessageCommand($user, substr($addr, 1), $msg);
                Event::handle('EndInterpretCommand', array($cmd, $arg, $user, $result));
                return false;
            }
        }

        return true;
    }

262 263 264
    /**
     * To add a "Message" button to the group profile page
     *
265
     * @param Widget     $widget The showgroup action being shown
266
     * @param User_group $group  The current group
267
     *
268 269
     * @return boolean hook value
     */
270
    function onEndGroupActionsList(Widget $widget, User_group $group)
271 272
    {
        $cur = common_current_user();
273
        $action = $widget->out;
274 275 276 277 278 279 280 281 282 283 284 285 286

        if (empty($cur)) {
            return true;
        }

        try {
            Group_privacy_settings::ensurePost($cur, $group);
        } catch (Exception $e) {
            return true;
        }

        $action->elementStart('li', 'entity_send-a-message');
        $action->element('a', array('href' => common_local_url('newgroupmessage', array('nickname' => $group->nickname)),
287
                                    // TRANS: Title for action in group actions list.
Siebrand Mazeland's avatar
Siebrand Mazeland committed
288
                                    'title' => _m('Send a direct message to this group.')),
289 290
                         // TRANS: Link text for action in group actions list to send a private message to a group.
                         _m('LINKTEXT','Message'));
291 292 293 294 295 296
        // $form = new GroupMessageForm($action, $group);
        // $form->hidden = true;
        // $form->show();
        $action->elementEnd('li');
        return true;
    }
297 298 299 300 301 302

    /**
     * When saving a notice, check its groups. If any of them has
     * privacy == always, force a group private message to all mentioned groups.
     * If any of the groups disallows private messages, skip it.
     *
303
     * @param
304
     */
305
    function onStartNoticeSave(Notice &$notice) {
306 307
        // Look for group tags
        // FIXME: won't work for remote groups
308 309
        // @fixme if Notice::saveNew is refactored so we can just pull its list
        // of groups between processing and saving, make use of it
310

311
        $count = preg_match_all('/(?:^|\s)!(' . Nickname::DISPLAY_FMT . ')/',
312 313 314 315 316 317 318
                                strtolower($notice->content),
                                $match);

        $groups = array();
        $ignored = array();

        $forcePrivate = false;
319
        $profile = $notice->getProfile();
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352

        if ($count > 0) {
            /* Add them to the database */

            foreach (array_unique($match[1]) as $nickname) {
                $group = User_group::getForNickname($nickname, $profile);

                if (empty($group)) {
                    continue;
                }

                $gps = Group_privacy_settings::forGroup($group);

                switch ($gps->allow_privacy) {
                case Group_privacy_settings::ALWAYS:
                    $forcePrivate = true;
                    // fall through
                case Group_privacy_settings::SOMETIMES:
                    $groups[] = $group;
                    break;
                case Group_privacy_settings::NEVER:
                    $ignored[] = $group;
                    break;
                }
            }

            if ($forcePrivate) {
                foreach ($ignored as $group) {
                    common_log(LOG_NOTICE,
                               "Notice forced to group direct message ".
                               "but group ".$group->nickname." does not allow them.");
                }

353
                $user = User::getKV('id', $notice->profile_id);
354 355 356 357 358 359 360 361 362 363 364 365 366

                if (empty($user)) {
                    common_log(LOG_WARNING,
                               "Notice forced to group direct message ".
                               "but profile ".$notice->profile_id." is not a local user.");
                } else {
                    foreach ($groups as $group) {
                        Group_message::send($user, $group, $notice->content);
                    }
                }

                // Don't save the notice!
                // FIXME: this is probably cheating.
367
                // TRANS: Client exception thrown when a private group message has to be forced.
368
                throw new ClientException(sprintf(_m('Forced notice to private group message.')),
369 370 371
                                          200);
            }
        }
372

373 374
        return true;
    }
375 376 377 378 379 380 381 382 383

    /**
     * Show an indicator that the group is (essentially) private on the group page
     *
     * @param Action     $action The action being shown
     * @param User_group $group  The group being shown
     *
     * @return boolean hook value
     */
384
    function onEndGroupProfileElements(Action $action, User_group $group)
385 386
    {
        $gps = Group_privacy_settings::forGroup($group);
387

388
        if ($gps->allow_privacy == Group_privacy_settings::ALWAYS) {
389
            // TRANS: Indicator on the group page that the group is (essentially) private.
390
            $action->element('p', 'privategroupindicator', _m('Private'));
391 392 393 394 395
        }

        return true;
    }

396
    function onStartShowExportData(Action $action)
397 398
    {
        if ($action instanceof ShowgroupAction) {
399
            $gps = Group_privacy_settings::forGroup($action->getGroup());
400

401 402 403 404 405 406 407
            if ($gps->allow_privacy == Group_privacy_settings::ALWAYS) {
                return false;
            }
        }
        return true;
    }

408 409
    function onPluginVersion(&$versions)
    {
410
        $versions[] = array('name' => 'GroupPrivateMessage',
411
                            'version' => GNUSOCIAL_VERSION,
412
                            'author' => 'Evan Prodromou',
413
                            'homepage' => 'http://status.net/wiki/Plugin:GroupPrivateMessage',
414
                            'rawdescription' =>
415
                            // TRANS: Plugin description.
Siebrand Mazeland's avatar
Siebrand Mazeland committed
416
                            _m('Allow posting private messages to groups.'));
417 418 419
        return true;
    }
}