diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/Contact.php | 4 | ||||
-rw-r--r-- | include/api.php | 6 | ||||
-rw-r--r-- | include/bbcode.php | 3 | ||||
-rw-r--r-- | include/contact_widgets.php | 40 | ||||
-rw-r--r-- | include/conversation.php | 10 | ||||
-rw-r--r-- | include/datetime.php | 54 | ||||
-rw-r--r-- | include/diaspora.php | 81 | ||||
-rw-r--r-- | include/items.php | 58 | ||||
-rw-r--r-- | include/nav.php | 5 | ||||
-rw-r--r-- | include/notifier.php | 28 | ||||
-rw-r--r-- | include/poller.php | 5 | ||||
-rw-r--r-- | include/text.php | 34 |
12 files changed, 280 insertions, 48 deletions
diff --git a/include/Contact.php b/include/Contact.php index 45920041e..45fd368d8 100644 --- a/include/Contact.php +++ b/include/Contact.php @@ -46,6 +46,10 @@ function contact_remove($id) { q("DELETE FROM `event` WHERE `cid` = %d ", intval($id) ); + q("DELETE FROM `queue` WHERE `cid` = %d ", + intval($id) + ); + } diff --git a/include/api.php b/include/api.php index 74b4aaf6e..1334d8eae 100644 --- a/include/api.php +++ b/include/api.php @@ -483,11 +483,7 @@ $txt = requestdata('htmlstatus'); if((strpos($txt,'<') !== false) || (strpos($txt,'>') !== false)) { - $txt = preg_replace('#<object[^>]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s', - '[youtube]$1[/youtube]', $txt); - - $txt = preg_replace('#<iframe[^>].+?' . 'http://www.youtube.com/embed/([A-Za-z0-9\-_=]+).+?</iframe>#s', - '[youtube]$1[/youtube]', $txt); + $txt = html2bb_video($txt); $config = HTMLPurifier_Config::createDefault(); $config->set('Cache.DefinitionImpl', null); diff --git a/include/bbcode.php b/include/bbcode.php index 9abc7c439..b33be686e 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -141,6 +141,9 @@ function bbcode($Text,$preserve_nl = false) { $Text = preg_replace("/\[youtube\]([A-Za-z0-9\-_=]+)(.*?)\[\/youtube\]/ism", '<iframe width="425" height="350" src="http://www.youtube.com/embed/$1" frameborder="0" ></iframe>', $Text); } + $Text = preg_replace("/\[vimeo\]https?:\/\/player.vimeo.com\/video\/([0-9]+)(.*?)\[\/vimeo\]/ism",'[vimeo]$1[/vimeo]',$Text); + $Text = preg_replace("/\[vimeo\]https?:\/\/vimeo.com\/([0-9]+)(.*?)\[\/vimeo\]/ism",'[vimeo]$1[/vimeo]',$Text); + $Text = preg_replace("/\[vimeo\]([0-9]+)(.*?)\[\/vimeo\]/ism", '<iframe width="425" height="350" src="http://player.vimeo.com/video/$1" frameborder="0" ></iframe>', $Text); // $Text = preg_replace("/\[youtube\](.*?)\[\/youtube\]/", '<object width="425" height="350" type="application/x-shockwave-flash" data="http://www.youtube.com/v/$1" ><param name="movie" value="http://www.youtube.com/v/$1"></param><!--[if IE]><embed src="http://www.youtube.com/v/$1" type="application/x-shockwave-flash" width="425" height="350" /><![endif]--></object>', $Text); diff --git a/include/contact_widgets.php b/include/contact_widgets.php new file mode 100644 index 000000000..efb833aaf --- /dev/null +++ b/include/contact_widgets.php @@ -0,0 +1,40 @@ +<?php + +function follow_widget() { + + return replace_macros(get_markup_template('follow.tpl'),array( + '$connect' => t('Add New Contact'), + '$desc' => t('Enter address or web location'), + '$hint' => t('Example: bob@example.com, http://example.com/barbara'), + '$follow' => t('Connect') + )); + +} + +function findpeople_widget() { + + $a = get_app(); + + $inv = (($a->config['register_policy'] != REGISTER_CLOSED) ? t('Invite Friends') : ''); + + if(get_config('system','invitation_only')) { + $x = get_pconfig(local_user(),'system','invites_remaining'); + if($x || is_site_admin()) { + $a->page['aside'] .= '<div class="side-link" id="side-invite-remain">' + . sprintf( tt('%d invitation available','%d invitations available',$x), $x) + . '</div>' . $inv; + } + } + + return replace_macros(get_markup_template('peoplefind.tpl'),array( + '$findpeople' => t('Find People'), + '$desc' => t('Enter name or interest'), + '$label' => t('Connect/Follow'), + '$hint' => t('Examples: Robert Morgenstein, Fishing'), + '$findthem' => t('Find'), + '$similar' => t('Similar Interests'), + '$inv' => $inv + )); + +} + diff --git a/include/conversation.php b/include/conversation.php index 18f6d080b..29fa77902 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -745,9 +745,8 @@ function status_editor($a,$x, $notes_cid = 0) { '$nickname' => $x['nickname'], '$ispublic' => t('Visible to <strong>everybody</strong>'), '$linkurl' => t('Please enter a link URL:'), - '$utubeurl' => t('Please enter a YouTube link:'), - '$vidurl' => t("Please enter a video\x28.ogg\x29 link/URL:"), - '$audurl' => t("Please enter an audio\x28.ogg\x29 link/URL:"), + '$vidurl' => t("Please enter a video link/URL:"), + '$audurl' => t("Please enter an audio link/URL:"), '$whereareu' => t('Where are you right now?'), '$title' => t('Enter a title for this item') )); @@ -795,9 +794,8 @@ function status_editor($a,$x, $notes_cid = 0) { '$upload' => t('Upload photo'), '$attach' => t('Attach file'), '$weblink' => t('Insert web link'), - '$youtube' => t('Insert YouTube video'), - '$video' => t('Insert Vorbis [.ogg] video'), - '$audio' => t('Insert Vorbis [.ogg] audio'), + '$video' => t('Insert video link'), + '$audio' => t('Insert audio link'), '$setloc' => t('Set your location'), '$noloc' => t('Clear browser location'), '$title' => t('Set title'), diff --git a/include/datetime.php b/include/datetime.php index 3033b88af..087e6cb20 100644 --- a/include/datetime.php +++ b/include/datetime.php @@ -393,3 +393,57 @@ function cal($y = 0,$m = 0, $links = false, $class='') { return $o; }} + + +function update_contact_birthdays() { + + // This only handles foreign or alien networks where a birthday has been provided. + // In-network birthdays are handled within local_delivery + + $r = q("SELECT * FROM contact WHERE `bd` != '' AND `bd` != '0000-00-00' AND SUBSTRING(`bd`,1,4) != `bdyear` "); + if(count($r)) { + foreach($r as $rr) { + + logger('update_contact_birthday: ' . $rr['bd']); + + $nextbd = datetime_convert('UTC','UTC','now','Y') . substr($rr['bd'],4); + + /** + * + * Add new birthday event for this person + * + * $bdtext 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. + * + */ + + $bdtext = t('Birthday:') . ' [url=' . $rr['url'] . ']' . $rr['name'] . '[/url]' ; + + + $r = q("INSERT INTO `event` (`uid`,`cid`,`created`,`edited`,`start`,`finish`,`desc`,`type`,`adjust`) + VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s', '%d' ) ", + intval($rr['uid']), + intval($rr['id']), + dbesc(datetime_convert()), + dbesc(datetime_convert()), + dbesc(datetime_convert('UTC','UTC', $nextbd)), + dbesc(datetime_convert('UTC','UTC', $nextbd . ' + 1 day ')), + dbesc($bdtext), + dbesc('birthday'), + intval(0) + ); + + + // update bdyear + + q("UPDATE `contact` SET `bdyear` = '%s', `bd` = '%s' WHERE `uid` = %d AND `id` = %d LIMIT 1", + dbesc(substr($nextbd,0,4)), + dbesc($nextbd), + intval($rr['uid']), + intval($rr['id']) + ); + + } + } +}
\ No newline at end of file diff --git a/include/diaspora.php b/include/diaspora.php index 968aa2fd2..2d9e2883d 100644 --- a/include/diaspora.php +++ b/include/diaspora.php @@ -38,6 +38,9 @@ function diaspora_dispatch($importer,$msg) { elseif($xmlbase->status_message) { $ret = diaspora_post($importer,$xmlbase->status_message); } + elseif($xmlbase->profile) { + $ret = diaspora_profile($importer,$xmlbase->profile); + } elseif($xmlbase->comment) { $ret = diaspora_comment($importer,$xmlbase->comment,$msg); } @@ -995,6 +998,84 @@ function diaspora_retraction($importer,$xml) { // NOTREACHED } +function diaspora_profile($importer,$xml) { + + $a = get_app(); + $diaspora_handle = notags(unxmlify($xml->diaspora_handle)); + + $contact = diaspora_get_contact_by_handle($importer['uid'],$diaspora_handle); + if(! $contact) + return; + + if($contact['blocked']) { + logger('diaspora_post: Ignoring this author.'); + return 202; + } + + $name = unxmlify($xml->first_name) . ((strlen($xml->last_name)) ? ' ' . unxmlify($xml->last_name) : ''); + $image_url = unxmlify($xml->image_url); + $birthday = unxmlify($xml->birthday); + + $r = q("SELECT DISTINCT ( `resource-id` ) FROM `photo` WHERE `uid` = %d AND `contact-id` = %d AND `album` = 'Contact Photos' ", + intval($importer['uid']), + intval($contact['id']) + ); + $oldphotos = ((count($r)) ? $r : null); + + $images = import_profile_photo($image_url,$importer['uid'],$contact['id']); + + // Generic birthday. We don't know the timezone. The year is irrelevant. + + $birthday = datetime_convert('UTC','UTC',$birthday,'Y-m-d'); + + $r = q("UPDATE `contact` SET `name` = '%s', `name-date` = '%s', `photo` = '%s', `thumb` = '%s', `micro` = '%s', `avatar-date` = '%s' , `bd` = '%s' WHERE `id` = %d AND `uid` = %d LIMIT 1", + dbesc($name), + dbesc(datetime_convert()), + dbesc($images[0]), + dbesc($images[1]), + dbesc($images[2]), + dbesc(datetime_convert()), + intval($contact['id']), + intval($importer['uid']), + dbesc($birthday) + ); + if($r) { + if($oldphotos) { + foreach($oldphotos as $ph) { + q("DELETE FROM `photo` WHERE `uid` = %d AND `contact-id` = %d AND `album` = 'Contact Photos' AND `resource-id` = '%s' ", + intval($importer['uid']), + intval($contact['id']), + dbesc($ph['resource-id']) + ); + } + } + } + + return; + +} + + + + + + + + + + + + + + + + + + + + + + function diaspora_share($me,$contact) { $a = get_app(); $myaddr = $me['nickname'] . '@' . substr($a->get_baseurl(), strpos($a->get_baseurl(),'://') + 3); diff --git a/include/items.php b/include/items.php index 8360a42ec..0c3c8b1d7 100644 --- a/include/items.php +++ b/include/items.php @@ -414,11 +414,7 @@ function get_atom_elements($feed,$item) { if((strpos($res['body'],'<') !== false) || (strpos($res['body'],'>') !== false)) { - $res['body'] = preg_replace('#<object[^>]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s', - '[youtube]$1[/youtube]', $res['body']); - - $res['body'] = preg_replace('#<iframe[^>].+?' . 'http://www.youtube.com/embed/([A-Za-z0-9\-_=]+).+?</iframe>#s', - '[youtube]$1[/youtube]', $res['body']); + $res['body'] = html2bb_video($res['body']); $res['body'] = oembed_html2bbcode($res['body']); @@ -586,12 +582,7 @@ function get_atom_elements($feed,$item) { $res['object'] .= '<orig>' . xmlify($body) . '</orig>' . "\n"; if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) { - $body = preg_replace('#<object[^>]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s', - '[youtube]$1[/youtube]', $body); - - $res['body'] = preg_replace('#<iframe[^>].+?' . 'http://www.youtube.com/embed/([A-Za-z0-9\-_=]+).+?</iframe>#s', - '[youtube]$1[/youtube]', $res['body']); - + $body = html2bb_video($body); $config = HTMLPurifier_Config::createDefault(); $config->set('Cache.DefinitionImpl', null); @@ -629,11 +620,7 @@ function get_atom_elements($feed,$item) { $res['object'] .= '<orig>' . xmlify($body) . '</orig>' . "\n"; if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) { - $body = preg_replace('#<object[^>]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s', - '[youtube]$1[/youtube]', $body); - - $res['body'] = preg_replace('#<iframe[^>].+?' . 'http://www.youtube.com/embed/([A-Za-z0-9\-_=]+).+?</iframe>#s', - '[youtube]$1[/youtube]', $res['body']); + $body = html2bb_video($body); $config = HTMLPurifier_Config::createDefault(); $config->set('Cache.DefinitionImpl', null); @@ -690,7 +677,7 @@ function item_store($arr,$force_parent = false) { if($arr['gravity']) $arr['gravity'] = intval($arr['gravity']); - elseif($arr['parent-uri'] == $arr['uri']) + elseif($arr['parent-uri'] === $arr['uri']) $arr['gravity'] = 0; elseif(activity_match($arr['verb'],ACTIVITY_POST)) $arr['gravity'] = 6; @@ -757,7 +744,7 @@ function item_store($arr,$force_parent = false) { // find the parent and snarf the item id and ACL's // and anything else we need to inherit - $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d ORDER BY `id` ASC LIMIT 1", dbesc($arr['parent-uri']), intval($arr['uid']) ); @@ -771,7 +758,8 @@ function item_store($arr,$force_parent = false) { if($r[0]['uri'] != $r[0]['parent-uri']) { $arr['thr-parent'] = $arr['parent-uri']; $arr['parent-uri'] = $r[0]['parent-uri']; - $z = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `parent-uri` = '%s' AND `uid` = %d LIMIT 1", + $z = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `parent-uri` = '%s' AND `uid` = %d + ORDER BY `id` ASC LIMIT 1", dbesc($r[0]['parent-uri']), dbesc($r[0]['parent-uri']), intval($arr['uid']) @@ -809,7 +797,7 @@ function item_store($arr,$force_parent = false) { $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", dbesc($arr['uri']), - dbesc($arr['uid']) + intval($arr['uid']) ); if($r && count($r)) { logger('item-store: duplicate item ignored. ' . print_r($arr,true)); @@ -830,18 +818,10 @@ function item_store($arr,$force_parent = false) { // find the item we just created - $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", + $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d ORDER BY `id` ASC ", $arr['uri'], // already dbesc'd intval($arr['uid']) ); - if(! count($r)) { - // This is not good, but perhaps we encountered a rare race/cache condition, so back off and try again. - sleep(3); - $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1", - $arr['uri'], // already dbesc'd - intval($arr['uid']) - ); - } if(count($r)) { $current_post = $r[0]['id']; @@ -851,6 +831,14 @@ function item_store($arr,$force_parent = false) { logger('item_store: could not locate created item'); return 0; } + if(count($r) > 1) { + logger('item_store: duplicated post occurred. Removing duplicates.'); + q("DELETE FROM `item` WHERE `uri` = '%s' AND `uid` = %d AND `id` != %d ", + $arr['uri'], + intval($arr['uid']), + intval($current_post) + ); + } if((! $parent_id) || ($arr['parent-uri'] === $arr['uri'])) $parent_id = $current_post; @@ -1877,13 +1865,14 @@ function local_delivery($importer,$data) { $r = q("select `item`.`id`, `contact`.`name`, `contact`.`url`, `contact`.`thumb` from `item` LEFT JOIN `contact` ON `contact`.`id` = `item`.`contact-id` - WHERE `contact`.`self` = 1 AND `item`.`wall` = 1 AND `item`.`uri` = '%s' AND `item`.`uid` = %d LIMIT 1", + WHERE `contact`.`self` = 1 AND `item`.`wall` = 1 AND `item`.`uri` = '%s' AND `item`.`parent-uri` = '%s' + AND `item`.`uid` = %d LIMIT 1", + dbesc($parent_uri), dbesc($parent_uri), intval($importer['importer_uid']) ); if($r && count($r)) { - logger('local_delivery: received remote comment'); $is_like = false; // remote reply to our post. Import and then notify everybody else. @@ -2392,6 +2381,9 @@ function atom_entry($item,$type,$author,$owner,$comment = false) { $a = get_app(); + if(! $item['parent']) + return; + if($item['deleted']) return '<at:deleted-entry ref="' . xmlify($item['uri']) . '" when="' . xmlify(datetime_convert('UTC','UTC',$item['edited'] . '+00:00',ATOM_TIME)) . '" />' . "\r\n"; @@ -2411,8 +2403,8 @@ function atom_entry($item,$type,$author,$owner,$comment = false) { if(strlen($item['owner-name'])) $o .= atom_author('dfrn:owner',$item['owner-name'],$item['owner-link'],80,80,$item['owner-avatar']); - if($item['parent'] != $item['id']) - $o .= '<thr:in-reply-to ref="' . xmlify($item['parent-uri']) . '" type="text/html" href="' . xmlify($a->get_baseurl() . '/display/' . $owner['nickname'] . '/' . $item['id']) . '" />' . "\r\n"; + if(($item['parent'] != $item['id']) || ($item['parent-uri'] !== $item['uri'])) + $o .= '<thr:in-reply-to ref="' . xmlify($item['parent-uri']) . '" type="text/html" href="' . xmlify($a->get_baseurl() . '/display/' . $owner['nickname'] . '/' . $item['parent']) . '" />' . "\r\n"; $o .= '<id>' . xmlify($item['uri']) . '</id>' . "\r\n"; $o .= '<title>' . xmlify($item['title']) . '</title>' . "\r\n"; diff --git a/include/nav.php b/include/nav.php index 600043ee5..5c24df51e 100644 --- a/include/nav.php +++ b/include/nav.php @@ -114,7 +114,9 @@ function nav(&$a) { /* only show friend requests for normal pages. Other page types have automatic friendship. */ if($_SESSION['page_flags'] == PAGE_NORMAL) { - $nav['notifications'] = array('notifications/network', t('Notifications'), "", t('Notifications')); + $nav['introductions'] = array('notifications/intros', t('Introductions'), "", t('Friend Requests')); + $nav['notifications'] = array('notifications', t('Notifications'), "", t('Notifications')); + } $nav['messages'] = array('message', t('Messages'), "", t('Private mail')); @@ -175,6 +177,7 @@ function nav_set_selected($item){ 'network' => null, 'home' => null, 'profiles' => null, + 'introductions' => null, 'notifications' => null, 'messages' => null, 'directory' => null, diff --git a/include/notifier.php b/include/notifier.php index 90111600a..18ad07012 100644 --- a/include/notifier.php +++ b/include/notifier.php @@ -142,7 +142,7 @@ function notifier_run($argv, $argc){ $item['deleted'] = 1; } - if((count($items) == 1) && ($items[0]['uri'] === $items[0]['parent-uri'])) { + if((count($items) == 1) && ($items[0]['id'] === $target_item['id']) && ($items[0]['uri'] === $items[0]['parent-uri'])) { logger('notifier: top level post'); $top_level = true; } @@ -192,6 +192,8 @@ function notifier_run($argv, $argc){ // be notified during this run. // Other DFRN conversation members will be alerted during polled updates. + + // Diaspora members currently are not notified of expirations, and other networks have // either limited or no ability to process deletions. We should at least fix Diaspora // by stringing togther an array of retractions and sending them onward. @@ -203,13 +205,21 @@ function notifier_run($argv, $argc){ /** * - * Be VERY CAREFUL if you make any changes to the following line. Seemingly innocuous changes + * Be VERY CAREFUL if you make any changes to the following lines. Seemingly innocuous changes * have been known to cause runaway conditions which affected several servers, along with * permissions issues. * */ + $relay_to_owner = false; + if((! $top_level) && ($parent['wall'] == 0) && (! $expire) && (stristr($target_item['uri'],$localhost))) { + $relay_to_owner = true; + } + + + if($relay_to_owner) { + logger('notifier: followup', LOGGER_DEBUG); // local followup to remote post $followup = true; $public_message = false; // not public @@ -218,6 +228,13 @@ function notifier_run($argv, $argc){ else { $followup = false; + // don't send deletions onward for other people's stuff + + if($target_item['deleted'] && (! intval($target_item['wall']))) { + logger('notifier: ignoring delete notification for non-wall item'); + return; + } + if((strlen($parent['allow_cid'])) || (strlen($parent['allow_gid'])) || (strlen($parent['deny_cid'])) @@ -357,6 +374,7 @@ function notifier_run($argv, $argc){ if(($public_message) && $item['private']) continue; + $contact = get_item_contact($item,$contacts); if(! $contact) @@ -779,6 +797,12 @@ function notifier_run($argv, $argc){ } + if($normal_mode) + call_hooks('notifier_normal',$target_item); + + call_hooks('notifier_end',$target_item); + + return; } diff --git a/include/poller.php b/include/poller.php index 427f8887c..cef0647b5 100644 --- a/include/poller.php +++ b/include/poller.php @@ -50,12 +50,15 @@ function poller_run($argv, $argc){ - // once daily run expire in background + // once daily run birthday_updates and then expire in background $d1 = get_config('system','last_expire_day'); $d2 = intval(datetime_convert('UTC','UTC','now','d')); if($d2 != intval($d1)) { + + update_contact_birthdays(); + set_config('system','last_expire_day',$d2); proc_run('php','include/expire.php'); } diff --git a/include/text.php b/include/text.php index fd413b7d6..299410a63 100644 --- a/include/text.php +++ b/include/text.php @@ -1003,3 +1003,37 @@ if (!function_exists('str_getcsv')) { } } } + +function cleardiv() { + return '<div class="clear"></div>'; +} + + +function bb_translate_video($s) { + + $matches = null; + $r = preg_match_all("/\[video\](.*?)\[\/video\]/ism",$s,$matches,PREG_SET_ORDER); + if($r) { + foreach($matches as $mtch) { + if((stristr($mtch[1],'youtube')) || (stristr($mtch[1],'youtu.be'))) + $s = str_replace($mtch[0],'[youtube]' . $mtch[1] . '[/youtube]',$s); + elseif(stristr($mtch[1],'vimeo')) + $s = str_replace($mtch[0],'[vimeo]' . $mtch[1] . '[/vimeo]',$s); + } + } + return $s; +} + +function html2bb_video($s) { + + $s = preg_replace('#<object[^>]+>(.*?)https+://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+)(.*?)</object>#ism', + '[youtube]$2[/youtube]', $s); + + $s = preg_replace('#<iframe[^>](.*?)https+://www.youtube.com/embed/([A-Za-z0-9\-_=]+)(.*?)</iframe>#ism', + '[youtube]$2[/youtube]', $s); + + $s = preg_replace('#<iframe[^>](.*?)https+://player.vimeo.com/video/([0-9]+)(.*?)</iframe>#ism', + '[vimeo]$2[/vimeo]', $s); + + return $s; +}
\ No newline at end of file |