]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - plugins/Event/RSVP.php
Update translator documentation.
[quix0rs-gnu-social.git] / plugins / Event / RSVP.php
index 38d68c91ed35a473041874bc32d0c15cfa14048d..1f47958e9a266b4e07fdd3828c614b6f0d46f813 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Data class for counting greetings
+ * Data class for event RSVPs
  *
  * PHP version 5
  *
@@ -11,7 +11,7 @@
  * @link     http://status.net/
  *
  * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2009, StatusNet, Inc.
+ * 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
@@ -31,154 +31,403 @@ 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 class for event RSVPs
  *
- * 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
+ * @category Event
  * @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
+ * @see      Managed_DataObject
  */
-
-class User_greeting_count extends Memcached_DataObject
+class RSVP extends Managed_DataObject
 {
-    public $__table = 'user_greeting_count'; // table name
-    public $user_id;                         // int(4)  primary_key not_null
-    public $greeting_count;                  // int(4)
+    const POSITIVE = 'http://activitystrea.ms/schema/1.0/rsvp-yes';
+    const POSSIBLE = 'http://activitystrea.ms/schema/1.0/rsvp-maybe';
+    const NEGATIVE = 'http://activitystrea.ms/schema/1.0/rsvp-no';
+
+    public $__table = 'rsvp'; // table name
+    public $id;                // varchar(36) UUID
+    public $uri;               // varchar(255)
+    public $profile_id;        // int
+    public $event_id;          // varchar(36) UUID
+    public $response;            // tinyint
+    public $created;           // datetime
 
     /**
      * 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 string $k Key to use to lookup (usually 'id' for this class)
      * @param mixed  $v Value to lookup
      *
-     * @return User_greeting_count object found, or null for no hits
-     *
+     * @return RSVP object found, or null for no hits
      */
     function staticGet($k, $v=null)
     {
-        return Memcached_DataObject::staticGet('User_greeting_count', $k, $v);
+        return Memcached_DataObject::staticGet('RSVP', $k, $v);
     }
 
     /**
-     * return table definition for DB_DataObject
+     * Get an instance by compound key
      *
-     * DB_DataObject needs to know something about the table to manipulate
-     * instances. This method provides all the DB_DataObject needs to know.
+     * @param array $kv array of key-value mappings
      *
-     * @return array array of column definitions
+     * @return Bookmark object found, or null for no hits
      */
-    function table()
+
+    function pkeyGet($kv)
     {
-        return array('user_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL,
-                     'greeting_count' => DB_DATAOBJECT_INT);
+        return Memcached_DataObject::pkeyGet('RSVP', $kv);
     }
 
     /**
-     * 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.
+     * Add the compound profile_id/event_id index to our cache keys
+     * since the DB_DataObject stuff doesn't understand compound keys
+     * except for the primary.
      *
-     * @return array list of key field names
+     * @return array
      */
-    function keys()
-    {
-        return array_keys($this->keyTypes());
+    function _allCacheKeys() {
+        $keys = parent::_allCacheKeys();
+        $keys[] = self::multicacheKey('RSVP', array('profile_id' => $this->profile_id,
+                                                    'event_id' => $this->event_id));
+        return $keys;
     }
 
     /**
-     * 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.
+     * The One True Thingy that must be defined and declared.
      */
-    function keyTypes()
+    public static function schemaDef()
     {
-        return array('user_id' => 'K');
+        return array(
+            'description' => 'Plan to attend event',
+            'fields' => array(
+                'id' => array('type' => 'char',
+                              'length' => 36,
+                              'not null' => true,
+                              'description' => 'UUID'),
+                'uri' => array('type' => 'varchar',
+                               'length' => 255,
+                               'not null' => true),
+                'profile_id' => array('type' => 'int'),
+                'event_id' => array('type' => 'char',
+                              'length' => 36,
+                              'not null' => true,
+                              'description' => 'UUID'),
+                'response' => array('type' => 'char',
+                                  'length' => '1',
+                                  'description' => 'Y, N, or ? for three-state yes, no, maybe'),
+                'created' => array('type' => 'datetime',
+                                   'not null' => true),
+            ),
+            'primary key' => array('id'),
+            'unique keys' => array(
+                'rsvp_uri_key' => array('uri'),
+                'rsvp_profile_event_key' => array('profile_id', 'event_id'),
+            ),
+            'foreign keys' => array('rsvp_event_id_key' => array('event', array('event_id' => 'id')),
+                                    'rsvp_profile_id__key' => array('profile', array('profile_id' => 'id'))),
+            'indexes' => array('rsvp_created_idx' => array('created')),
+        );
     }
 
-    /**
-     * 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()
+    function saveNew($profile, $event, $verb, $options=array())
     {
-        return array(false, false, false);
+        if (array_key_exists('uri', $options)) {
+            $other = RSVP::staticGet('uri', $options['uri']);
+            if (!empty($other)) {
+                // TRANS: Client exception thrown when trying to save an already existing RSVP ("please respond").
+                throw new ClientException(_m('RSVP already exists.'));
+            }
+        }
+
+        $other = RSVP::pkeyGet(array('profile_id' => $profile->id,
+                                     'event_id' => $event->id));
+
+        if (!empty($other)) {
+            // TRANS: Client exception thrown when trying to save an already existing RSVP ("please respond").
+            throw new ClientException(_m('RSVP already exists.'));
+        }
+
+        $rsvp = new RSVP();
+
+        $rsvp->id          = UUID::gen();
+        $rsvp->profile_id  = $profile->id;
+        $rsvp->event_id    = $event->id;
+        $rsvp->response      = self::codeFor($verb);
+
+        if (array_key_exists('created', $options)) {
+            $rsvp->created = $options['created'];
+        } else {
+            $rsvp->created = common_sql_now();
+        }
+
+        if (array_key_exists('uri', $options)) {
+            $rsvp->uri = $options['uri'];
+        } else {
+            $rsvp->uri = common_local_url('showrsvp',
+                                        array('id' => $rsvp->id));
+        }
+
+        $rsvp->insert();
+
+        self::blow('rsvp:for-event:%s', $event->id);
+
+        // XXX: come up with something sexier
+
+        $content = $rsvp->asString();
+
+        $rendered = $rsvp->asHTML();
+
+        $options = array_merge(array('object_type' => $verb),
+                               $options);
+
+        if (!array_key_exists('uri', $options)) {
+            $options['uri'] = $rsvp->uri;
+        }
+
+        $eventNotice = $event->getNotice();
+
+        if (!empty($eventNotice)) {
+            $options['reply_to'] = $eventNotice->id;
+        }
+
+        $saved = Notice::saveNew($profile->id,
+                                 $content,
+                                 array_key_exists('source', $options) ?
+                                 $options['source'] : 'web',
+                                 $options);
+
+        return $saved;
     }
 
-    /**
-     * 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 User_greeting_count instance for this user, with count already incremented.
-     */
-    static function inc($user_id)
+    function codeFor($verb)
+    {
+        switch ($verb) {
+        case RSVP::POSITIVE:
+            return 'Y';
+            break;
+        case RSVP::NEGATIVE:
+            return 'N';
+            break;
+        case RSVP::POSSIBLE:
+            return '?';
+            break;
+        default:
+            // TRANS: Exception thrown when requesting an undefined verb for RSVP.
+            throw new Exception(sprintf(_m('Unknown verb "%s".'),$verb));
+        }
+    }
+
+    static function verbFor($code)
     {
-        $gc = User_greeting_count::staticGet('user_id', $user_id);
+        switch ($code) {
+        case 'Y':
+            return RSVP::POSITIVE;
+            break;
+        case 'N':
+            return RSVP::NEGATIVE;
+            break;
+        case '?':
+            return RSVP::POSSIBLE;
+            break;
+        default:
+            // TRANS: Exception thrown when requesting an undefined code for RSVP.
+            throw new Exception(sprintf(_m('Unknown code "%s".'),$code));
+        }
+    }
 
-        if (empty($gc)) {
+    function getNotice()
+    {
+        $notice = Notice::staticGet('uri', $this->uri);
+        if (empty($notice)) {
+            // TRANS: Server exception thrown when requesting a non-exsting notice for an RSVP ("please respond").
+            // TRANS: %s is the RSVP with the missing notice.
+            throw new ServerException(sprintf(_m('RSVP %s does not correspond to a notice in the database.'),$this->id));
+        }
+        return $notice;
+    }
 
-            $gc = new User_greeting_count();
+    static function fromNotice($notice)
+    {
+        return RSVP::staticGet('uri', $notice->uri);
+    }
 
-            $gc->user_id        = $user_id;
-            $gc->greeting_count = 1;
+    static function forEvent($event)
+    {
+        $keypart = sprintf('rsvp:for-event:%s', $event->id);
 
-            $result = $gc->insert();
+        $idstr = self::cacheGet($keypart);
 
-            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));
-            }
+        if ($idstr !== false) {
+            $ids = explode(',', $idstr);
         } else {
-            $orig = clone($gc);
+            $ids = array();
+
+            $rsvp = new RSVP();
 
-            $gc->greeting_count++;
+            $rsvp->selectAdd();
+            $rsvp->selectAdd('id');
+
+            $rsvp->event_id = $event->id;
+
+            if ($rsvp->find()) {
+                while ($rsvp->fetch()) {
+                    $ids[] = $rsvp->id;
+                }
+            }
+            self::cacheSet($keypart, implode(',', $ids));
+        }
 
-            $result = $gc->update($orig);
+        $rsvps = array(RSVP::POSITIVE => array(),
+                       RSVP::NEGATIVE => array(),
+                       RSVP::POSSIBLE => array());
 
-            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));
+        foreach ($ids as $id) {
+            $rsvp = RSVP::staticGet('id', $id);
+            if (!empty($rsvp)) {
+                $verb = self::verbFor($rsvp->response);
+                $rsvps[$verb][] = $rsvp;
             }
         }
 
-        return $gc;
+        return $rsvps;
+    }
+
+    function getProfile()
+    {
+        $profile = Profile::staticGet('id', $this->profile_id);
+        if (empty($profile)) {
+            // TRANS: Exception thrown when requesting a non-existing profile.
+            // TRANS: %s is the ID of the non-existing profile.
+            throw new Exception(sprintf(_m('No profile with ID %s.'),$this->profile_id));
+        }
+        return $profile;
+    }
+
+    function getEvent()
+    {
+        $event = Happening::staticGet('id', $this->event_id);
+        if (empty($event)) {
+            // TRANS: Exception thrown when requesting a non-existing event.
+            // TRANS: %s is the ID of the non-existing event.
+            throw new Exception(sprintf(_m('No event with ID %s.'),$this->event_id));
+        }
+        return $event;
+    }
+
+    function asHTML()
+    {
+        $event = Happening::staticGet('id', $this->event_id);
+
+        return self::toHTML($this->getProfile(),
+                            $event,
+                            $this->response);
+    }
+
+    function asString()
+    {
+        $event = Happening::staticGet('id', $this->event_id);
+
+        return self::toString($this->getProfile(),
+                              $event,
+                              $this->response);
+    }
+
+    static function toHTML($profile, $event, $response)
+    {
+        $fmt = null;
+
+        switch ($response) {
+        case 'Y':
+            // TRANS: HTML version of an RSVP ("please respond") status for a user.
+            // TRANS: %1$s is a profile URL, %2$s a profile name,
+            // TRANS: %3$s is an event URL, %4$s an event title.
+            $fmt = _m("<span class='automatic event-rsvp'><a href='%1\$s'>%2\$s</a> is attending <a href='%3\$s'>%4\$s</a>.</span>");
+            break;
+        case 'N':
+            // TRANS: HTML version of an RSVP ("please respond") status for a user.
+            // TRANS: %1$s is a profile URL, %2$s a profile name,
+            // TRANS: %3$s is an event URL, %4$s an event title.
+            $fmt = _m("<span class='automatic event-rsvp'><a href='%1\$s'>%2\$s</a> is not attending <a href='%3\$s'>%4\$s</a>.</span>");
+            break;
+        case '?':
+            // TRANS: HTML version of an RSVP ("please respond") status for a user.
+            // TRANS: %1$s is a profile URL, %2$s a profile name,
+            // TRANS: %3$s is an event URL, %4$s an event title.
+            $fmt = _m("<span class='automatic event-rsvp'><a href='%1\$s'>%2\$s</a> might attend <a href='%3\$s'>%4\$s</a>.</span>");
+            break;
+        default:
+            // TRANS: Exception thrown when requesting a user's RSVP status for a non-existing response code.
+            // TRANS: %s is the non-existing response code.
+            throw new Exception(sprintf(_m('Unknown response code %s.'),$response));
+            break;
+        }
+
+        if (empty($event)) {
+            $eventUrl = '#';
+            // TRANS: Used as event title when not event title is available.
+            // TRANS: Used as: Username [is [not ] attending|might attend] an unknown event.
+            $eventTitle = _m('an unknown event');
+        } else {
+            $notice = $event->getNotice();
+            $eventUrl = $notice->bestUrl();
+            $eventTitle = $event->title;
+        }
+
+        return sprintf($fmt,
+                       htmlspecialchars($profile->profileurl),
+                       htmlspecialchars($profile->getBestName()),
+                       htmlspecialchars($eventUrl),
+                       htmlspecialchars($eventTitle));
+    }
+
+    static function toString($profile, $event, $response)
+    {
+        $fmt = null;
+
+        switch ($response) {
+        case 'Y':
+            // TRANS: Plain text version of an RSVP ("please respond") status for a user.
+            // TRANS: %1$s is a profile name, %2$s is an event title.
+            $fmt = _m('%1$s is attending %2$s.');
+            break;
+        case 'N':
+            // TRANS: Plain text version of an RSVP ("please respond") status for a user.
+            // TRANS: %1$s is a profile name, %2$s is an event title.
+            $fmt = _m('%1$s is not attending %2$s.');
+            break;
+        case '?':
+            // TRANS: Plain text version of an RSVP ("please respond") status for a user.
+            // TRANS: %1$s is a profile name, %2$s is an event title.
+            $fmt = _m('%1$s might attend %2$s.');
+            break;
+        default:
+            // TRANS: Exception thrown when requesting a user's RSVP status for a non-existing response code.
+            // TRANS: %s is the non-existing response code.
+            throw new Exception(sprintf(_m('Unknown response code %s.'),$response));
+            break;
+        }
+
+        if (empty($event)) {
+            // TRANS: Used as event title when not event title is available.
+            // TRANS: Used as: Username [is [not ] attending|might attend] an unknown event.
+            $eventTitle = _m('an unknown event');
+        } else {
+            $notice = $event->getNotice();
+            $eventTitle = $event->title;
+        }
+
+        return sprintf($fmt,
+                       $profile->getBestName(),
+                       $eventTitle);
+    }
+
+    function delete()
+    {
+        self::blow('rsvp:for-event:%s', $event->id);
+        parent::delete();
     }
 }