/** * Moving member markers to the new centralised location for all markers */ public function step1() { $memberGroupId = \IPS\membermap\Map::i()->getMemberGroupId(); $limit = 0; $did = 0; $perCycle = 500; if (isset(\IPS\Request::i()->extra)) { $limit = (int) \IPS\Request::i()->extra; } /* Try to prevent timeouts to the extent possible */ $cutOff = \IPS\core\Setup\Upgrade::determineCutoff(); foreach (\IPS\Db::i()->select('mm.*, m.name, m.members_seo_name', array('membermap_members', 'mm'), '', 'mm.member_id ASC', array($limit, $perCycle))->join(array('core_members', 'm'), 'mm.member_id=m.member_id') as $member) { if ($cutOff !== null and time() >= $cutOff) { return $limit + $did; } $did++; /* We don't have a name, likely this member has been deleted */ if ($member['name'] == '' or $member['name'] == NULL) { continue; } \IPS\Db::i()->insert('membermap_markers', array('marker_parent_id' => $memberGroupId, 'marker_name' => $member['name'], 'marker_name_seo' => $member['members_seo_name'], 'marker_lat' => $member['lat'], 'marker_lon' => $member['lon'], 'marker_member_id' => $member['member_id'], 'marker_added' => $member['marker_date'] ?: time())); } if ($did) { return $limit + $did; } else { unset($_SESSION['_step1Count']); return TRUE; } }
/** * Run Background Task * * @param mixed $data Data as it was passed to \IPS\Task::queue() * @param int $offset Offset * @return int|null New offset or NULL if complete * @throws \OutOfRangeException Indicates offset doesn't exist and thus task is complete */ public function run($data, $offset) { $currentMemUsage = memory_get_usage(TRUE); /* Wipe out the old files on the first run */ if ($offset === 0) { \IPS\membermap\Map::i()->deleteCacheFiles(); } $count = 0; $memberMarkers = array(); $customMarkers = array(); $selectColumns = array('mm.*', 'mg.*', 'm.member_id', 'm.name', 'm.members_seo_name', 'm.member_group_id', 'm.pp_photo_type', 'm.pp_main_photo', 'm.pp_thumb_photo'); if (\IPS\Settings::i()->allow_gravatars) { $selectColumns[] = 'm.pp_gravatar'; $selectColumns[] = 'm.email'; $selectColumns[] = 'm.members_bitoptions'; } /* Remember to update membermap\Map too */ $_markers = \IPS\Db::i()->select(implode(',', $selectColumns), array('membermap_markers', 'mm'), array('marker_open=1'), 'mg.group_position ASC, mm.marker_id DESC', array($offset, $this->perCycle))->join(array('membermap_markers_groups', 'mg'), 'mm.marker_parent_id=mg.group_id')->join(array('core_members', 'm'), 'mm.marker_member_id=m.member_id'); foreach ($_markers as $marker) { $count++; if ($marker['group_type'] == 'member') { $memberMarkers[] = $marker; } else { $customMarkers[] = $marker; } } if ($count > 0) { $markers = \IPS\membermap\Map::i()->formatMemberMarkers($memberMarkers); $custMarkers = \IPS\membermap\Map::i()->formatCustomMarkers($customMarkers); $markers = array_merge($markers, $custMarkers); $fileNumber = $offset / $this->perCycle; touch(\IPS\ROOT_PATH . '/datastore/membermap_cache/membermap-' . $fileNumber . '.json'); chmod(\IPS\ROOT_PATH . '/datastore/membermap_cache/membermap-' . $fileNumber . '.json', \IPS\IPS_FILE_PERMISSION); \file_put_contents(\IPS\ROOT_PATH . '/datastore/membermap_cache/membermap-' . $fileNumber . '.json', json_encode(array('markers' => $markers, 'memUsage' => (memory_get_usage(TRUE) - $currentMemUsage) / 1024 . 'kB', 'fromQueue' => 1))); /* Store the timestamp of the cache to force the browser to purge its local storage */ \IPS\Data\Store::i()->membermap_cacheTime = time(); } if (!$count) { throw new \IPS\Task\Queue\OutOfRangeException(); } return $offset + $count; }
/** * Member account has been updated * * @param $member \IPS\Member Member updating profile * @param $changes array The changes * @return void */ public function onProfileUpdate($member, $changes) { /* An endless loop is formed when \Item::createItem() is saving \Member, which then fire this membersync, which then calls \Item::createItem, and so on, and so on */ static $wereDoneHere = false; if ($wereDoneHere) { return; } $wereDoneHere = true; if (isset($changes['name'])) { $existingMarker = \IPS\membermap\Map::i()->getMarkerByMember($member->member_id, FALSE, FALSE); if ($existingMarker instanceof \IPS\membermap\Markers\Markers) { $existingMarker->name = $member->name; $existingMarker->updated = time(); $existingMarker->save(); } } if (count($changes) and \IPS\Settings::i()->membermap_monitorLocationField and !$member->members_bitoptions['bw_is_spammer']) { if (\IPS\Settings::i()->membermap_monitorLocationField_groupPerm === '*' or \IPS\Member::loggedIn()->inGroup(explode(',', \IPS\Settings::i()->membermap_monitorLocationField_groupPerm))) { if (isset($changes['field_' . \IPS\Settings::i()->membermap_profileLocationField]) and !empty($changes['field_' . \IPS\Settings::i()->membermap_profileLocationField])) { try { $lat = $lng = $location = NULL; $fieldValue = $changes['field_' . \IPS\Settings::i()->membermap_profileLocationField]; /* If it's an array, it might be from an address field, which already have the lat/lng data */ if (is_array(json_decode($fieldValue, TRUE))) { $addressData = json_decode($fieldValue, TRUE); if (is_float($addressData['lat']) and is_float($addressData['long'])) { $lat = floatval($addressData['lat']); $lng = floatval($addressData['long']); } $addressData['addressLines'][] = $addressData['city']; if (count($addressData['addressLines'])) { $location = implode(', ', $addressData['addressLines']); } } else { /* Remove HTML, newlines, tab, etc, etc */ $fieldValue = preg_replace("/[\\x00-\\x20]|\\xc2|\\xa0+/", ' ', strip_tags($fieldValue)); $fieldValue = trim(preg_replace("/\\s\\s+/", ' ', $fieldValue)); /* To my understanding we're not allowed to use \IPS\Geolocation, as that uses Google API, and we're not showing the info on a Google Map. */ $nominatim = \IPS\membermap\Map::i()->getLatLng($fieldValue); if (is_array($nominatim) and count($nominatim)) { $lat = $nominatim['lat']; $lng = $nominatim['lng']; $location = $nominatim['location']; } } if ($lat and $lng) { $existingMarker = \IPS\membermap\Map::i()->getMarkerByMember($member->member_id, FALSE); if ($existingMarker instanceof \IPS\membermap\Markers\Markers) { $marker = $existingMarker; $marker->updated = time(); } else { $groupId = \IPS\membermap\Map::i()->getMemberGroupId(); $marker = \IPS\membermap\Markers\Markers::createItem($member, \IPS\Request::i()->ipAddress(), new \IPS\DateTime(), \IPS\membermap\Markers\Groups::load($groupId)); } $marker->name = $member->name; $marker->lat = $lat; $marker->lon = $lng; $marker->location = $location ?: $fieldValue; /* Save and add to search index */ $marker->save(); \IPS\Content\Search\Index::i()->index($marker); } } catch (\Exception $e) { /* Something went wrong. Such as the input field being an editor */ \IPS\Log::log($e, 'membermap'); return false; } } } } }
/** * Delete a marker * * @return void */ protected function delete() { \IPS\Session::i()->csrfCheck(); if (!\IPS\Member::loggedIn()->member_id or !intval(\IPS\Request::i()->member_id)) { \IPS\Output::i()->error('no_permission', '2MM3/4', 403, ''); } /* Get the marker */ $existing = \IPS\membermap\Map::i()->getMarkerByMember(intval(\IPS\Request::i()->member_id), FALSE); if (isset($existing)) { $is_supmod = \IPS\Member::loggedIn()->modPermission() ?: 0; if ($is_supmod or $existing->mapped('author') == \IPS\Member::loggedIn()->member_id and $existing->canDelete()) { $existing->delete(); \IPS\Output::i()->json('OK'); } } /* Fall back to a generic error */ \IPS\Output::i()->error('no_permission', '2MM3/5', 403, ''); }
/** * Delete data * * @return void */ public function delete() { /* Can't delete a protected group */ if ($this->protected) { throw new \DomainException('No no no no no'); } parent::delete(); \IPS\membermap\Map::i()->invalidateJsonCache(); }
public function import() { $id = isset(\IPS\Request::i()->id) ? intval(\IPS\Request::i()->id) : 0; /* Build form */ $form = new \IPS\Helpers\Form(NULL, 'import'); if (isset(\IPS\Request::i()->id)) { $group = \IPS\membermap\Markers\Groups::load(intval(\IPS\Request::i()->id)); if ($group->type == 'member') { \IPS\Output::i()->error('generic_error', '1MM4/1', 403, ''); } } $form->add(new \IPS\Helpers\Form\Upload('import_upload', NULL, TRUE, array('allowedFileTypes' => array('kml'), 'temporary' => TRUE))); $form->add(new \IPS\Helpers\Form\YesNo('import_creategroups', FALSE, FALSE, array('togglesOff' => array('import_group')))); $form->add(new \IPS\Helpers\Form\Node('import_group', $id ?: 0, FALSE, array('class' => '\\IPS\\membermap\\Markers\\Groups', 'permissionCheck' => 'add', 'subnodes' => false, 'where' => array('group_type != ?', 'member')), NULL, NULL, NULL, 'import_group')); if ($values = $form->values()) { try { $xml = \IPS\Xml\SimpleXML::loadFile($values['import_upload']); } catch (\InvalidArgumentException $e) { $form->error = \IPS\Member::loggedIn()->language()->addToStack('xml_upload_invalid'); \IPS\Output::i()->output = $form; return; } /* No group selected, and don't create groups?! */ if ($values['import_creategroups'] == FALSE and !$values['import_group']) { $form->error = \IPS\Member::loggedIn()->language()->addToStack('membermap_error_no_id_no_create'); \IPS\Output::i()->output = $form; return; } $markers = array(); $groupOrder = NULL; $imported = 0; foreach ($xml->Document->Folder as $folder) { if (!isset($folder->Placemark)) { continue; } $folderName = (string) $folder->name; foreach ($folder->Placemark as $placemark) { if (!isset($placemark->Point->coordinates)) { continue; } list($lon, $lat, $elev) = explode(',', $placemark->Point->coordinates); $markers[] = array('marker_name' => (string) $placemark->name, 'marker_name_seo' => \IPS\Http\Url::seoTitle((string) $placemark->name), 'marker_description' => (string) $placemark->description, 'marker_lat' => $lat, 'marker_lon' => $lon, 'marker_member_id' => \IPS\Member::loggedIn()->member_id, 'marker_added' => time(), 'marker_open' => 1, 'marker_parent_id' => isset($values['import_group']) ? $values['import_group']->id : NULL); } /* Create a new group per "folder" */ if ($values['import_creategroups'] == TRUE and count($markers) > 0) { if ($groupOrder === NULL) { $groupOrder = \IPS\Db::i()->select(array("MAX( `group_position` ) as position"), 'membermap_markers_groups')->first(); } $groupOrder = $groupOrder + 1; $group = new \IPS\membermap\Markers\Groups(); $group->name = $folderName; $group->name_seo = \IPS\Http\Url::seoTitle($folderName); $group->type = 'custom'; $group->pin_colour = '#FFFFFF'; $group->pin_bg_colour = 'red'; $group->pin_icon = 'fa-globe'; $group->position = $groupOrder; $group->save(); \IPS\Lang::saveCustom('membermap', "membermap_marker_group_{$group->id}", trim($folderName)); \IPS\Lang::saveCustom('membermap', "membermap_marker_group_{$group->id}_JS", trim($folderName), 1); // Add group id to all elements of the array array_walk($markers, function (&$v, $k) use($group) { $v['marker_parent_id'] = $group->id; }); // Insert \IPS\Db::i()->insert('membermap_markers', $markers); $group->setLastComment(); $group->save(); // Set default permissions $perms = $group->permissions(); \IPS\Db::i()->update('core_permission_index', array('perm_view' => '*', 'perm_2' => '*', 'perm_3' => \IPS\Settings::i()->admin_group, 'perm_4' => \IPS\Settings::i()->admin_group), array('perm_id=?', $perms['perm_id'])); // Reset $imported += count($markers); $markers = array(); } } /* If we still got markers here, it's all pushed to one group, probably */ if (is_array($markers) and count($markers) > 0) { \IPS\Db::i()->insert('membermap_markers', $markers); $group = $values['import_group']; $group->setLastComment(); $group->save(); $imported += count($markers); } \IPS\membermap\Map::i()->invalidateJsonCache(); $message = \IPS\Member::loggedIn()->language()->addToStack('membermap_import_thumbup', FALSE, array('sprintf' => array($imported))); \IPS\Output::i()->redirect(\IPS\Http\Url::internal("app=membermap&module=membermap&controller=markers"), $message); } /* Display */ \IPS\Output::i()->output = $form; }
/** * Delete data * * @return void */ public function delete() { parent::delete(); $this->container()->setLastComment(); $this->container()->save(); \IPS\membermap\Map::i()->invalidateJsonCache(); }
/** * Install 'other' items. * * @return void */ public function installOther() { /* Install default Members marker group */ /* Calling this will create a group if one don't exist */ \IPS\membermap\Map::i()->getMemberGroupId(); }
/** * Execute * * If ran successfully, should return anything worth logging. Only log something * worth mentioning (don't log "task ran successfully"). Return NULL (actual NULL, not '' or 0) to not log (which will be most cases). * If an error occurs which means the task could not finish running, throw an \IPS\Task\Exception - do not log an error as a normal log. * Tasks should execute within the time of a normal HTTP request. * * @return mixed Message to log or NULL * @throws \IPS\Task\Exception */ public function execute() { if (!\IPS\Settings::i()->membermap_syncLocationField or !\IPS\Settings::i()->membermap_monitorLocationField or !\IPS\Settings::i()->membermap_profileLocationField) { $this->enabled = FALSE; $this->save(); return; } $fieldKey = \IPS\Settings::i()->membermap_profileLocationField; $limit = 100; $counter = 0; $memberMarkerGroupId = \IPS\membermap\Map::i()->getMemberGroupId(); try { $where = array(); $where[] = array("( pf.field_{$fieldKey} IS NOT NULL OR pf.field_{$fieldKey} != '' )"); $where[] = array("mm.marker_id IS NULL"); $where[] = array("m.membermap_location_synced = 0"); $where[] = array('( ! ' . \IPS\Db::i()->bitwiseWhere(\IPS\Member::$bitOptions['members_bitoptions'], 'bw_is_spammer') . ' )'); if (\IPS\Settings::i()->membermap_monitorLocationField_groupPerm !== '*') { $where[] = \IPS\Db::i()->in('m.member_group_id', explode(',', \IPS\Settings::i()->membermap_monitorLocationField_groupPerm)); } $members = \IPS\Db::i()->select('*', array('core_members', 'm'), $where, 'm.last_activity DESC', array(0, $limit))->join(array('core_pfields_content', 'pf'), 'pf.member_id=m.member_id')->join(array('membermap_markers', 'mm'), 'mm.marker_member_id=m.member_id AND mm.marker_parent_id=' . $memberMarkerGroupId); foreach ($members as $member) { $lat = $lng = $location = NULL; $_member = \IPS\Member::constructFromData($member); /* Need to set this to prevent us from looping over the same members with invalid locations over and over again */ $_member->membermap_location_synced = 1; $_member->save(); $_location = trim($member['field_' . $fieldKey]); if (empty($_location)) { continue; } /* If it's an array, it might be from an address field, which already have the lat/lng data */ if (is_array(json_decode($_location, TRUE))) { $addressData = json_decode($_location, TRUE); if (is_float($addressData['lat']) and is_float($addressData['long'])) { $lat = floatval($addressData['lat']); $lng = floatval($addressData['long']); } $addressData['addressLines'][] = $addressData['city']; if (count($addressData['addressLines'])) { $location = implode(', ', $addressData['addressLines']); } } else { /* Remove HTML, newlines, tab, etc, etc */ $_location = preg_replace("/[\\x00-\\x20]|\\xc2|\\xa0+/", ' ', strip_tags($_location)); $_location = trim(preg_replace("/\\s\\s+/", ' ', $_location)); /* To my understanding we're not allowed to use \IPS\Geolocation, as that uses Google API, and we're not showing the info on a Google Map. */ $nominatim = \IPS\membermap\Map::i()->getLatLng($_location); if (is_array($nominatim) and count($nominatim)) { $lat = $nominatim['lat']; $lng = $nominatim['lng']; $location = $nominatim['location']; } } if ($lat and $lng) { $marker = \IPS\membermap\Markers\Markers::createItem($_member, NULL, new \IPS\DateTime(), \IPS\membermap\Markers\Groups::load($memberMarkerGroupId), FALSE); $marker->name = $_member->name; $marker->lat = $lat; $marker->lon = $lng; $marker->location = $location ?: $_location; $marker->save(); /* Add to index */ \IPS\Content\Search\Index::i()->index($marker); $counter++; } } } catch (\UnderflowException $e) { } catch (\RuntimeException $e) { \IPS\Log::log(array($e->getMessage(), $nominatim), 'membermap'); } catch (\Exception $e) { \IPS\Log::log(array($e->getMessage(), $nominatim), 'membermap'); throw new \IPS\Task\Exception($this, $e->getMessage()); } if ($counter > 0) { $foundRows = $members->count(); return "Synchronised {$counter} out of {$foundRows} member locations"; } else { $this->enabled = FALSE; $this->save(); /* Turn the setting off as well */ \IPS\Db::i()->update('core_sys_conf_settings', array('conf_value' => 0), array('conf_key=?', 'membermap_syncLocationField')); unset(\IPS\Data\Store::i()->settings); return; } return NULL; }