New function to convert BBCode for a given ID

This commit is contained in:
Michael 2021-07-08 13:47:46 +00:00
parent f3452d86c4
commit f29bd23ea8
15 changed files with 145 additions and 38 deletions

View file

@ -1,6 +1,6 @@
-- ------------------------------------------ -- ------------------------------------------
-- Friendica 2021.09-dev (Siberian Iris) -- 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', `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)', `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` 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', `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', `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', `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`), PRIMARY KEY(`id`),
INDEX `uid_start` (`uid`,`start`), INDEX `uid_start` (`uid`,`start`),
INDEX `cid` (`cid`), INDEX `cid` (`cid`),
INDEX `uri-id` (`uri-id`),
FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE, 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'; ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='Events';
-- --

View file

@ -7,12 +7,13 @@ Fields
------ ------
| Field | Description | Type | Null | Key | Default | Extra | | Field | Description | Type | Null | Key | Default | Extra |
| --------- | ------------------------------------------------------ | ------------------ | ---- | --- | ------------------- | -------------- | | --------- | ---------------------------------------------------------- | ------------------ | ---- | --- | ------------------- | -------------- |
| id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment | | id | sequential ID | int unsigned | NO | PRI | NULL | auto_increment |
| guid | | varchar(255) | NO | | | | | guid | | varchar(255) | NO | | | |
| uid | Owner User id | mediumint unsigned | NO | | 0 | | | uid | Owner User id | mediumint unsigned | NO | | 0 | |
| cid | contact_id (ID of the contact in contact table) | int unsigned | NO | | 0 | | | cid | contact_id (ID of the contact in contact table) | int unsigned | NO | | 0 | |
| uri | | varchar(255) | NO | | | | | 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 | | | created | creation time | datetime | NO | | 0001-01-01 00:00:00 | |
| edited | last edit 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 | | | start | event start time | datetime | NO | | 0001-01-01 00:00:00 | |
@ -37,6 +38,7 @@ Indexes
| PRIMARY | id | | PRIMARY | id |
| uid_start | uid, start | | uid_start | uid, start |
| cid | cid | | cid | cid |
| uri-id | uri-id |
Foreign Keys Foreign Keys
------------ ------------
@ -45,5 +47,6 @@ Foreign Keys
|-------|--------------|--------------| |-------|--------------|--------------|
| uid | [user](help/database/db_user) | uid | | uid | [user](help/database/db_user) | uid |
| cid | [contact](help/database/db_contact) | id | | cid | [contact](help/database/db_contact) | id |
| uri-id | [item-uri](help/database/db_item-uri) | id |
Return to [database documentation](help/database) Return to [database documentation](help/database)

View file

@ -1263,6 +1263,37 @@ class BBCode
return $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 * Converts a BBCode message to HTML message
* *

View file

@ -1886,7 +1886,7 @@ class Contact
{ {
if (Strings::normaliseLink($new_url) != Strings::normaliseLink($old_url)) { if (Strings::normaliseLink($new_url) != Strings::normaliseLink($old_url)) {
Logger::notice('New URL differs from old URL', ['old' => $old_url, 'new' => $new_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])) { if (!DBA::update('contact', $fields, ['id' => $id])) {
@ -2073,6 +2073,14 @@ class Contact
return false; 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'])) { if (isset($ret['hide']) && is_bool($ret['hide'])) {
$ret['unsearchable'] = $ret['hide']; $ret['unsearchable'] = $ret['hide'];
} }

View file

@ -273,6 +273,7 @@ class Event
$event['cid'] = intval($arr['cid'] ?? 0); $event['cid'] = intval($arr['cid'] ?? 0);
$event['guid'] = ($arr['guid'] ?? '') ?: System::createUUID(); $event['guid'] = ($arr['guid'] ?? '') ?: System::createUUID();
$event['uri'] = ($arr['uri'] ?? '') ?: Item::newURI($event['uid'], $event['guid']); $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['type'] = ($arr['type'] ?? '') ?: 'event';
$event['summary'] = $arr['summary'] ?? ''; $event['summary'] = $arr['summary'] ?? '';
$event['desc'] = $arr['desc'] ?? ''; $event['desc'] = $arr['desc'] ?? '';
@ -937,7 +938,7 @@ class Event
$tpl = Renderer::getMarkupTemplate('event_stream_item.tpl'); $tpl = Renderer::getMarkupTemplate('event_stream_item.tpl');
$return = Renderer::replaceMacros($tpl, [ $return = Renderer::replaceMacros($tpl, [
'$id' => $item['event-id'], '$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_label' => DI::l10n()->t('Starts:'),
'$dtstart_title' => $dtstart_title, '$dtstart_title' => $dtstart_title,
'$dtstart_dt' => $dtstart_dt, '$dtstart_dt' => $dtstart_dt,
@ -955,7 +956,7 @@ class Event
'$author_name' => $item['author-name'], '$author_name' => $item['author-name'],
'$author_link' => $profile_link, '$author_link' => $profile_link,
'$author_avatar' => $item['author-avatar'], '$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:'), '$location_label' => DI::l10n()->t('Location:'),
'$show_map_label' => DI::l10n()->t('Show map'), '$show_map_label' => DI::l10n()->t('Show map'),
'$hide_map_label' => DI::l10n()->t('Hide map'), '$hide_map_label' => DI::l10n()->t('Hide map'),

View file

@ -2640,7 +2640,7 @@ class Item
) { ) {
self::addRedirToImageTags($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); $item['rendered-hash'] = hash('md5', BBCode::VERSION . '::' . $body);
$hook_data = ['item' => $item, 'rendered-html' => $item['rendered-html'], 'rendered-hash' => $item['rendered-hash']]; $hook_data = ['item' => $item, 'rendered-html' => $item['rendered-html'], 'rendered-hash' => $item['rendered-hash']];

View file

@ -140,6 +140,8 @@ class Statuses extends BaseApi
$item['gravity'] = GRAVITY_COMMENT; $item['gravity'] = GRAVITY_COMMENT;
$item['object-type'] = Activity\ObjectType::COMMENT; $item['object-type'] = Activity\ObjectType::COMMENT;
} else { } else {
self::checkThrottleLimit();
$item['gravity'] = GRAVITY_PARENT; $item['gravity'] = GRAVITY_PARENT;
$item['object-type'] = Activity\ObjectType::NOTE; $item['object-type'] = Activity\ObjectType::NOTE;
} }

View file

@ -25,9 +25,11 @@ use Friendica\BaseModule;
use Friendica\Core\Logger; use Friendica\Core\Logger;
use Friendica\Core\System; use Friendica\Core\System;
use Friendica\DI; use Friendica\DI;
use Friendica\Model\Post;
use Friendica\Network\HTTPException; use Friendica\Network\HTTPException;
use Friendica\Security\BasicAuth; use Friendica\Security\BasicAuth;
use Friendica\Security\OAuth; use Friendica\Security\OAuth;
use Friendica\Util\DateTimeFormat;
use Friendica\Util\HTTPInputData; use Friendica\Util\HTTPInputData;
require_once __DIR__ . '/../../include/api.php'; 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. * Get user info array.
* *

View file

@ -131,7 +131,7 @@ class Status extends BaseDataTransferObject
$this->muted = $userAttributes->muted; $this->muted = $userAttributes->muted;
$this->bookmarked = $userAttributes->bookmarked; $this->bookmarked = $userAttributes->bookmarked;
$this->pinned = $userAttributes->pinned; $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->reblog = $reblog;
$this->application = $application->toArray(); $this->application = $application->toArray();
$this->account = $account->toArray(); $this->account = $account->toArray();

View file

@ -1464,7 +1464,7 @@ class Transmitter
{ {
$event = []; $event = [];
$event['name'] = $item['event-summary']; $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); $event['startTime'] = DateTimeFormat::utc($item['event-start'] . '+00:00', DateTimeFormat::ATOM);
if (!$item['event-nofinish']) { if (!$item['event-nofinish']) {
@ -1571,7 +1571,7 @@ class Transmitter
$regexp = "/[@!]\[url\=([^\[\]]*)\].*?\[\/url\]/ism"; $regexp = "/[@!]\[url\=([^\[\]]*)\].*?\[\/url\]/ism";
$body = preg_replace_callback($regexp, ['self', 'mentionCallback'], $body); $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 // 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 = preg_replace_callback($regexp, ['self', 'mentionCallback'], $item['body']);
$richbody = BBCode::removeAttachment($richbody); $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"]; $data['source'] = ['content' => $item['body'], 'mediaType' => "text/bbcode"];

View file

@ -918,7 +918,7 @@ class DFRN
$htmlbody = "[b]" . $item['title'] . "[/b]\n\n" . $htmlbody; $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); $author = self::addEntryAuthor($doc, "author", $item["author-link"], $item);

View file

@ -1109,7 +1109,7 @@ class Feed
$body = OStatus::formatPicturePost($item['body'], $item['uri-id']); $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"]); XML::addElement($doc, $entry, "content", $body, ["type" => "html"]);
@ -1186,7 +1186,7 @@ class Feed
private static function getTitle(array $item) private static function getTitle(array $item)
{ {
if ($item['title'] != '') { 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 // Fetch information about the post

View file

@ -1803,7 +1803,7 @@ class OStatus
if (!$toplevel) { if (!$toplevel) {
if (!empty($item['title'])) { 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 { } else {
$title = sprintf("New note by %s", $owner["nick"]); $title = sprintf("New note by %s", $owner["nick"]);
} }
@ -1892,7 +1892,7 @@ class OStatus
$body = "[b]".$item['title']."[/b]\n\n".$body; $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"]); XML::addElement($doc, $entry, "content", $body, ["type" => "html"]);

View 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 `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 `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 `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 `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']]); AND NOT EXISTS(SELECT `thr-parent-id` FROM `mail` WHERE `thr-parent-id` = `item-uri`.`id`)", $item['uri-id']]);

View file

@ -55,7 +55,7 @@
use Friendica\Database\DBA; use Friendica\Database\DBA;
if (!defined('DB_UPDATE_VERSION')) { if (!defined('DB_UPDATE_VERSION')) {
define('DB_UPDATE_VERSION', 1425); define('DB_UPDATE_VERSION', 1426);
} }
return [ return [
@ -628,6 +628,7 @@ return [
"uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "foreign" => ["user" => "uid"], "comment" => "Owner User id"], "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)"], "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" => ["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"], "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"], "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"], "start" => ["type" => "datetime", "not null" => "1", "default" => DBA::NULL_DATETIME, "comment" => "event start time"],
@ -648,6 +649,7 @@ return [
"PRIMARY" => ["id"], "PRIMARY" => ["id"],
"uid_start" => ["uid", "start"], "uid_start" => ["uid", "start"],
"cid" => ["cid"], "cid" => ["cid"],
"uri-id" => ["uri-id"],
] ]
], ],
"fcontact" => [ "fcontact" => [