X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FModel%2FEvent.php;h=d8657c1e9a61a2c706fde9c89131150ba8f0fa58;hb=1917f04153ffa37880a9900c76e54d6d73e90c85;hp=66d5f389fe709f05583fd5efecb6748556e7d097;hpb=6cf50a14fae25210a0cdb617c29d549abcfde9ac;p=friendica.git diff --git a/src/Model/Event.php b/src/Model/Event.php index 66d5f389fe..d8657c1e9a 100644 --- a/src/Model/Event.php +++ b/src/Model/Event.php @@ -5,21 +5,19 @@ namespace Friendica\Model; -use dba; use Friendica\BaseObject; use Friendica\Content\Text\BBCode; -use Friendica\Core\Addon; +use Friendica\Core\Hook; use Friendica\Core\L10n; +use Friendica\Core\Logger; use Friendica\Core\PConfig; +use Friendica\Core\Renderer; use Friendica\Core\System; -use Friendica\Database\DBM; -use Friendica\Model\Item; +use Friendica\Database\DBA; use Friendica\Util\DateTimeFormat; use Friendica\Util\Map; - -require_once 'boot.php'; -require_once 'include/dba.php'; -require_once 'include/items.php'; +use Friendica\Util\Strings; +use Friendica\Util\XML; /** * @brief functions for interacting with the event database table @@ -35,20 +33,30 @@ class Event extends BaseObject $bd_format = L10n::t('l F d, Y \@ g:i A'); // Friday January 18, 2011 @ 8 AM. - $event_start = day_translate( - $event['adjust'] ? + $event_start = L10n::getDay( + !empty($event['adjust']) ? DateTimeFormat::local($event['start'], $bd_format) : DateTimeFormat::utc($event['start'], $bd_format) ); - $event_end = day_translate( - $event['adjust'] ? - DateTimeFormat::local($event['finish'], $bd_format) : DateTimeFormat::utc($event['finish'], $bd_format) - ); + if (!empty($event['finish'])) { + $event_end = L10n::getDay( + !empty($event['adjust']) ? + DateTimeFormat::local($event['finish'], $bd_format) : DateTimeFormat::utc($event['finish'], $bd_format) + ); + } else { + $event_end = ''; + } if ($simple) { - $o = "

" . BBCode::convert($event['summary'], false, $simple) . "

"; + $o = ''; - $o .= "
" . BBCode::convert($event['desc'], false, $simple) . "
"; + if (!empty($event['summary'])) { + $o .= "

" . BBCode::convert(Strings::escapeHtml($event['summary']), false, $simple) . "

"; + } + + if (!empty($event['desc'])) { + $o .= "
" . BBCode::convert(Strings::escapeHtml($event['desc']), false, $simple) . "
"; + } $o .= "

" . L10n::t('Starts:') . "

" . $event_start . "

"; @@ -56,8 +64,8 @@ class Event extends BaseObject $o .= "

" . L10n::t('Finishes:') . "

" . $event_end . "

"; } - if (strlen($event['location'])) { - $o .= "

" . L10n::t('Location:') . "

" . BBCode::convert($event['location'], false, $simple) . "

"; + if (!empty($event['location'])) { + $o .= "

" . L10n::t('Location:') . "

" . BBCode::convert(Strings::escapeHtml($event['location']), false, $simple) . "

"; } return $o; @@ -65,25 +73,27 @@ class Event extends BaseObject $o = '
' . "\r\n"; - $o .= '
' . BBCode::convert($event['summary'], false, $simple) . '
' . "\r\n"; + $o .= '
' . BBCode::convert(Strings::escapeHtml($event['summary']), false, $simple) . '
' . "\r\n"; $o .= '
' . L10n::t('Starts:') . ' ' . $event_start . '
' . "\r\n"; if (!$event['nofinish']) { $o .= '
' . L10n::t('Finishes:') . ' ' . $event_end . '
' . "\r\n"; } - $o .= '
' . BBCode::convert($event['desc'], false, $simple) . '
' . "\r\n"; + if (!empty($event['desc'])) { + $o .= '
' . BBCode::convert(Strings::escapeHtml($event['desc']), false, $simple) . '
' . "\r\n"; + } - if (strlen($event['location'])) { + if (!empty($event['location'])) { $o .= '
' . L10n::t('Location:') . ' ' - . BBCode::convert($event['location'], false, $simple) + . BBCode::convert(Strings::escapeHtml($event['location']), false, $simple) . '
' . "\r\n"; // Include a map of the location if the [map] BBCode is used. @@ -140,6 +150,7 @@ class Event extends BaseObject * @brief Extract bbcode formatted event data from a string. * * @params: string $s The string which should be parsed for event data. + * @param $text * @return array The array with the event information. */ public static function fromBBCode($text) @@ -207,6 +218,7 @@ class Event extends BaseObject * * @param int $event_id Event ID. * @return void + * @throws \Exception */ public static function delete($event_id) { @@ -214,8 +226,8 @@ class Event extends BaseObject return; } - dba::delete('event', ['id' => $event_id]); - logger("Deleted event ".$event_id, LOGGER_DEBUG); + DBA::delete('event', ['id' => $event_id]); + Logger::log("Deleted event ".$event_id, Logger::DEBUG); } /** @@ -225,16 +237,16 @@ class Event extends BaseObject * * @param array $arr Array with event data. * @return int The new event id. + * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ public static function store($arr) { - $a = self::getApp(); - $event = []; $event['id'] = intval(defaults($arr, 'id' , 0)); $event['uid'] = intval(defaults($arr, 'uid' , 0)); $event['cid'] = intval(defaults($arr, 'cid' , 0)); - $event['uri'] = defaults($arr, 'uri' , Item::newURI($event['uid'])); + $event['guid'] = defaults($arr, 'guid' , System::createUUID()); + $event['uri'] = defaults($arr, 'uri' , Item::newURI($event['uid'], $event['guid'])); $event['type'] = defaults($arr, 'type' , 'event'); $event['summary'] = defaults($arr, 'summary' , ''); $event['desc'] = defaults($arr, 'desc' , ''); @@ -248,10 +260,10 @@ class Event extends BaseObject $event['created'] = DateTimeFormat::utc(defaults($arr, 'created' , 'now')); $event['edited'] = DateTimeFormat::utc(defaults($arr, 'edited' , 'now')); - $event['start'] = DateTimeFormat::utc(defaults($arr, 'start' , NULL_DATE)); - $event['finish'] = DateTimeFormat::utc(defaults($arr, 'finish' , NULL_DATE)); - if ($event['finish'] < NULL_DATE) { - $event['finish'] = NULL_DATE; + $event['start'] = DateTimeFormat::utc(defaults($arr, 'start' , DBA::NULL_DATETIME)); + $event['finish'] = DateTimeFormat::utc(defaults($arr, 'finish' , DBA::NULL_DATETIME)); + if ($event['finish'] < DBA::NULL_DATETIME) { + $event['finish'] = DBA::NULL_DATETIME; } $private = intval(defaults($arr, 'private', 0)); @@ -262,17 +274,17 @@ class Event extends BaseObject $conditions['self'] = true; } - $contact = dba::selectFirst('contact', [], $conditions); + $contact = DBA::selectFirst('contact', [], $conditions); // Existing event being modified. if ($event['id']) { // has the event actually changed? - $existing_event = dba::selectFirst('event', ['edited'], ['id' => $event['id'], 'uid' => $event['uid']]); - if (!DBM::is_result($existing_event) || ($existing_event['edited'] === $event['edited'])) { + $existing_event = DBA::selectFirst('event', ['edited'], ['id' => $event['id'], 'uid' => $event['uid']]); + if (!DBA::isResult($existing_event) || ($existing_event['edited'] === $event['edited'])) { $item = Item::selectFirst(['id'], ['event-id' => $event['id'], 'uid' => $event['uid']]); - return DBM::is_result($item) ? $item['id'] : 0; + return DBA::isResult($item) ? $item['id'] : 0; } $updated_fields = [ @@ -287,12 +299,12 @@ class Event extends BaseObject 'nofinish' => $event['nofinish'], ]; - dba::update('event', $updated_fields, ['id' => $event['id'], 'uid' => $event['uid']]); + DBA::update('event', $updated_fields, ['id' => $event['id'], 'uid' => $event['uid']]); $item = Item::selectFirst(['id'], ['event-id' => $event['id'], 'uid' => $event['uid']]); - if (DBM::is_result($item)) { - $object = '' . xmlify(ACTIVITY_OBJ_EVENT) . '' . xmlify($event['uri']) . ''; - $object .= '' . xmlify(self::getBBCode($event)) . ''; + if (DBA::isResult($item)) { + $object = '' . XML::escape(ACTIVITY_OBJ_EVENT) . '' . XML::escape($event['uri']) . ''; + $object .= '' . XML::escape(self::getBBCode($event)) . ''; $object .= '' . "\n"; $fields = ['body' => self::getBBCode($event), 'object' => $object, 'edited' => $event['edited']]; @@ -303,51 +315,55 @@ class Event extends BaseObject $item_id = 0; } - Addon::callHooks('event_updated', $event['id']); + Hook::callAll('event_updated', $event['id']); } else { - $event['guid'] = System::createGUID(32); - // New event. Store it. - dba::insert('event', $event); - - $event['id'] = dba::lastInsertId(); - - $item_arr = []; - - $item_arr['uid'] = $event['uid']; - $item_arr['contact-id'] = $event['cid']; - $item_arr['uri'] = $event['uri']; - $item_arr['parent-uri'] = $event['uri']; - $item_arr['guid'] = $event['guid']; - $item_arr['type'] = 'activity'; - $item_arr['wall'] = $event['cid'] ? 0 : 1; - $item_arr['contact-id'] = $contact['id']; - $item_arr['owner-name'] = $contact['name']; - $item_arr['owner-link'] = $contact['url']; - $item_arr['owner-avatar'] = $contact['thumb']; - $item_arr['author-name'] = $contact['name']; - $item_arr['author-link'] = $contact['url']; - $item_arr['author-avatar'] = $contact['thumb']; - $item_arr['title'] = ''; - $item_arr['allow_cid'] = $event['allow_cid']; - $item_arr['allow_gid'] = $event['allow_gid']; - $item_arr['deny_cid'] = $event['deny_cid']; - $item_arr['deny_gid'] = $event['deny_gid']; - $item_arr['private'] = $private; - $item_arr['visible'] = 1; - $item_arr['verb'] = ACTIVITY_POST; - $item_arr['object-type'] = ACTIVITY_OBJ_EVENT; - $item_arr['origin'] = $event['cid'] === 0 ? 1 : 0; - $item_arr['body'] = self::getBBCode($event); - $item_arr['event-id'] = $event['id']; - - $item_arr['object'] = '' . xmlify(ACTIVITY_OBJ_EVENT) . '' . xmlify($event['uri']) . ''; - $item_arr['object'] .= '' . xmlify(self::getBBCode($event)) . ''; - $item_arr['object'] .= '' . "\n"; - - $item_id = Item::insert($item_arr); - - Addon::callHooks("event_created", $event['id']); + DBA::insert('event', $event); + + $item_id = 0; + + // Don't create an item for birthday events + if ($event['type'] == 'event') { + $event['id'] = DBA::lastInsertId(); + + $item_arr = []; + + $item_arr['uid'] = $event['uid']; + $item_arr['contact-id'] = $event['cid']; + $item_arr['uri'] = $event['uri']; + $item_arr['parent-uri'] = $event['uri']; + $item_arr['guid'] = $event['guid']; + $item_arr['plink'] = defaults($arr, 'plink', ''); + $item_arr['post-type'] = Item::PT_EVENT; + $item_arr['wall'] = $event['cid'] ? 0 : 1; + $item_arr['contact-id'] = $contact['id']; + $item_arr['owner-name'] = $contact['name']; + $item_arr['owner-link'] = $contact['url']; + $item_arr['owner-avatar'] = $contact['thumb']; + $item_arr['author-name'] = $contact['name']; + $item_arr['author-link'] = $contact['url']; + $item_arr['author-avatar'] = $contact['thumb']; + $item_arr['title'] = ''; + $item_arr['allow_cid'] = $event['allow_cid']; + $item_arr['allow_gid'] = $event['allow_gid']; + $item_arr['deny_cid'] = $event['deny_cid']; + $item_arr['deny_gid'] = $event['deny_gid']; + $item_arr['private'] = $private; + $item_arr['visible'] = 1; + $item_arr['verb'] = ACTIVITY_POST; + $item_arr['object-type'] = ACTIVITY_OBJ_EVENT; + $item_arr['origin'] = $event['cid'] === 0 ? 1 : 0; + $item_arr['body'] = self::getBBCode($event); + $item_arr['event-id'] = $event['id']; + + $item_arr['object'] = '' . XML::escape(ACTIVITY_OBJ_EVENT) . '' . XML::escape($event['uri']) . ''; + $item_arr['object'] .= '' . XML::escape(self::getBBCode($event)) . ''; + $item_arr['object'] .= '' . "\n"; + + $item_id = Item::insert($item_arr); + } + + Hook::callAll("event_created", $event['id']); } return $item_id; @@ -357,6 +373,7 @@ class Event extends BaseObject * @brief Create an array with translation strings used for events. * * @return array Array with translations strings. + * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ public static function getStrings() { @@ -400,7 +417,6 @@ class Event extends BaseObject "February" => L10n::t("February"), "March" => L10n::t("March"), "April" => L10n::t("April"), - "May" => L10n::t("May"), "June" => L10n::t("June"), "July" => L10n::t("July"), "August" => L10n::t("August"), @@ -453,6 +469,7 @@ class Event extends BaseObject * @param int $event_id The ID of the event in the event table * @param string $sql_extra * @return array Query result + * @throws \Exception */ public static function getListById($owner_uid, $event_id, $sql_extra = '') { @@ -471,7 +488,7 @@ class Event extends BaseObject intval($event_id) ); - if (DBM::is_result($r)) { + if (DBA::isResult($r)) { $return = self::removeDuplicates($r); } @@ -481,17 +498,18 @@ class Event extends BaseObject /** * @brief Get all events in a specific time frame. * - * @param int $owner_uid The User ID of the owner of the events. - * @param array $event_params An associative array with - * int 'ignore' => - * string 'start' => Start time of the timeframe. - * string 'finish' => Finish time of the timeframe. - * string 'adjust_start' => - * string 'adjust_finish' => + * @param int $owner_uid The User ID of the owner of the events. + * @param array $event_params An associative array with + * int 'ignore' => + * string 'start' => Start time of the timeframe. + * string 'finish' => Finish time of the timeframe. + * string 'adjust_start' => + * string 'adjust_finish' => * - * @param string $sql_extra Additional sql conditions (e.g. permission request). + * @param string $sql_extra Additional sql conditions (e.g. permission request). * * @return array Query results. + * @throws \Exception */ public static function getListByDate($owner_uid, $event_params, $sql_extra = '') { @@ -512,15 +530,15 @@ class Event extends BaseObject $sql_extra ", intval($owner_uid), intval($event_params["ignore"]), - dbesc($event_params["start"]), - dbesc($event_params["start"]), - dbesc($event_params["finish"]), - dbesc($event_params["adjust_start"]), - dbesc($event_params["adjust_start"]), - dbesc($event_params["adjust_finish"]) + DBA::escape($event_params["start"]), + DBA::escape($event_params["start"]), + DBA::escape($event_params["finish"]), + DBA::escape($event_params["adjust_start"]), + DBA::escape($event_params["adjust_start"]), + DBA::escape($event_params["adjust_finish"]) ); - if (DBM::is_result($r)) { + if (DBA::isResult($r)) { $return = self::removeDuplicates($r); } @@ -532,6 +550,8 @@ class Event extends BaseObject * * @param array $event_result Event query array. * @return array Event array for the template. + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws \ImagickException */ public static function prepareListForTemplate(array $event_result) { @@ -541,14 +561,17 @@ class Event extends BaseObject $fmt = L10n::t('l, F j'); foreach ($event_result as $event) { $item = Item::selectFirst(['plink', 'author-name', 'author-avatar', 'author-link'], ['id' => $event['itemid']]); - if (DBM::is_result($item)) { - $event = array_merge($event, $item); + if (!DBA::isResult($item)) { + // Using default values when no item had been found + $item = ['plink' => '', 'author-name' => '', 'author-avatar' => '', 'author-link' => '']; } + $event = array_merge($event, $item); + $start = $event['adjust'] ? DateTimeFormat::local($event['start'], 'c') : DateTimeFormat::utc($event['start'], 'c'); $j = $event['adjust'] ? DateTimeFormat::local($event['start'], 'j') : DateTimeFormat::utc($event['start'], 'j'); $day = $event['adjust'] ? DateTimeFormat::local($event['start'], $fmt) : DateTimeFormat::utc($event['start'], $fmt); - $day = day_translate($day); + $day = L10n::getDay($day); if ($event['nofinish']) { $end = null; @@ -571,15 +594,21 @@ class Event extends BaseObject $drop = [System::baseUrl() . '/events/drop/' . $event['id'] , L10n::t('Delete event') , '', '']; } - $title = strip_tags(html_entity_decode(BBCode::convert($event['summary']), ENT_QUOTES, 'UTF-8')); + $title = BBCode::convert(Strings::escapeHtml($event['summary'])); if (!$title) { - list($title, $_trash) = explode(" $event['id'], 'start' => $start, @@ -604,25 +633,27 @@ class Event extends BaseObject /** * @brief Format event to export format (ical/csv). * - * @param array $events Query result for events. - * @param string $format The output format (ical/csv). - * @param string $timezone The timezone of the user (not implemented yet). + * @param array $events Query result for events. + * @param string $format The output format (ical/csv). * + * @param $timezone * @return string Content according to selected export format. * - * @todo Implement timezone support + * @todo Implement timezone support */ - private static function formatListForExport(array $events, $format, $timezone) + private static function formatListForExport(array $events, $format) { + $o = ''; + if (!count($events)) { - return ''; + return $o; } switch ($format) { // Format the exported data as a CSV file. case "csv": header("Content-type: text/csv"); - $o = '"Subject", "Start Date", "Start Time", "Description", "End Date", "End Time", "Location"' . PHP_EOL; + $o .= '"Subject", "Start Date", "Start Time", "Description", "End Date", "End Time", "Location"' . PHP_EOL; foreach ($events as $event) { /// @todo The time / date entries don't include any information about the @@ -718,6 +749,7 @@ class Event extends BaseObject * @param int $uid The user ID. * * @return array Query results. + * @throws \Exception */ private static function getListByUserId($uid = 0) { @@ -738,9 +770,9 @@ class Event extends BaseObject $conditions += ['allow_cid' => '', 'allow_gid' => '']; } - $events = dba::select('event', $fields, $conditions); - if (DBM::is_result($events)) { - $return = dba::inArray($events); + $events = DBA::select('event', $fields, $conditions); + if (DBA::isResult($events)) { + $return = DBA::toArray($events); } return $return; @@ -748,33 +780,29 @@ class Event extends BaseObject /** * - * @param int $uid The user ID. + * @param int $uid The user ID. * @param string $format Output format (ical/csv). * @return array With the results: - * bool 'success' => True if the processing was successful,
- * string 'format' => The output format,
- * string 'extension' => The file extension of the output format,
- * string 'content' => The formatted output content.
+ * bool 'success' => True if the processing was successful,
+ * string 'format' => The output format,
+ * string 'extension' => The file extension of the output format,
+ * string 'content' => The formatted output content.
* + * @throws \Exception * @todo Respect authenticated users with events_by_uid(). */ public static function exportListByUserId($uid, $format = 'ical') { $process = false; - $user = dba::selectFirst('user', ['timezone'], ['uid' => $uid]); - if (DBM::is_result($user)) { - $timezone = $user['timezone']; - } - // Get all events which are owned by a uid (respects permissions). $events = self::getListByUserId($uid); // We have the events that are available for the requestor. // Now format the output according to the requested format. - $res = self::formatListForExport($events, $format, $timezone); + $res = self::formatListForExport($events, $format); - // If there are results the precess was successfull. + // If there are results the precess was successful. if (!empty($res)) { $process = true; } @@ -808,6 +836,8 @@ class Event extends BaseObject * * @param array $item Array with item and event data. * @return string HTML output. + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws \ImagickException */ public static function getItemHTML(array $item) { $same_date = false; @@ -819,14 +849,14 @@ class Event extends BaseObject $tformat = L10n::t('g:i A'); // 8:01 AM. // Convert the time to different formats. - $dtstart_dt = day_translate( + $dtstart_dt = L10n::getDay( $item['event-adjust'] ? DateTimeFormat::local($item['event-start'], $dformat) : DateTimeFormat::utc($item['event-start'], $dformat) ); $dtstart_title = DateTimeFormat::utc($item['event-start'], $item['event-adjust'] ? DateTimeFormat::ATOM : 'Y-m-d\TH:i:s'); // Format: Jan till Dec. - $month_short = day_short_translate( + $month_short = L10n::getDayShort( $item['event-adjust'] ? DateTimeFormat::local($item['event-start'], 'M') : DateTimeFormat::utc($item['event-start'], 'M') @@ -838,7 +868,7 @@ class Event extends BaseObject $start_time = $item['event-adjust'] ? DateTimeFormat::local($item['event-start'], $tformat) : DateTimeFormat::utc($item['event-start'], $tformat); - $start_short = day_short_translate( + $start_short = L10n::getDayShort( $item['event-adjust'] ? DateTimeFormat::local($item['event-start'], $dformat_short) : DateTimeFormat::utc($item['event-start'], $dformat_short) @@ -847,13 +877,13 @@ class Event extends BaseObject // If the option 'nofinisch' isn't set, we need to format the finish date/time. if (!$item['event-nofinish']) { $finish = true; - $dtend_dt = day_translate( + $dtend_dt = L10n::getDay( $item['event-adjust'] ? DateTimeFormat::local($item['event-finish'], $dformat) : DateTimeFormat::utc($item['event-finish'], $dformat) ); $dtend_title = DateTimeFormat::utc($item['event-finish'], $item['event-adjust'] ? DateTimeFormat::ATOM : 'Y-m-d\TH:i:s'); - $end_short = day_short_translate( + $end_short = L10n::getDayShort( $item['event-adjust'] ? DateTimeFormat::local($item['event-finish'], $dformat_short) : DateTimeFormat::utc($item['event-finish'], $dformat_short) @@ -865,6 +895,11 @@ class Event extends BaseObject if (substr($dtstart_title, 0, 10) === substr($dtend_title, 0, 10)) { $same_date = true; } + } else { + $dtend_title = ''; + $dtend_dt = ''; + $end_time = ''; + $end_short = ''; } // Format the event location. @@ -873,8 +908,8 @@ class Event extends BaseObject // Construct the profile link (magic-auth). $profile_link = Contact::magicLinkById($item['author-id']); - $tpl = get_markup_template('event_stream_item.tpl'); - $return = replace_macros($tpl, [ + $tpl = Renderer::getMarkupTemplate('event_stream_item.tpl'); + $return = Renderer::replaceMacros($tpl, [ '$id' => $item['event-id'], '$title' => prepare_text($item['event-summary']), '$dtstart_label' => L10n::t('Starts:'), @@ -917,6 +952,7 @@ class Event extends BaseObject * 'name' => The name of the location,
* 'address' => The address of the location,
* 'coordinates' => Latitude‎ and longitude‎ (e.g. '48.864716,2.349014').
+ * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ private static function locationToArray($s = '') { if ($s == '') { @@ -954,4 +990,48 @@ class Event extends BaseObject return $location; } + + /** + * @brief Add new birthday event for this person + * + * @param array $contact Contact array, expects: id, uid, url, name + * @param string $birthday Birthday of the contact + * @return bool + * @throws \Exception + */ + public static function createBirthday($contact, $birthday) + { + // Check for duplicates + $condition = [ + 'uid' => $contact['uid'], + 'cid' => $contact['id'], + 'start' => DateTimeFormat::utc($birthday), + 'type' => 'birthday' + ]; + if (DBA::exists('event', $condition)) { + return false; + } + + /* + * Add new birthday event for this person + * + * summary is just a readable placeholder in case the event is shared + * with others. We will replace it during presentation to our $importer + * to contain a sparkle link and perhaps a photo. + */ + $values = [ + 'uid' => $contact['uid'], + 'cid' => $contact['id'], + 'start' => DateTimeFormat::utc($birthday), + 'finish' => DateTimeFormat::utc($birthday . ' + 1 day '), + 'summary' => L10n::t('%s\'s birthday', $contact['name']), + 'desc' => L10n::t('Happy Birthday %s', ' [url=' . $contact['url'] . ']' . $contact['name'] . '[/url]'), + 'type' => 'birthday', + 'adjust' => 0 + ]; + + self::store($values); + + return true; + } }