]> git.mxchange.org Git - friendica.git/commitdiff
New function to convert BBCode for a given ID
authorMichael <heluecht@pirati.ca>
Thu, 8 Jul 2021 13:47:46 +0000 (13:47 +0000)
committerMichael <heluecht@pirati.ca>
Thu, 8 Jul 2021 13:47:46 +0000 (13:47 +0000)
15 files changed:
database.sql
doc/database/db_event.md
src/Content/Text/BBCode.php
src/Model/Contact.php
src/Model/Event.php
src/Model/Item.php
src/Module/Api/Mastodon/Statuses.php
src/Module/BaseApi.php
src/Object/Api/Mastodon/Status.php
src/Protocol/ActivityPub/Transmitter.php
src/Protocol/DFRN.php
src/Protocol/Feed.php
src/Protocol/OStatus.php
src/Worker/ExpirePosts.php
static/dbstructure.config.php

index 2121fec97943b3a620e1645481d1b208af2a510f..69ae84696e79fc0726d00ed19e3075bd8094dc92 100644 (file)
@@ -1,6 +1,6 @@
 -- ------------------------------------------
 -- Friendica 2021.09-dev (Siberian Iris)
--- DB_UPDATE_VERSION 1425
+-- DB_UPDATE_VERSION 1426
 -- ------------------------------------------
 
 
@@ -563,6 +563,7 @@ CREATE TABLE IF NOT EXISTS `event` (
        `uid` mediumint unsigned NOT NULL DEFAULT 0 COMMENT 'Owner User id',
        `cid` int unsigned NOT NULL DEFAULT 0 COMMENT 'contact_id (ID of the contact in contact table)',
        `uri` varchar(255) NOT NULL DEFAULT '' COMMENT '',
+       `uri-id` int unsigned COMMENT 'Id of the item-uri table entry that contains the event uri',
        `created` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'creation time',
        `edited` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'last edit time',
        `start` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'event start time',
@@ -581,8 +582,10 @@ CREATE TABLE IF NOT EXISTS `event` (
         PRIMARY KEY(`id`),
         INDEX `uid_start` (`uid`,`start`),
         INDEX `cid` (`cid`),
+        INDEX `uri-id` (`uri-id`),
        FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE,
-       FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
+       FOREIGN KEY (`cid`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE,
+       FOREIGN KEY (`uri-id`) REFERENCES `item-uri` (`id`) ON UPDATE RESTRICT ON DELETE CASCADE
 ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Events';
 
 --
index 37048599bb91d145f9c4a694ae295d0940012efd..5550d1a1a050f6bdbca8472b36de2c4aea01dd22 100644 (file)
@@ -6,28 +6,29 @@ Events
 Fields
 ------
 
-| Field     | Description                                            | Type               | Null | Key | Default             | Extra          |
-| --------- | ------------------------------------------------------ | ------------------ | ---- | --- | ------------------- | -------------- |
-| id        | sequential ID                                          | int unsigned       | NO   | PRI | NULL                | auto_increment |
-| guid      |                                                        | varchar(255)       | NO   |     |                     |                |
-| uid       | Owner User id                                          | mediumint unsigned | NO   |     | 0                   |                |
-| cid       | contact_id (ID of the contact in contact table)        | int unsigned       | NO   |     | 0                   |                |
-| uri       |                                                        | varchar(255)       | NO   |     |                     |                |
-| created   | creation time                                          | datetime           | NO   |     | 0001-01-01 00:00:00 |                |
-| edited    | last edit time                                         | datetime           | NO   |     | 0001-01-01 00:00:00 |                |
-| start     | event start time                                       | datetime           | NO   |     | 0001-01-01 00:00:00 |                |
-| finish    | event end time                                         | datetime           | NO   |     | 0001-01-01 00:00:00 |                |
-| summary   | short description or title of the event                | text               | YES  |     | NULL                |                |
-| desc      | event description                                      | text               | YES  |     | NULL                |                |
-| location  | event location                                         | text               | YES  |     | NULL                |                |
-| type      | event or birthday                                      | varchar(20)        | NO   |     |                     |                |
-| nofinish  | if event does have no end this is 1                    | boolean            | NO   |     | 0                   |                |
-| adjust    | adjust to timezone of the recipient (0 or 1)           | boolean            | NO   |     | 1                   |                |
-| ignore    | 0 or 1                                                 | boolean            | NO   |     | 0                   |                |
-| allow_cid | Access Control - list of allowed contact.id '<19><78>' | mediumtext         | YES  |     | NULL                |                |
-| allow_gid | Access Control - list of allowed groups                | mediumtext         | YES  |     | NULL                |                |
-| deny_cid  | Access Control - list of denied contact.id             | mediumtext         | YES  |     | NULL                |                |
-| deny_gid  | Access Control - list of denied groups                 | mediumtext         | YES  |     | NULL                |                |
+| Field     | Description                                                | Type               | Null | Key | Default             | Extra          |
+| --------- | ---------------------------------------------------------- | ------------------ | ---- | --- | ------------------- | -------------- |
+| id        | sequential ID                                              | int unsigned       | NO   | PRI | NULL                | auto_increment |
+| guid      |                                                            | varchar(255)       | NO   |     |                     |                |
+| uid       | Owner User id                                              | mediumint unsigned | NO   |     | 0                   |                |
+| cid       | contact_id (ID of the contact in contact table)            | int unsigned       | NO   |     | 0                   |                |
+| uri       |                                                            | varchar(255)       | NO   |     |                     |                |
+| uri-id    | Id of the item-uri table entry that contains the event uri | int unsigned       | YES  |     | NULL                |                |
+| created   | creation time                                              | datetime           | NO   |     | 0001-01-01 00:00:00 |                |
+| edited    | last edit time                                             | datetime           | NO   |     | 0001-01-01 00:00:00 |                |
+| start     | event start time                                           | datetime           | NO   |     | 0001-01-01 00:00:00 |                |
+| finish    | event end time                                             | datetime           | NO   |     | 0001-01-01 00:00:00 |                |
+| summary   | short description or title of the event                    | text               | YES  |     | NULL                |                |
+| desc      | event description                                          | text               | YES  |     | NULL                |                |
+| location  | event location                                             | text               | YES  |     | NULL                |                |
+| type      | event or birthday                                          | varchar(20)        | NO   |     |                     |                |
+| nofinish  | if event does have no end this is 1                        | boolean            | NO   |     | 0                   |                |
+| adjust    | adjust to timezone of the recipient (0 or 1)               | boolean            | NO   |     | 1                   |                |
+| ignore    | 0 or 1                                                     | boolean            | NO   |     | 0                   |                |
+| allow_cid | Access Control - list of allowed contact.id '<19><78>'     | mediumtext         | YES  |     | NULL                |                |
+| allow_gid | Access Control - list of allowed groups                    | mediumtext         | YES  |     | NULL                |                |
+| deny_cid  | Access Control - list of denied contact.id                 | mediumtext         | YES  |     | NULL                |                |
+| deny_gid  | Access Control - list of denied groups                     | mediumtext         | YES  |     | NULL                |                |
 
 Indexes
 ------------
@@ -37,6 +38,7 @@ Indexes
 | PRIMARY   | id         |
 | uid_start | uid, start |
 | cid       | cid        |
+| uri-id    | uri-id     |
 
 Foreign Keys
 ------------
@@ -45,5 +47,6 @@ Foreign Keys
 |-------|--------------|--------------|
 | uid | [user](help/database/db_user) | uid |
 | cid | [contact](help/database/db_contact) | id |
+| uri-id | [item-uri](help/database/db_item-uri) | id |
 
 Return to [database documentation](help/database)
index dbd9a5d9aa5409ad0a48f1d5cab35953267e5900..954d630eb292ec5bb1856c982330f69e1db22f33 100644 (file)
@@ -1263,6 +1263,37 @@ class BBCode
                return $bbcode;
        }
 
+       /**
+        * Converts a BBCode message for a given ID to a HTML message
+        *
+        * BBcode 2 HTML was written by WAY2WEB.net
+        * extended to work with Mistpark/Friendica - Mike Macgirvin
+        *
+        * Simple HTML values meaning:
+        * - 0: Friendica display
+        * - 1: Unused
+        * - 2: Used for Windows Phone push, Friendica API
+        * - 3: Used before converting to Markdown in bb2diaspora.php
+        * - 4: Used for WordPress, Libertree (before Markdown), pump.io and tumblr
+        * - 5: Unused
+        * - 6: Unused
+        * - 7: Used for dfrn, OStatus
+        * - 8: Used for Twitter, WP backlink text setting
+        * - 9: ActivityPub
+        *
+        * @param int    $uriid
+        * @param string $text
+        * @param int    $simple_html
+        * @return string
+        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
+        */
+       public static function convertForItem(int $uriid, string $text, int $simple_html = self::INTERNAL)
+       {
+               $try_oembed = ($simple_html == self::INTERNAL);
+
+               return self::convert($text, $try_oembed, $simple_html, false, $uriid);
+       }
+
        /**
         * Converts a BBCode message to HTML message
         *
index c65837bd971a13dfbdaceee79a2d97e4819fb792..92f3a48bf53e73263f60324c33b61dcd8cad3ac9 100644 (file)
@@ -1886,7 +1886,7 @@ class Contact
        {
                if (Strings::normaliseLink($new_url) != Strings::normaliseLink($old_url)) {
                        Logger::notice('New URL differs from old URL', ['old' => $old_url, 'new' => $new_url]);
-                       // @todo It is to decide what to do when the URL is changed
+                       return;
                }
 
                if (!DBA::update('contact', $fields, ['id' => $id])) {
@@ -2073,6 +2073,14 @@ class Contact
                        return false;
                }
 
+               if (Strings::normaliseLink($ret['url']) != Strings::normaliseLink($contact['url'])) {
+                       $cid = self::getIdForURL($ret['url']);
+                       if (!empty($cid) && ($cid != $id)) {
+                               Logger::notice('URL of contact changed.', ['id' => $id, 'new_id' => $cid, 'old' => $contact['url'], 'new' => $ret['url']]);
+                               return self::updateFromProbeArray($cid, $ret);
+                       }
+               }
+
                if (isset($ret['hide']) && is_bool($ret['hide'])) {
                        $ret['unsearchable'] = $ret['hide'];
                }
index 81e33013fb6d2b2606b656a7b9a191b407db330a..7791ac88d32bf65d26302038175b9ded7b20e8b0 100644 (file)
@@ -273,6 +273,7 @@ class Event
                $event['cid']       = intval($arr['cid']       ?? 0);
                $event['guid']      =       ($arr['guid']      ?? '') ?: System::createUUID();
                $event['uri']       =       ($arr['uri']       ?? '') ?: Item::newURI($event['uid'], $event['guid']);
+               $event['uri-id']    = ItemURI::insert(['uri' => $event['uri'], 'guid' => $event['guid']]);
                $event['type']      =       ($arr['type']      ?? '') ?: 'event';
                $event['summary']   =        $arr['summary']   ?? '';
                $event['desc']      =        $arr['desc']      ?? '';
@@ -937,7 +938,7 @@ class Event
                $tpl = Renderer::getMarkupTemplate('event_stream_item.tpl');
                $return = Renderer::replaceMacros($tpl, [
                        '$id'             => $item['event-id'],
-                       '$title'          => BBCode::convert($item['event-summary']),
+                       '$title'          => BBCode::convertForItem($item['uri-id'], $item['event-summary']),
                        '$dtstart_label'  => DI::l10n()->t('Starts:'),
                        '$dtstart_title'  => $dtstart_title,
                        '$dtstart_dt'     => $dtstart_dt,
@@ -955,7 +956,7 @@ class Event
                        '$author_name'    => $item['author-name'],
                        '$author_link'    => $profile_link,
                        '$author_avatar'  => $item['author-avatar'],
-                       '$description'    => BBCode::convert($item['event-desc']),
+                       '$description'    => BBCode::convertForItem($item['uri-id'], $item['event-desc']),
                        '$location_label' => DI::l10n()->t('Location:'),
                        '$show_map_label' => DI::l10n()->t('Show map'),
                        '$hide_map_label' => DI::l10n()->t('Hide map'),
index ac43e8a05d632d3ce79f73c59025da01de1ffccb..0a4af82bc9696022d997eca4e215b72140f4d168 100644 (file)
@@ -2640,7 +2640,7 @@ class Item
                ) {
                        self::addRedirToImageTags($item);
 
-                       $item['rendered-html'] = BBCode::convert($item['body'], true, BBCode::INTERNAL, false, $item['uri-id']);
+                       $item['rendered-html'] = BBCode::convertForItem($item['uri-id'], $item['body']);
                        $item['rendered-hash'] = hash('md5', BBCode::VERSION . '::' . $body);
 
                        $hook_data = ['item' => $item, 'rendered-html' => $item['rendered-html'], 'rendered-hash' => $item['rendered-hash']];
index 77915cab27d9dbb0edd18c90c8a90c396efea24c..1007bea0be5aab5e30609fa270ad64c0af8ab163 100644 (file)
@@ -140,6 +140,8 @@ class Statuses extends BaseApi
                        $item['gravity']     = GRAVITY_COMMENT;
                        $item['object-type'] = Activity\ObjectType::COMMENT;
                } else {
+                       self::checkThrottleLimit();
+
                        $item['gravity']     = GRAVITY_PARENT;
                        $item['object-type'] = Activity\ObjectType::NOTE;
                }
index f5a16da765de750ec097e67a3bbe1343dcf4f753..d2aa9662c184118f615c365c4cf82b22b27dd752 100644 (file)
@@ -25,9 +25,11 @@ use Friendica\BaseModule;
 use Friendica\Core\Logger;
 use Friendica\Core\System;
 use Friendica\DI;
+use Friendica\Model\Post;
 use Friendica\Network\HTTPException;
 use Friendica\Security\BasicAuth;
 use Friendica\Security\OAuth;
+use Friendica\Util\DateTimeFormat;
 use Friendica\Util\HTTPInputData;
 
 require_once __DIR__ . '/../../include/api.php';
@@ -282,6 +284,60 @@ class BaseApi extends BaseModule
                }
        }
 
+       public static function checkThrottleLimit()
+       {
+               $uid = self::getCurrentUserID();
+
+               // Check for throttling (maximum posts per day, week and month)
+               $throttle_day = DI::config()->get('system', 'throttle_limit_day');
+               if ($throttle_day > 0) {
+                       $datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60);
+
+                       $condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", GRAVITY_PARENT, $uid, $datefrom];
+                       $posts_day = Post::count($condition);
+
+                       if ($posts_day > $throttle_day) {
+                               Logger::info('Daily posting limit reached', ['uid' => $uid, 'posts' => $posts_day, 'limit' => $throttle_day]);
+                               $error = DI::l10n()->t('Too Many Requests');
+                               $error_description = DI::l10n()->tt("Daily posting limit of %d post reached. The post was rejected.", "Daily posting limit of %d posts reached. The post was rejected.", $throttle_day);
+                               $errorobj = new \Friendica\Object\Api\Mastodon\Error($error, $error_description);
+                               System::jsonError(429, $errorobj->toArray());
+                       }
+               }
+
+               $throttle_week = DI::config()->get('system', 'throttle_limit_week');
+               if ($throttle_week > 0) {
+                       $datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60*7);
+
+                       $condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", GRAVITY_PARENT, $uid, $datefrom];
+                       $posts_week = Post::count($condition);
+
+                       if ($posts_week > $throttle_week) {
+                               Logger::info('Weekly posting limit reached', ['uid' => $uid, 'posts' => $posts_week, 'limit' => $throttle_week]);
+                               $error = DI::l10n()->t('Too Many Requests');
+                               $error_description = DI::l10n()->tt("Weekly posting limit of %d post reached. The post was rejected.", "Weekly posting limit of %d posts reached. The post was rejected.", $throttle_week);
+                               $errorobj = new \Friendica\Object\Api\Mastodon\Error($error, $error_description);
+                               System::jsonError(429, $errorobj->toArray());
+                       }
+               }
+
+               $throttle_month = DI::config()->get('system', 'throttle_limit_month');
+               if ($throttle_month > 0) {
+                       $datefrom = date(DateTimeFormat::MYSQL, time() - 24*60*60*30);
+
+                       $condition = ["`gravity` = ? AND `uid` = ? AND `wall` AND `received` > ?", GRAVITY_PARENT, $uid, $datefrom];
+                       $posts_month = Post::count($condition);
+
+                       if ($posts_month > $throttle_month) {
+                               Logger::info('Monthly posting limit reached', ['uid' => $uid, 'posts' => $posts_month, 'limit' => $throttle_month]);
+                               $error = DI::l10n()->t('Too Many Requests');
+                               $error_description = DI::l10n()->t("Monthly posting limit of %d post reached. The post was rejected.", "Monthly posting limit of %d posts reached. The post was rejected.", $throttle_month);
+                               $errorobj = new \Friendica\Object\Api\Mastodon\Error($error, $error_description);
+                               System::jsonError(429, $errorobj->toArray());
+                       }
+               }
+       }
+
        /**
         * Get user info array.
         *
index f476710d4ee051f1daf6f7c7b247b2bc570295cd..2c60d4ee36f220ce614c832688c09d46363d4a8d 100644 (file)
@@ -131,7 +131,7 @@ class Status extends BaseDataTransferObject
                $this->muted = $userAttributes->muted;
                $this->bookmarked = $userAttributes->bookmarked;
                $this->pinned = $userAttributes->pinned;
-               $this->content = BBCode::convert($item['raw-body'] ?? $item['body'], false, BBCode::API, false, $item['uri-id']);
+               $this->content = BBCode::convertForItem($item['uri-id'], ($item['raw-body'] ?? $item['body']), BBCode::API);
                $this->reblog = $reblog;
                $this->application = $application->toArray();
                $this->account = $account->toArray();
index 652e7652944432ff8c1ea4c8bf25aebd2858d110..a60b7c7645da10d05a1a6fe6637cd23640d92c74 100644 (file)
@@ -1464,7 +1464,7 @@ class Transmitter
        {
                $event = [];
                $event['name'] = $item['event-summary'];
-               $event['content'] = BBCode::convert($item['event-desc'], false, BBCode::ACTIVITYPUB, false, $item['uri-id']);
+               $event['content'] = BBCode::convertForItem($item['uri-id'], $item['event-desc'], BBCode::ACTIVITYPUB);
                $event['startTime'] = DateTimeFormat::utc($item['event-start'] . '+00:00', DateTimeFormat::ATOM);
 
                if (!$item['event-nofinish']) {
@@ -1571,7 +1571,7 @@ class Transmitter
                        $regexp = "/[@!]\[url\=([^\[\]]*)\].*?\[\/url\]/ism";
                        $body = preg_replace_callback($regexp, ['self', 'mentionCallback'], $body);
 
-                       $data['content'] = BBCode::convert($body, false, BBCode::ACTIVITYPUB, false, $item['uri-id']);
+                       $data['content'] = BBCode::convertForItem($item['uri-id'], $body, BBCode::ACTIVITYPUB);
                }
 
                // The regular "content" field does contain a minimized HTML. This is done since systems like
@@ -1583,7 +1583,7 @@ class Transmitter
                        $richbody = preg_replace_callback($regexp, ['self', 'mentionCallback'], $item['body']);
                        $richbody = BBCode::removeAttachment($richbody);
 
-                       $data['contentMap'][$language] = BBCode::convert($richbody, false, BBCode::EXTERNAL, false, $item['uri-id']);
+                       $data['contentMap'][$language] = BBCode::convertForItem($item['uri-id'], $richbody, BBCode::EXTERNAL);
                }
 
                $data['source'] = ['content' => $item['body'], 'mediaType' => "text/bbcode"];
index 9ac40c0a2dafb499175b175dba106a6d9f133c1e..d8db7251b859a836da854ac03b75decf1f91da21 100644 (file)
@@ -918,7 +918,7 @@ class DFRN
                                $htmlbody = "[b]" . $item['title'] . "[/b]\n\n" . $htmlbody;
                        }
 
-                       $htmlbody = BBCode::convert($htmlbody, false, BBCode::OSTATUS, false, $item['uri-id']);
+                       $htmlbody = BBCode::convert($item['uri-id'], $htmlbody, BBCode::OSTATUS);
                }
 
                $author = self::addEntryAuthor($doc, "author", $item["author-link"], $item);
index bed67faacd6b7fa7add9587355d81e6ab201581b..47248bde1baa3c567b0d3dab0048b05e7e03a086 100644 (file)
@@ -1109,7 +1109,7 @@ class Feed
 
                $body = OStatus::formatPicturePost($item['body'], $item['uri-id']);
 
-               $body = BBCode::convert($body, false, BBCode::OSTATUS, false, $item['uri-id']);
+               $body = BBCode::convertForItem($item['uri-id'], $body, BBCode::OSTATUS, false);
 
                XML::addElement($doc, $entry, "content", $body, ["type" => "html"]);
 
@@ -1186,7 +1186,7 @@ class Feed
        private static function getTitle(array $item)
        {
                if ($item['title'] != '') {
-                       return BBCode::convert($item['title'], false, BBCode::OSTATUS, false, $item['uri-id']);
+                       return BBCode::convertForItem($item['uri-id'], $item['title'], BBCode::OSTATUS);
                }
 
                // Fetch information about the post
index 6318f0940a1c4f0724fe5f36da63c22f05d2b3bc..8b02eb00e91f28d18b11ae235e9e0091035210a8 100644 (file)
@@ -1803,7 +1803,7 @@ class OStatus
 
                if (!$toplevel) {
                        if (!empty($item['title'])) {
-                               $title = BBCode::convert($item['title'], false, BBCode::OSTATUS, false, $item['uri-id']);
+                               $title = BBCode::convertForItem($item['uri-id'], $item['title'], BBCode::OSTATUS);
                        } else {
                                $title = sprintf("New note by %s", $owner["nick"]);
                        }
@@ -1892,7 +1892,7 @@ class OStatus
                        $body = "[b]".$item['title']."[/b]\n\n".$body;
                }
 
-               $body = BBCode::convert($body, false, BBCode::OSTATUS, false, $item['uri-id']);
+               $body = BBCode::convertForItem($item['uri-id'], $body, BBCode::OSTATUS);
 
                XML::addElement($doc, $entry, "content", $body, ["type" => "html"]);
 
index 3659edfdee7bc1470b7d1965fd2672eb18694c85..3bdfd0c26fafc40c7ea0a6fa5a7cece9b041c9c0 100644 (file)
@@ -183,6 +183,7 @@ class ExpirePosts
                        AND NOT EXISTS(SELECT `thr-parent-id` FROM `post-user` WHERE `thr-parent-id` = `item-uri`.`id`)
                        AND NOT EXISTS(SELECT `external-id` FROM `post-user` WHERE `external-id` = `item-uri`.`id`)
                        AND NOT EXISTS(SELECT `uri-id` FROM `mail` WHERE `uri-id` = `item-uri`.`id`)
+                       AND NOT EXISTS(SELECT `uri-id` FROM `event` WHERE `uri-id` = `item-uri`.`id`)
                        AND NOT EXISTS(SELECT `parent-uri-id` FROM `mail` WHERE `parent-uri-id` = `item-uri`.`id`)
                        AND NOT EXISTS(SELECT `thr-parent-id` FROM `mail` WHERE `thr-parent-id` = `item-uri`.`id`)", $item['uri-id']]);
 
index c774b8570560078ffba9cc4cac889e291579fa7a..f302f1ecc40699b1e65be6be5dbe2f82f8be31b0 100644 (file)
@@ -55,7 +55,7 @@
 use Friendica\Database\DBA;
 
 if (!defined('DB_UPDATE_VERSION')) {
-       define('DB_UPDATE_VERSION', 1425);
+       define('DB_UPDATE_VERSION', 1426);
 }
 
 return [
@@ -628,6 +628,7 @@ return [
                        "uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "foreign" => ["user" => "uid"], "comment" => "Owner User id"],
                        "cid" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "foreign" => ["contact" => "id"], "comment" => "contact_id (ID of the contact in contact table)"],
                        "uri" => ["type" => "varchar(255)", "not null" => "1", "default" => "", "comment" => ""],
+                       "uri-id" => ["type" => "int unsigned", "foreign" => ["item-uri" => "id"], "comment" => "Id of the item-uri table entry that contains the event uri"],
                        "created" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "creation time"],
                        "edited" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "last edit time"],
                        "start" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "event start time"],
@@ -648,6 +649,7 @@ return [
                        "PRIMARY" => ["id"],
                        "uid_start" => ["uid", "start"],
                        "cid" => ["cid"],
+                       "uri-id" => ["uri-id"],
                ]
        ],
        "fcontact" => [