diff --git a/Code/Lib/Activity.php b/Code/Lib/Activity.php index 73d530055..ee74b3478 100644 --- a/Code/Lib/Activity.php +++ b/Code/Lib/Activity.php @@ -343,7 +343,7 @@ class Activity $numpages = $total / App::$pager['itemspage']; $lastpage = (($numpages > intval($numpages)) ? intval($numpages) + 1 : $numpages); - $stripped = preg_replace('/([&|\?]page=[0-9]*)/', '', $id); + $stripped = preg_replace('/([&|?]page=[0-9]*)/', '', $id); $stripped = rtrim($stripped, '/'); $ret['partOf'] = z_root() . '/' . $stripped; @@ -790,15 +790,14 @@ class Activity if ($i['app']) { $ret['generator'] = ['type' => 'Application', 'name' => $i['app']]; } - if ($i['location'] || $i['coord']) { + if ($i['location'] || $i['lat'] || $i['lon']) { $ret['location'] = ['type' => 'Place']; if ($i['location']) { $ret['location']['name'] = $i['location']; } - if ($i['coord']) { - $l = explode(' ', $i['coord']); - $ret['location']['latitude'] = $l[0]; - $ret['location']['longitude'] = $l[1]; + if ($i['lat'] || $i['lon']) { + $ret['location']['latitude'] = $i['lat'] ?? 0; + $ret['location']['longitude'] = $i['lon'] ?? 0; } } @@ -1126,7 +1125,7 @@ class Activity $images = false; - $has_images = preg_match_all('/\[[zi]mg(.*?)\](.*?)\[/ism', $i['body'], $images, PREG_SET_ORDER); + $has_images = preg_match_all('/\[[zi]mg(.*?)](.*?)\[/ism', $i['body'], $images, PREG_SET_ORDER); $ret['id'] = $i['mid']; @@ -1150,18 +1149,16 @@ class Activity if ($i['app']) { $ret['generator'] = ['type' => 'Application', 'name' => $i['app']]; } - if ($i['location'] || $i['coord']) { + if ($i['location'] || $i['lat'] || $i['lon']) { $ret['location'] = ['type' => 'Place']; if ($i['location']) { $ret['location']['name'] = $i['location']; } - if ($i['coord']) { - $l = explode(' ', $i['coord']); - $ret['location']['latitude'] = $l[0]; - $ret['location']['longitude'] = $l[1]; + if ($i['lat'] || $i['lon']) { + $ret['location']['latitude'] = $i['lat'] ?? 0; + $ret['location']['longitude'] = $i['lon'] ?? 0; } } - if (intval($i['item_wall']) && $i['mid'] === $i['parent_mid']) { $ret['commentPolicy'] = $i['comment_policy']; } @@ -3121,9 +3118,9 @@ class Activity if (array_key_exists('content', $location)) { $s['location'] = html2plain(purify_html($location['content']), 256); } - if (array_key_exists('latitude', $location) && array_key_exists('longitude', $location)) { - $s['coord'] = escape_tags($location['latitude']) . ' ' . escape_tags($location['longitude']); + $s['lat'] = floatval($location['latitude']); + $s['lon'] = floatval($location['longitude']); } } diff --git a/Code/Lib/ActivityPub.php b/Code/Lib/ActivityPub.php index 6b511257b..625c58f83 100644 --- a/Code/Lib/ActivityPub.php +++ b/Code/Lib/ActivityPub.php @@ -503,6 +503,122 @@ class ActivityPub return false; } + public static function copy($src, $dst) + { + if (!($src && $dst)) { + return; + } + + if ($src && !is_array($src)) { + $src = Activity::fetch($src); + if (is_array($src)) { + $src_xchan = $src['id']; + } + } + + $approvals = null; + + if ($dst && !is_array($dst)) { + $dst = Activity::fetch($dst); + if (is_array($dst)) { + $dst_xchan = $dst['id']; + if (array_key_exists('alsoKnownAs', $dst)) { + if (!is_array($dst['alsoKnownAs'])) { + $dst['alsoKnownAs'] = [$dst['alsoKnownAs']]; + } + $approvals = $dst['alsoKnownAs']; + } + } + } + + if (!($src_xchan && $dst_xchan)) { + return; + } + + if ($approvals) { + foreach ($approvals as $approval) { + if ($approval === $src_xchan) { + $abooks = q( + "select abook_channel from abook where abook_xchan = '%s'", + dbesc($src_xchan) + ); + if ($abooks) { + foreach ($abooks as $abook) { + // check to see if we already performed this action + $x = q( + "select * from abook where abook_xchan = '%s' and abook_channel = %d", + dbesc($dst_xchan), + intval($abook['abook_channel']) + ); + if ($x) { + continue; + } + // update the local abook + $abc = q("select * from abconfig where chan = %d and xchan = '%s'", + intval($abook['abook_channel']), + dbesc($src_xchan) + ); + if ($abc) { + foreach ($abc as $abcentry) { + q("insert into abconfig (chan, xchan, cat, k, v) values ( %d, '%s', '%s', '%s', '%s')", + intval($abcentry['chan']), + dbesc($dst_xchan), + dbesc($abcentry['cat']), + dbesc($abcentry['k']), + dbesc($abcentry['v']) + ); + } + } + $pg = q("select * from pgrp_member where uid = %d and xchan = '%s'", + intval($abook['abook_channel']), + dbesc($src_xchan) + ); + if ($pg) { + foreach ($pg as $pgentry) { + q("insert into pgrp_member (uid, gid, xchan) values (%d, %d, '%s')", + intval($pgentry['uid']), + intval($pgentry['gid']), + dbesc($dst_xchan) + ); + } + } + $ab = q("select * from abook where abook_channel = %d and abook_xchan = '%s'", + intval($abook['abook_channel']), + dbesc($src_xchan) + ); + if ($ab) { + $ab = array_shift($ab); + unset($ab['abook_id']); + $ab['abook_xchan'] = $dst_xchan; + $ab['abook_created'] = $ab['abook_updated'] = $ab['abook_connected'] = datetime_convert(); + abook_store_lowlevel($ab); + } + + $r = q( + "SELECT abook.*, xchan.* + FROM abook left join xchan on abook_xchan = xchan_hash + WHERE abook_channel = %d and abook_id = %d LIMIT 1", + intval($abook['abook_channel']), + intval($dst_xchan) + ); + if ($r) { + $clone = array_shift($r); + unset($clone['abook_id']); + unset($clone['abook_account']); + unset($clone['abook_channel']); + $abconfig = load_abconfig($abook['abook_channel'], $clone['abook_xchan']); + if ($abconfig) { + $clone['abconfig'] = $abconfig; + } + Libsync::build_sync_packet($abook['abook_channel'], ['abook' => [$clone]]); + } + } + } + } + } + } + } + public static function move($src, $dst) { diff --git a/Code/Lib/Resizer.php b/Code/Lib/Resizer.php index b4bd2eb38..6c3b697a9 100644 --- a/Code/Lib/Resizer.php +++ b/Code/Lib/Resizer.php @@ -25,7 +25,7 @@ class Resizer } } - private function constructDimension($max) + private function constructDimension($max): bool|string { if (!isset($this->getimagesize)) { return false; @@ -36,7 +36,7 @@ class Resizer return false; } - public function resize($infile,$outfile,$max_size = self::DEFAULT_MAX_SIZE) + public function resize($infile,$outfile,$max_size = self::DEFAULT_MAX_SIZE): bool { $dim = $this->constructDimension($max_size); if ($dim && $this->converter_path) { diff --git a/Code/Module/Inbox.php b/Code/Module/Inbox.php index c6ad19fb8..4d4745993 100644 --- a/Code/Module/Inbox.php +++ b/Code/Module/Inbox.php @@ -343,6 +343,51 @@ class Inbox extends Controller if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && (ActivityStreams::is_an_actor($AS->obj['type']) || $AS->obj['type'] === 'Member')) { break; } + case 'Undo': + if ($AS->obj && is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Follow') { + // do unfollow activity + Activity::unfollow($channel, $AS); + break; + } + case 'Leave': + if ($AS->obj && is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Group') { + // do unfollow activity + Activity::unfollow($channel, $AS); + break; + } + case 'Tombstone': + case 'Delete': + Activity::drop($channel, $observer_hash, $AS); + break; + case 'Copy': + if ( + $observer_hash && $observer_hash === $AS->actor + && is_array($AS->obj) && array_key_exists('type', $AS->obj) && ActivityStream::is_an_actor($AS->obj['type']) + && is_array($AS->tgt) && array_key_exists('type', $AS->tgt) && ActivityStream::is_an_actor($AS->tgt['type']) + ) { + ActivityPub::copy($AS->obj, $AS->tgt); + break; + } + case 'Move': + if ( + $observer_hash && $observer_hash === $AS->actor + && is_array($AS->obj) && array_key_exists('type', $AS->obj) && ActivityStream::is_an_actor($AS->obj['type']) + && is_array($AS->tgt) && array_key_exists('type', $AS->tgt) && ActivityStream::is_an_actor($AS->tgt['type']) + ) { + ActivityPub::move($AS->obj, $AS->tgt); + break; + } + case 'Add': + case 'Remove': + // for writeable collections as target, it's best to provide an array and include both the type and the id in the target element. + // If it's just a string id, we'll try to fetch the collection when we receive it and that's wasteful since we don't actually need + // the contents. + if (is_array($AS->obj) && isset($AS->tgt)) { + // The boolean flag enables html cache of the item + $item = Activity::decode_note($AS, true); + break; + } + case 'Create': case 'Like': case 'Dislike': @@ -374,42 +419,6 @@ class Inbox extends Controller logger('unresolved object: ' . print_r($AS->obj, true)); } break; - case 'Undo': - if ($AS->obj && is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Follow') { - // do unfollow activity - Activity::unfollow($channel, $AS); - break; - } - case 'Leave': - if ($AS->obj && is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Group') { - // do unfollow activity - Activity::unfollow($channel, $AS); - break; - } - case 'Tombstone': - case 'Delete': - Activity::drop($channel, $observer_hash, $AS); - break; - - case 'Move': - if ( - $observer_hash && $observer_hash === $AS->actor - && is_array($AS->obj) && array_key_exists('type', $AS->obj) && ActivityStream::is_an_actor($AS->obj['type']) - && is_array($AS->tgt) && array_key_exists('type', $AS->tgt) && ActivityStream::is_an_actor($AS->tgt['type']) - ) { - ActivityPub::move($AS->obj, $AS->tgt); - } - break; - case 'Add': - case 'Remove': - // for writeable collections as target, it's best to provide an array and include both the type and the id in the target element. - // If it's just a string id, we'll try to fetch the collection when we receive it and that's wasteful since we don't actually need - // the contents. - if (is_array($AS->obj) && isset($AS->tgt)) { - // The boolean flag enables html cache of the item - $item = Activity::decode_note($AS, true); - break; - } default: break; } diff --git a/Code/Module/Item.php b/Code/Module/Item.php index 5d67c8358..4314f3c8b 100644 --- a/Code/Module/Item.php +++ b/Code/Module/Item.php @@ -393,7 +393,7 @@ class Item extends Controller $post_tags = false; $pub_copy = false; - + logger('args: ' . print_r($_REQUEST,true)); /** * Is this a reply to something? */ @@ -816,7 +816,8 @@ class Item extends Controller } $location = $orig_post['location']; - $coord = $orig_post['coord']; + $lat = $orig_post['lat']; + $lon = $orig_post['lon']; $verb = $orig_post['verb']; $app = $orig_post['app']; $title = escape_tags(trim($_REQUEST['title'])); @@ -876,7 +877,8 @@ class Item extends Controller $location = ((isset($_REQUEST['location'])) ? notags(trim($_REQUEST['location'])) : EMPTY_STR); - $coord = ((isset($_REQUEST['coord'])) ? notags(trim($_REQUEST['coord'])) : EMPTY_STR); + $lat = ((isset($_REQUEST['lat'])) ? floatval($_REQUEST['lat']) : 0.0); + $lon = ((isset($_REQUEST['lon'])) ? floatval($_REQUEST['lon']) : 0.0); $verb = ((isset($_REQUEST['verb'])) ? notags(trim($_REQUEST['verb'])) : EMPTY_STR); $title = ((isset($_REQUEST['title'])) ? escape_tags(trim($_REQUEST['title'])) : EMPTY_STR); $summary = ((isset($_REQUEST['summary'])) ? trim($_REQUEST['summary']) : EMPTY_STR); @@ -1438,7 +1440,8 @@ class Item extends Controller $datarray['body'] = $body; $datarray['app'] = $app; $datarray['location'] = $location; - $datarray['coord'] = $coord; + $datarray['lat'] = $lat; + $datarray['lon'] = $lon; $datarray['verb'] = $verb; $datarray['obj_type'] = $obj_type; $datarray['allow_cid'] = $str_contact_allow; @@ -1518,12 +1521,14 @@ class Item extends Controller $datarray['owner'] = $owner_xchan; $datarray['author'] = $observer; $datarray['attach'] = json_encode($datarray['attach']); + Hook::call('post_prestore', $datarray); $o = conversation([$datarray], 'search', false, 'preview'); // logger('preview: ' . $o, LOGGER_DEBUG); echo json_encode(['preview' => $o]); killme(); } + Hook::call('post_prestore', $datarray); // Let 'post_local' event listeners know if this is an edit. // We will unset it immediately afterward. diff --git a/Code/Module/Outbox.php b/Code/Module/Outbox.php index 57f35f702..51496647b 100644 --- a/Code/Module/Outbox.php +++ b/Code/Module/Outbox.php @@ -146,6 +146,31 @@ class Outbox extends Controller if (is_array($AS->obj) && array_key_exists('type', $AS->obj) && (ActivityStreams::is_an_actor($AS->obj['type']) || $AS->obj['type'] === 'Member')) { break; } + case 'Undo': + if ($AS->obj && is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Follow') { + // do unfollow activity + Activity::unfollow($channel, $AS); + break; + } + case 'Leave': + if ($AS->obj && is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Group') { + // do unfollow activity + Activity::unfollow($channel, $AS); + break; + } + case 'Tombstone': + case 'Delete': + Activity::drop($channel, $observer_hash, $AS); + break; + case 'Move': + if ( + $observer_hash && $observer_hash === $AS->actor + && is_array($AS->obj) && array_key_exists('type', $AS->obj) && ActivityStreams::is_an_actor($AS->obj['type']) + && is_array($AS->tgt) && array_key_exists('type', $AS->tgt) && ActivityStreams::is_an_actor($AS->tgt['type']) + ) { + ActivityPub::move($AS->obj, $AS->tgt); + break; + } case 'Create': case 'Like': case 'Dislike': @@ -176,31 +201,6 @@ class Outbox extends Controller logger('unresolved object: ' . print_r($AS->obj, true)); } break; - case 'Undo': - if ($AS->obj && is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Follow') { - // do unfollow activity - Activity::unfollow($channel, $AS); - break; - } - case 'Leave': - if ($AS->obj && is_array($AS->obj) && array_key_exists('type', $AS->obj) && $AS->obj['type'] === 'Group') { - // do unfollow activity - Activity::unfollow($channel, $AS); - break; - } - case 'Tombstone': - case 'Delete': - Activity::drop($channel, $observer_hash, $AS); - break; - case 'Move': - if ( - $observer_hash && $observer_hash === $AS->actor - && is_array($AS->obj) && array_key_exists('type', $AS->obj) && ActivityStreams::is_an_actor($AS->obj['type']) - && is_array($AS->tgt) && array_key_exists('type', $AS->tgt) && ActivityStreams::is_an_actor($AS->tgt['type']) - ) { - ActivityPub::move($AS->obj, $AS->tgt); - } - break; case 'Add': case 'Remove': default: diff --git a/Code/Module/Photos.php b/Code/Module/Photos.php index 8d3585749..a0d8054ab 100644 --- a/Code/Module/Photos.php +++ b/Code/Module/Photos.php @@ -1075,8 +1075,8 @@ class Photos extends Controller ); } - if ($link_item['coord'] && Apps::system_app_installed($owner_uid, 'Photomap')) { - $map = generate_map($link_item['coord']); + if (($link_item['lat'] || $link_item['lon']) && Apps::system_app_installed($owner_uid, 'Photomap')) { + $map = generate_map($link_item['lat'], $link_item['lon']); } } diff --git a/Code/Module/Settings/Channel.php b/Code/Module/Settings/Channel.php index fff36a744..87091a217 100644 --- a/Code/Module/Settings/Channel.php +++ b/Code/Module/Settings/Channel.php @@ -14,6 +14,7 @@ use Code\Lib\PermissionDescription; use Code\Access\AccessControl; use Code\Daemon\Run; use Code\Lib\Permcat; +use Code\Lib\PConfig; use Code\Lib\Libacl; use Code\Lib\Features; use Code\Lib\Menu; @@ -191,7 +192,20 @@ class Channel if (!isset($autoperms)) { $autoperms = ((x($_POST, 'autoperms')) ? intval($_POST['autoperms']) : 0); } - + $set_location = (isset($_POST['set_location']) ? trim($_POST['set_location']) : ''); + if ($set_location) { + $lat = false; + $lon = false; + $tmp = explode(',', $set_location); + if (count($tmp) > 1) { + $lat = floatval(trim($tmp[0])); + $lon = floatval(trim($tmp[1])); + } + $valid = $lat || $lon; + if ($valid) { + PConfig::Set(local_channel(),'system', 'set_location', (string) $lat . ',' . (string) $lon); + } + } $pageflags = $channel['channel_pageflags']; $existing_adult = (($pageflags & PAGE_ADULT) ? 1 : 0); @@ -631,7 +645,7 @@ class Channel '$timezone' => array('timezone_select', t('Your timezone'), $timezone, t('This is important for showing the correct time on shared events'), get_timezones()), '$defloc' => array('defloc', t('Default post location'), $defloc, t('Optional geographical location to display on your posts')), '$allowloc' => array('allow_location', t('Obtain post location from your web browser or device'), ((get_pconfig(local_channel(), 'system', 'use_browser_location')) ? 1 : ''), '', $yes_no), - + '$set_location' => [ 'set_location', t('Over-ride your web browser or device and use these coordinates (latitude,longitude)'), get_pconfig(local_channel(),'system','set_location')], '$adult' => array('adult', t('Adult content'), $adult_flag, t('Enable to indicate if this channel frequently or regularly publishes adult content. (Please also tag any adult material and/or nudity with #NSFW)'), $yes_no), '$h_prv' => t('Security and Privacy'), diff --git a/Code/Update/_1261.php b/Code/Update/_1261.php new file mode 100644 index 000000000..3667da7c8 --- /dev/null +++ b/Code/Update/_1261.php @@ -0,0 +1,48 @@ + 1) { + $lat = $tmp[0]; + $lon = $tmp[1]; + } // the extra space in the following line is intentional - return str_replace($match[0], '
' . generate_map(str_replace('/', ' ', $match[1])) . '
', $match[0]); + return str_replace($match[0], '
' . generate_map($lat,$lon) . '
', $match[0]); } function bb_map_location($match) diff --git a/include/conversation.php b/include/conversation.php index 1ab95131a..810182159 100644 --- a/include/conversation.php +++ b/include/conversation.php @@ -1216,12 +1216,21 @@ function z_status_editor($x, $popup = false) $feature_markup = false; } - + $lat = ''; + $lon = ''; $geotag = (($x['allow_location']) ? replace_macros(Theme::get_template('jot_geotag.tpl'), []) : ''); $setloc = t('Set your location'); - $clearloc = ((get_pconfig($x['profile_uid'], 'system', 'use_browser_location')) ? t('Clear browser location') : ''); + $clearloc = t('Clear your location'); + $set_location = get_pconfig($x['profile_uid'], 'system', 'set_location'); + if ($set_location) { + $tmp = explode(',', $set_location); + if (count($tmp) > 1) { + $lat = floatval(trim($tmp[0])); + $lon = floatval(trim($tmp[1])); + } + } if (x($x, 'hide_location')) { - $geotag = $setloc = $clearloc = ''; + $geotag = $setloc = $clearloc = $lat = $lon = ''; } $summaryenabled = ((array_key_exists('allow_summary', $x)) ? intval($x['allow_summary']) : false); @@ -1287,7 +1296,7 @@ function z_status_editor($x, $popup = false) '$nickname' => $x['nickname'], '$linkurl' => t('Please enter a link URL:'), '$term' => t('Tag term:'), - '$whereareu' => t('Where are you right now?'), + '$whereareu' => t('Where are you right now?') . ' ' . t('(Enter a dot . to use your current device coordinates.)'), '$editor_autocomplete' => ((x($x, 'editor_autocomplete')) ? $x['editor_autocomplete'] : ''), '$bbco_autocomplete' => ((x($x, 'bbco_autocomplete')) ? $x['bbco_autocomplete'] : ''), '$modalchooseimages' => t('Choose images to embed'), @@ -1448,6 +1457,8 @@ function z_status_editor($x, $popup = false) '$defcommpolicy' => $defcommpolicy, '$defcommuntil' => $defcommuntil, '$clearloc' => $clearloc, + '$lat' => $lat, + '$lon' => $lon, '$title' => ((x($x, 'title')) ? htmlspecialchars($x['title'], ENT_COMPAT, 'UTF-8') : ''), '$placeholdertitle' => ((x($x, 'placeholdertitle')) ? $x['placeholdertitle'] : t('Title (optional)')), '$catsenabled' => $catsenabled, @@ -1868,7 +1879,7 @@ function format_location($item) $location = substr($item['location'], 1); $location = ((strpos($location, '[') !== false) ? zidify_links(bbcode($location)) : $location); } else { - $locate = array('location' => $item['location'], 'coord' => $item['coord'], 'html' => ''); + $locate = array('location' => $item['location'], 'lat' => $item['lat'], 'lon' => $item['lon'], 'coord' => $item['coord'], 'html' => ''); Hook::call('render_location', $locate); $location = ((strlen($locate['html'])) ? $locate['html'] : render_location_default($locate)); } @@ -1879,16 +1890,16 @@ function render_location_default($item) { $location = $item['location']; - $coord = $item['coord']; + $latitude = $item['lat']; + $longitude = $item['lon']; - if ($coord) { + if ($latitude || $longitude) { if ($location) { - $location .= ' (' . $coord . ')'; + $location .= ' (' . $latitude . ',' . $longitude . ')'; } else { - $location = '' . $coord . ''; + $location = '' . $latitude . ',' . $longitude . ''; } } - return $location; } diff --git a/include/items.php b/include/items.php index e7fd8190c..4ffd59964 100644 --- a/include/items.php +++ b/include/items.php @@ -556,13 +556,18 @@ function get_item_elements($x,$allow_code = false) { $arr['plink'] = (($x['permalink']) ? htmlspecialchars($x['permalink'], ENT_COMPAT,'UTF-8',false) : ''); $arr['location'] = (($x['location']) ? htmlspecialchars($x['location'], ENT_COMPAT,'UTF-8',false) : ''); - $arr['coord'] = (($x['longlat']) ? htmlspecialchars($x['longlat'], ENT_COMPAT,'UTF-8',false) : ''); $arr['verb'] = (($x['verb']) ? htmlspecialchars($x['verb'], ENT_COMPAT,'UTF-8',false) : ''); $arr['mimetype'] = (($x['mimetype']) ? htmlspecialchars($x['mimetype'], ENT_COMPAT,'UTF-8',false) : ''); $arr['obj_type'] = (($x['object_type']) ? htmlspecialchars($x['object_type'], ENT_COMPAT,'UTF-8',false) : ''); $arr['tgt_type'] = (($x['target_type']) ? htmlspecialchars($x['target_type'], ENT_COMPAT,'UTF-8',false) : ''); - + if ($x['longlat']) { + $coordinates = explode(' ', $x['longlat']); + if (count($coordinates) > 1) { + $arr['lat'] = floatval($coordinates[0]); + $arr['lon'] = floatval($coordinates[1]); + } + } // convert AS1 namespaced elements to AS-JSONLD $arr['verb'] = Activity::activity_mapper($arr['verb']); @@ -976,7 +981,7 @@ function encode_item($item,$mirror = false) { $x['target_type'] = $item['tgt_type']; $x['permalink'] = $item['plink']; $x['location'] = $item['location']; - $x['longlat'] = $item['coord']; + $x['longlat'] = ($item['lat'] || $item['lon']) ? $item['lat'] . ' ' . $item['lon'] : 0.0; $x['signature'] = $item['sig']; $x['replyto'] = $item['replyto']; $x['owner'] = encode_item_xchan($item['owner']); @@ -1567,7 +1572,8 @@ function item_store($arr, $allow_exec = false, $deliver = true, $linkid = true) } $arr['location'] = ((x($arr,'location')) ? notags(trim($arr['location'])) : ''); - $arr['coord'] = ((x($arr,'coord')) ? notags(trim($arr['coord'])) : ''); + $arr['lat'] = ((x($arr, 'lat')) ? floatval($arr['lat']) : 0.0); + $arr['lon'] = ((x($arr, 'lon')) ? floatval($arr['lon']) : 0.0); $arr['parent_mid'] = ((x($arr,'parent_mid')) ? notags(trim($arr['parent_mid'])) : ''); $arr['thr_parent'] = ((x($arr,'thr_parent')) ? notags(trim($arr['thr_parent'])) : $arr['parent_mid']); $arr['verb'] = ((x($arr,'verb')) ? notags(trim($arr['verb'])) : ACTIVITY_POST); @@ -2100,7 +2106,8 @@ function item_store_update($arr, $allow_exec = false, $deliver = true, $linkid = $arr['location'] = ((x($arr,'location')) ? notags(trim($arr['location'])) : $orig[0]['location']); $arr['uuid'] = ((x($arr,'uuid')) ? notags(trim($arr['uuid'])) : $orig[0]['uuid']); - $arr['coord'] = ((x($arr,'coord')) ? notags(trim($arr['coord'])) : $orig[0]['coord']); + $arr['lat'] = ((x($arr,'lat')) ? floatval($arr['lat']) : $orig[0]['lat']); + $arr['lon'] = ((x($arr,'lon')) ? floatval($arr['lon']) : $orig[0]['lon']); $arr['verb'] = ((x($arr,'verb')) ? notags(trim($arr['verb'])) : $orig[0]['verb']); $arr['obj_type'] = ((x($arr,'obj_type')) ? notags(trim($arr['obj_type'])) : $orig[0]['obj_type']); $arr['obj'] = ((x($arr,'obj')) ? trim($arr['obj']) : $orig[0]['obj']); diff --git a/include/misc.php b/include/misc.php index 707ba3093..09c34be28 100644 --- a/include/misc.php +++ b/include/misc.php @@ -1743,23 +1743,12 @@ function format_filer(&$item) } -function generate_map($coord) +function generate_map($lat, $lon, $zoom = 16) { - $coord = str_replace(array(',','/',' '), array(' ',' ',' '), trim($coord)); - - - $zoom = ((strpos($coord, '?z=') !== false) ? substr($coord, strpos($coord, '?z=') + 3) : 0); - - if ($zoom) { - $coord = substr($coord, 0, strpos($coord, '?')); - } else { - $zoom = 16; - } - $arr = [ - 'lat' => trim(substr($coord, 0, strpos($coord, ' '))), - 'lon' => trim(substr($coord, strpos($coord, ' ') + 1)), + 'lat' => $lat, + 'lon' => $lon, 'zoom' => $zoom, 'html' => '' ]; @@ -1772,7 +1761,7 @@ function generate_map($coord) */ Hook::call('generate_map', $arr); - return (($arr['html']) ? $arr['html'] : $coord); + return (($arr['html']) ?? 'geo:' . $lat . ',' . $lon . '&z=' . $zoom); } function generate_named_map($location) @@ -1908,8 +1897,19 @@ function prepare_body(&$item, $attach = false, $opts = false) return $s; } - if (strpos($s, '
') !== false && $item['coord']) { - $x = generate_map(trim($item['coord'])); + if (strpos($s, '
') !== false) { + if ($item['lat'] || $item['lon']) { + $lat = $item['lat']; + $lon = $item['lon']; + } + elseif ($item['coord']) { + $tmp = explode(' ', $item['coord']); + if (count($tmp) > 1) { + $lat = $tmp[0]; + $lon = $tmp[1]; + } + } + $x = generate_map($lat, $lon); if ($x) { $s = preg_replace('/\
/', '$0' . $x, $s); } diff --git a/include/photos.php b/include/photos.php index 14f686620..e981a2ed4 100644 --- a/include/photos.php +++ b/include/photos.php @@ -560,8 +560,9 @@ function photo_upload($channel, $observer, $args) $arr['plink'] = z_root() . '/channel/' . $channel['channel_address'] . '/?f=&mid=' . urlencode($arr['mid']); - if ($lat && $lon) { - $arr['coord'] = $lat . ' ' . $lon; + if ($lat || $lon) { + $arr['lat'] = floatval($lat); + $arr['lon'] = floatval($lon); } $result = item_store($arr, false, $deliver); diff --git a/install/schema_mysql.sql b/install/schema_mysql.sql index ebe600fc7..62bd5d881 100644 --- a/install/schema_mysql.sql +++ b/install/schema_mysql.sql @@ -614,6 +614,8 @@ CREATE TABLE IF NOT EXISTS `item` ( `item_delayed` tinyint(1) NOT NULL DEFAULT 0 , `item_pending_remove` tinyint(1) NOT NULL DEFAULT 0 , `item_blocked` tinyint(1) NOT NULL DEFAULT 0 , + `lat` float NOT NULL DEFAULT '0', + `lon` float NOT NULL DEFAULT '0', PRIMARY KEY (`id`), KEY `parent` (`parent`), KEY `created` (`created`), @@ -668,7 +670,9 @@ CREATE TABLE IF NOT EXISTS `item` ( KEY `item_rss` (`item_rss`), KEY `item_consensus` (`item_consensus`), KEY `item_deleted_pending_remove_changed` (`item_deleted`, `item_pending_remove`, `changed`), - KEY `item_pending_remove_changed` (`item_pending_remove`, `changed`) + KEY `item_pending_remove_changed` (`item_pending_remove`, `changed`), + KEY `lat` (`lat`), + KEY `lon` (`lon`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE IF NOT EXISTS `likes` ( diff --git a/install/schema_postgres.sql b/install/schema_postgres.sql index d6f113c86..67ecb3cc8 100644 --- a/install/schema_postgres.sql +++ b/install/schema_postgres.sql @@ -628,6 +628,8 @@ CREATE TABLE "item" ( "item_delayed" smallint NOT NULL DEFAULT '0', "item_pending_remove" smallint NOT NULL DEFAULT '0', "item_blocked" smallint NOT NULL DEFAULT '0', + "lat" float NOT NULL DEFAULT '0', + "lon" float NOT NULL DEFAULT '0', "item_search_vector" tsvector, PRIMARY KEY ("id") ); @@ -688,6 +690,8 @@ create index "item_unpublished" on item ("item_unpublished"); create index "item_delayed" on item ("item_delayed"); create index "item_pending_remove" on item ("item_pending_remove"); create index "item_blocked" on item ("item_blocked"); +create index "lat" on item ("lat"); +create index "lon" on item ("lon"); -- fulltext indexes create index "item_search_idx" on item USING gist("item_search_vector"); create index "item_allow_cid" on item ("allow_cid"); diff --git a/view/tpl/jot-header.tpl b/view/tpl/jot-header.tpl index 7ee3eb17e..2a8d9d24f 100755 --- a/view/tpl/jot-header.tpl +++ b/view/tpl/jot-header.tpl @@ -1,5 +1,5 @@ -