2011-03-09 07:33:26 +00:00
|
|
|
<?php
|
2020-07-31 14:12:48 +01:00
|
|
|
// This file is part of GNU social - https://www.gnu.org/software/social
|
|
|
|
//
|
|
|
|
// GNU social 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.
|
|
|
|
//
|
|
|
|
// GNU social 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 GNU social. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
2011-03-09 07:33:26 +00:00
|
|
|
/**
|
|
|
|
* Data class for happenings
|
|
|
|
*
|
2020-07-31 14:12:48 +01:00
|
|
|
* @category Data
|
|
|
|
* @package GNUsocial
|
|
|
|
* @author Evan Prodromou <evan@status.net>
|
|
|
|
* @copyright 2011 StatusNet, Inc.
|
|
|
|
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
2011-03-09 07:33:26 +00:00
|
|
|
*/
|
|
|
|
|
2020-07-31 14:12:48 +01:00
|
|
|
defined('GNUSOCIAL') || die();
|
2011-03-09 07:33:26 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Data class for happenings
|
|
|
|
*
|
|
|
|
* There's already an Event class in lib/event.php, so we couldn't
|
|
|
|
* call this an Event without causing a hole in space-time.
|
|
|
|
*
|
|
|
|
* "Happening" seemed good enough.
|
|
|
|
*
|
2020-07-31 14:12:48 +01:00
|
|
|
* @category Event
|
|
|
|
* @package GNUsocial
|
|
|
|
* @author Evan Prodromou <evan@status.net>
|
|
|
|
* @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later
|
2011-03-09 07:33:26 +00:00
|
|
|
*
|
2020-07-31 14:12:48 +01:00
|
|
|
* @see Managed_DataObject
|
2011-03-09 07:33:26 +00:00
|
|
|
*/
|
|
|
|
class Happening extends Managed_DataObject
|
|
|
|
{
|
|
|
|
const OBJECT_TYPE = 'http://activitystrea.ms/schema/1.0/event';
|
|
|
|
|
|
|
|
public $__table = 'happening'; // table name
|
|
|
|
public $id; // varchar(36) UUID
|
2015-02-12 17:18:55 +00:00
|
|
|
public $uri; // varchar(191) not 255 because utf8mb4 takes more space
|
2011-03-09 07:33:26 +00:00
|
|
|
public $profile_id; // int
|
|
|
|
public $start_time; // datetime
|
|
|
|
public $end_time; // datetime
|
2015-02-12 17:18:55 +00:00
|
|
|
public $title; // varchar(191) not 255 because utf8mb4 takes more space
|
|
|
|
public $location; // varchar(191) not 255 because utf8mb4 takes more space
|
|
|
|
public $url; // varchar(191) not 255 because utf8mb4 takes more space
|
2011-03-09 07:33:26 +00:00
|
|
|
public $description; // text
|
|
|
|
public $created; // datetime
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The One True Thingy that must be defined and declared.
|
|
|
|
*/
|
|
|
|
public static function schemaDef()
|
|
|
|
{
|
|
|
|
return array(
|
|
|
|
'description' => 'A real-world happening',
|
|
|
|
'fields' => array(
|
|
|
|
'id' => array('type' => 'char',
|
|
|
|
'length' => 36,
|
|
|
|
'not null' => true,
|
|
|
|
'description' => 'UUID'),
|
|
|
|
'uri' => array('type' => 'varchar',
|
2015-02-12 17:18:55 +00:00
|
|
|
'length' => 191,
|
2011-03-09 07:33:26 +00:00
|
|
|
'not null' => true),
|
2011-03-09 15:07:30 +00:00
|
|
|
'profile_id' => array('type' => 'int', 'not null' => true),
|
|
|
|
'start_time' => array('type' => 'datetime', 'not null' => true),
|
|
|
|
'end_time' => array('type' => 'datetime', 'not null' => true),
|
2011-03-09 07:33:26 +00:00
|
|
|
'title' => array('type' => 'varchar',
|
2015-02-12 17:18:55 +00:00
|
|
|
'length' => 191,
|
2011-03-09 07:33:26 +00:00
|
|
|
'not null' => true),
|
|
|
|
'location' => array('type' => 'varchar',
|
2015-02-12 17:18:55 +00:00
|
|
|
'length' => 191),
|
2011-03-09 15:07:30 +00:00
|
|
|
'url' => array('type' => 'varchar',
|
2015-02-12 17:18:55 +00:00
|
|
|
'length' => 191),
|
2011-03-09 07:33:26 +00:00
|
|
|
'description' => array('type' => 'text'),
|
|
|
|
'created' => array('type' => 'datetime',
|
|
|
|
'not null' => true),
|
|
|
|
),
|
|
|
|
'primary key' => array('id'),
|
|
|
|
'unique keys' => array(
|
|
|
|
'happening_uri_key' => array('uri'),
|
|
|
|
),
|
2019-09-11 10:07:54 +01:00
|
|
|
'foreign keys' => array(
|
|
|
|
'happening_profile_id_fkey' => array('profile', array('profile_id' => 'id')),
|
|
|
|
'happening_uri_fkey' => array('notice', array('uri' => 'uri'))
|
|
|
|
),
|
2020-07-31 14:12:48 +01:00
|
|
|
'indexes' => array(
|
2020-07-31 14:36:40 +01:00
|
|
|
'happening_profile_id_idx' => array('profile_id'),
|
2020-07-31 14:12:48 +01:00
|
|
|
'happening_created_idx' => array('created'),
|
|
|
|
'happening_start_time_end_time_idx' => array('start_time', 'end_time'),
|
|
|
|
),
|
2011-03-09 07:33:26 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-01-19 00:10:06 +00:00
|
|
|
public static function saveActivityObject(Activity $act, Notice $stored)
|
2016-01-18 16:23:33 +00:00
|
|
|
{
|
2016-01-19 00:10:06 +00:00
|
|
|
if (count($act->objects) !== 1) {
|
|
|
|
// TRANS: Exception thrown when there are too many activity objects.
|
|
|
|
throw new Exception(_m('Too many activity objects.'));
|
|
|
|
}
|
|
|
|
$actobj = $act->objects[0];
|
|
|
|
if (!ActivityUtils::compareTypes($actobj->type, [Happening::OBJECT_TYPE])) {
|
|
|
|
// TRANS: Exception thrown when event plugin comes across a non-event type object.
|
|
|
|
throw new Exception(_m('Wrong type for object.'));
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
$other = Happening::getByKeys(['uri' => $actobj->id]);
|
|
|
|
throw AlreadyFulfilledException('Happening already exists.');
|
|
|
|
} catch (NoResultException $e) {
|
|
|
|
// alright, let's save this
|
2016-01-18 16:23:33 +00:00
|
|
|
}
|
|
|
|
|
2016-01-18 19:57:44 +00:00
|
|
|
$dtstart = null;
|
|
|
|
$dtend = null;
|
|
|
|
$location = null;
|
|
|
|
$url = null;
|
|
|
|
|
|
|
|
foreach ($actobj->extra as $extra) {
|
|
|
|
switch ($extra[0]) {
|
2020-07-31 14:12:48 +01:00
|
|
|
case 'dtstart':
|
|
|
|
$dtstart = $extra[2];
|
|
|
|
break;
|
|
|
|
case 'dtend':
|
|
|
|
$dtend = $extra[2];
|
|
|
|
break;
|
|
|
|
case 'location':
|
|
|
|
// location is optional
|
|
|
|
$location = $extra[2];
|
|
|
|
break;
|
|
|
|
case 'url':
|
|
|
|
// url is optional
|
|
|
|
$url = $extra[2];
|
2016-01-18 19:57:44 +00:00
|
|
|
}
|
|
|
|
}
|
2020-07-31 14:12:48 +01:00
|
|
|
if (empty($dtstart)) {
|
2016-01-18 16:23:33 +00:00
|
|
|
// TRANS: Exception thrown when has no start date
|
|
|
|
throw new Exception(_m('No start date for event.'));
|
|
|
|
}
|
2020-07-31 14:12:48 +01:00
|
|
|
if (empty($dtend)) {
|
2016-01-18 16:23:33 +00:00
|
|
|
// TRANS: Exception thrown when has no end date
|
|
|
|
throw new Exception(_m('No end date for event.'));
|
|
|
|
}
|
|
|
|
|
|
|
|
// convert RFC3339 dates delivered in Activity Stream to MySQL DATETIME date format
|
2016-01-18 19:57:44 +00:00
|
|
|
$start_time = new DateTime($dtstart);
|
2016-01-18 16:23:33 +00:00
|
|
|
$start_time->setTimezone(new DateTimeZone('UTC'));
|
|
|
|
$start_time = $start_time->format('Y-m-d H:i:s');
|
2016-01-18 19:57:44 +00:00
|
|
|
$end_time = new DateTime($dtend);
|
2016-01-18 16:23:33 +00:00
|
|
|
$end_time->setTimezone(new DateTimeZone('UTC'));
|
|
|
|
$end_time = $end_time->format('Y-m-d H:i:s');
|
|
|
|
|
|
|
|
$ev = new Happening();
|
|
|
|
|
|
|
|
$ev->id = UUID::gen();
|
2016-01-18 19:57:44 +00:00
|
|
|
$ev->uri = $actobj->id;
|
2016-01-18 16:23:33 +00:00
|
|
|
$ev->profile_id = $stored->getProfile()->getID();
|
|
|
|
$ev->start_time = $start_time;
|
|
|
|
$ev->end_time = $end_time;
|
|
|
|
$ev->title = $actobj->title;
|
|
|
|
$ev->location = $location;
|
2016-01-18 19:57:44 +00:00
|
|
|
$ev->description = $stored->getContent();
|
2016-01-18 16:23:33 +00:00
|
|
|
$ev->url = $url;
|
|
|
|
$ev->created = $stored->getCreated();
|
|
|
|
|
|
|
|
$ev->insert();
|
2016-01-18 19:57:44 +00:00
|
|
|
return $ev;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function insert()
|
|
|
|
{
|
|
|
|
$result = parent::insert();
|
|
|
|
if ($result === false) {
|
|
|
|
common_log_db_error($this, 'INSERT', __FILE__);
|
|
|
|
throw new ServerException(_('Failed to insert '._ve(get_called_class()).' into database'));
|
|
|
|
}
|
|
|
|
return $result;
|
2016-01-18 16:23:33 +00:00
|
|
|
}
|
|
|
|
|
2014-06-23 13:18:59 +01:00
|
|
|
/**
|
|
|
|
* Returns the profile's canonical url, not necessarily a uri/unique id
|
|
|
|
*
|
|
|
|
* @return string $url
|
|
|
|
*/
|
|
|
|
public function getUrl()
|
|
|
|
{
|
|
|
|
if (empty($this->url) ||
|
|
|
|
!filter_var($this->url, FILTER_VALIDATE_URL)) {
|
|
|
|
throw new InvalidUrlException($this->url);
|
|
|
|
}
|
|
|
|
return $this->url;
|
|
|
|
}
|
|
|
|
|
2015-12-31 17:04:12 +00:00
|
|
|
public function getUri()
|
|
|
|
{
|
|
|
|
return $this->uri;
|
|
|
|
}
|
|
|
|
|
2016-01-18 23:21:16 +00:00
|
|
|
public function getStored()
|
2011-03-09 07:33:26 +00:00
|
|
|
{
|
2016-01-18 22:58:32 +00:00
|
|
|
return Notice::getByKeys(array('uri'=>$this->getUri()));
|
2011-03-09 07:33:26 +00:00
|
|
|
}
|
|
|
|
|
2020-07-31 14:12:48 +01:00
|
|
|
public static function fromStored(Notice $stored)
|
2011-03-09 07:33:26 +00:00
|
|
|
{
|
2016-01-21 01:10:34 +00:00
|
|
|
if (!ActivityUtils::compareTypes($stored->getObjectType(), [self::OBJECT_TYPE])) {
|
|
|
|
throw new ServerException('Notice is not of type '.self::OBJECT_TYPE);
|
|
|
|
}
|
2016-01-18 22:58:32 +00:00
|
|
|
return self::getByKeys(array('uri'=>$stored->getUri()));
|
2011-03-09 07:33:26 +00:00
|
|
|
}
|
2011-03-09 15:40:49 +00:00
|
|
|
|
2020-07-31 14:12:48 +01:00
|
|
|
public function getRSVPs()
|
2011-03-09 15:40:49 +00:00
|
|
|
{
|
|
|
|
return RSVP::forEvent($this);
|
|
|
|
}
|
2011-03-09 17:28:25 +00:00
|
|
|
|
2020-07-31 14:12:48 +01:00
|
|
|
public function getRSVP($profile)
|
2011-03-09 17:28:25 +00:00
|
|
|
{
|
2020-07-31 14:12:48 +01:00
|
|
|
return RSVP::pkeyGet([
|
|
|
|
'profile_id' => $profile->getID(),
|
|
|
|
'event_uri' => $this->getUri(),
|
|
|
|
]);
|
2011-03-09 17:28:25 +00:00
|
|
|
}
|
2016-01-21 01:10:34 +00:00
|
|
|
|
2020-07-31 14:12:48 +01:00
|
|
|
public static function getObjectType()
|
2016-01-21 01:10:34 +00:00
|
|
|
{
|
|
|
|
return self::OBJECT_TYPE;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function asActivityObject()
|
|
|
|
{
|
|
|
|
$actobj = new ActivityObject();
|
|
|
|
$actobj->id = $this->getUri();
|
|
|
|
$actobj->type = self::getObjectType();
|
|
|
|
$actobj->title = $this->title;
|
|
|
|
$actobj->summary = $this->description;
|
|
|
|
$actobj->extra[] = array('dtstart',
|
|
|
|
array('xmlns' => 'urn:ietf:params:xml:ns:xcal'),
|
|
|
|
common_date_iso8601($this->start_time));
|
|
|
|
$actobj->extra[] = array('dtend',
|
|
|
|
array('xmlns' => 'urn:ietf:params:xml:ns:xcal'),
|
|
|
|
common_date_iso8601($this->end_time));
|
|
|
|
$actobj->extra[] = array('location',
|
|
|
|
array('xmlns' => 'urn:ietf:params:xml:ns:xcal'),
|
|
|
|
$this->location);
|
2016-01-21 01:20:50 +00:00
|
|
|
try {
|
|
|
|
$actobj->extra[] = array('url',
|
|
|
|
array('xmlns' => 'urn:ietf:params:xml:ns:xcal'),
|
|
|
|
$this->getUrl());
|
|
|
|
} catch (InvalidUrlException $e) {
|
|
|
|
// oh well, no URL for you!
|
|
|
|
}
|
2016-01-21 01:10:34 +00:00
|
|
|
|
|
|
|
/* We don't use these ourselves, but we add them to be nice RSS/XML citizens */
|
|
|
|
$actobj->extra[] = array('startdate',
|
2019-08-12 15:03:30 +01:00
|
|
|
array('xmlns' => 'http://purl.org/rss/1.0/plugins/event/'),
|
2016-01-21 01:10:34 +00:00
|
|
|
common_date_iso8601($this->start_time));
|
|
|
|
$actobj->extra[] = array('enddate',
|
2019-08-12 15:03:30 +01:00
|
|
|
array('xmlns' => 'http://purl.org/rss/1.0/plugins/event/'),
|
2016-01-21 01:10:34 +00:00
|
|
|
common_date_iso8601($this->end_time));
|
|
|
|
$actobj->extra[] = array('location',
|
2019-08-12 15:03:30 +01:00
|
|
|
array('xmlns' => 'http://purl.org/rss/1.0/plugins/event/'),
|
2016-01-21 01:10:34 +00:00
|
|
|
$this->location);
|
|
|
|
|
|
|
|
return $actobj;
|
|
|
|
}
|
2011-03-09 07:33:26 +00:00
|
|
|
}
|