I'm at a loss here. I've found all the HTML5 geolocation items to detect user location. But what I need to do is redirect them to the nearest location webpage. So if they are in/near San Antonio, maybe within a certain range (50 miles), they will be sent to the /san-antonio/ page on the site. Same would go for other locations around the USA. And if they do no have geolocation permissions turned on, they get directed to the generic /locations/ page.
I've found this basic code for getting the position:
<script>
var x=document.getElementById("demo");
function getLocation()
{
if (navigator.geolocation)
{
navigator.geolocation.getCurrentPosition(showPosition);
}
else{x.innerHTML="Geolocation is not supported by this browser.";}
}
function showPosition(position)
{
x.innerHTML="Latitude: " + position.coords.latitude +
"<br>Longitude: " + position.coords.longitude;
}
</script>
Where to go from here is a gray area. I thought this would be easy and easy google search, but i've come up with nothing.
Any help would be awesome since I just spent 5 hours trying to figure it out and have come up with nothing.
EDIT:* After searching our entire server for the word geolocation, I just found this in an old client theme, but i tested it and it doesn't give me the correct location. I don't know if it has something to do w/the where.yahoo api which isn't functional anymore. In the header.php file there is:
if (geo_position_js.init()) {
geo_position_js.getCurrentPosition(success, null);
}
function lookup_location() {
geo_position_js.getCurrentPosition(success, show_map_error);
}
function success(loc) {
url='http://www.websiteexample.com/location/?long='+loc.coords.longitude+'&lat='+loc.coords.latitude;
location.href=url;
}
function show_map_error() {
}
Then in the functions.php there is this:
function calc_distance($point1, $point2) {
$radius = 3958; // Earth's radius (miles)
$deg_per_rad = 57.29578; // Number of degrees/radian (for conversion)
$distance = ($radius * pi() * sqrt(
($point1['lat'] - $point2['lat'])
* ($point1['lat'] - $point2['lat'])
+ cos($point1['lat'] / $deg_per_rad) // Convert these to
* cos($point2['lat'] / $deg_per_rad) // radians for cos()
* ($point1['long'] - $point2['long'])
* ($point1['long'] - $point2['long'])
) / 180);
return $distance; // Returned using the units used for $radius.
}
function get_longlat($address) {
$ch = curl_init();
curl_setopt($ch,CURLOPT_URL,'http://where.yahooapis.com/geocode?appid=8gkElW6o&q='.urlencode($address));
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
$content = curl_exec($ch);
curl_close($ch);
$loc = simplexml_load_string($content)->Result;
$geo['lat'] = (string)$loc->latitude;
$geo['long'] = (string)$loc->longitude;
return $geo;
}
function redirect_to_nearest($clientlong, $clientlat) {
$clientgeo['long'] = $clientlong;
$clientgeo['lat'] = $clientlat;
$query = new WP_Query( array( 'post_type' => 'location' ) );
while ( $query->have_posts() ) : $query->the_post();
$custom = get_post_custom();
$slug = basename(get_permalink());
$location[$slug]['permalink'] = get_permalink();
$location[$slug]['address'] = $custom[google_maps_address][0];
$location[$slug]['geo'] = get_longlat($location[$slug][address]);
$location[$slug]['distance'] = calc_distance($clientgeo, $location[$slug]['geo']);
endwhile;
wp_reset_postdata();
usort($location,"cmp");
return $location[0]['permalink'];
}
function cmp($a, $b) {
if ($a['distance'] == $b['distance']) { return 0; }
return ($a['distance'] < $b['distance']) ? -1 : 1;
}
Then in the actual custom post type archive page there is this:
if(isset($_GET[long]) && isset($_GET[lat])) {
$redirectto = redirect_to_nearest($_GET[long], $_GET[lat]);}
if(isset($redirectto) && preg_match("/http:\/\/www.websiteexample.com\/location\/.*/i", $redirectto)) {
header( "HTTP/1.1 303 See Other" );
header( "Location: $redirectto" );
}
I thought I'd had struck gold. But I was wrong :-/
Related
I'm looking to (programmatically) edit/alter the title of a page when its created and before its saved. I've tried a couple hooks to no avail. I can access the title being saved along with other info which ill be used to alter the title, but can not find the right call to save the altered title.
It is for a private/local lan wiki (currently MediaWiki version 1.38.1 ).
When a new article is created with in a certon category I want to number it with a prefix of #### - based on the number of articles already in the category. At the top of the category page itself I have a <inputbox> which pulls a template that has the wiki syntax for the category in it [[Category:BlaBla]]. When the article is saved I'm doing a check to make sure its a new article and some ofther checks for the info I need, which is working fine, but I can not save the new altered page name.
I've tried the following hooks to no avail.
onMultiContentSave
onArticlePrepareTextForEdit
https://www.mediawiki.org/wiki/Manual:Hooks/MultiContentSave
https://www.mediawiki.org/wiki/Manual:Hooks/ArticlePrepareTextForEdit
Heres acouple snippets ive been testing with, both do as i want, aside from saving the altered page name.
public static function onArticlePrepareTextForEdit( WikiPage $wikiPage, ParserOptions $parserOptions ) {
return;
$exists = $wikiPage->exists();
if ($exists == 1) {
#return true;
}
$getTitle = $wikiPage->getTitle();
# check if title starts with 0000, exit if so, no work needs to be done
if (self::titleCheck($getTitle)) {
#return true;
}
$checkCategories = $wikiPage->getCategories();
$inMalak = false;
foreach ($checkCategories as $value) {
if ($value == "Category:Malak") {
$inMalak = true;
}
}
if ($inMalak == 1) {
$newTitle = self::newTitlePre() . $getTitle;
#$wikiPage->setTitle($newTitle);
print(">" . $newTitle . "<br>");
}
self::pr($newTitle);
}
public static function onMultiContentSave(RenderedRevision $renderedRevision, UserIdentity $user, CommentStoreComment $summary, $flags, Status $hookStatus){
#return;
$revision = $renderedRevision->getRevision();
$getTitle = $revision->getPageAsLinkTarget();
if (self::titleCheck($getTitle)) {
return true;
}
#$titleOBJ = $revision->Title();
$title = $revision->getId();
$parent_id = $revision->getId();
$content = $revision->getContent( SlotRecord::MAIN );
$new_content = $content->getText();
#$test = $revision->ParserOutput();
$parent_id = "";
if ($parent_id == "") {
$pos = strpos($new_content, "[[Category:Malak]]");
if ($pos) {
$newTitle = self::newTitlePre() . $getTitle;
#$wikiPage->setTitle($newTitle);
}
}
self::pr($newTitle);
}
EDIT........
Still have not found the proper way to do this, but came up with a work around (hackery) which works for my needs.
Using the onEditFormPreloadText hook, change the url and added a new parameter ('MalakHere'), edited the 'title' parameter to the altered title, then do a redirect with the new page name. In the hook function there is a check for the 'MalakHere' parameter, if found (only cause of redirect) then it will exit the function so not to create a loop.
public static function onEditFormPreloadText(string &$text, Title &$title ) {
global $wgOut;
if ( isset( $_GET["MalakHere"] ) ) {
return true;
}
$pos = strpos($text, "[[Category:Malak]]");
if ($pos) {
$url = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
$urlTitle = urlencode($_GET["title"]);
$newURL = str_replace("title=" . $urlTitle,"MalakHere=yes",$url);
$newTitle = self::newTitlePre() . $title->prefixedText;
$url = $newURL . "&title=" . $newTitle;
return $wgOut->redirect($url);
}
return true;
}
Still have not found the proper way to do this, but came up with a work around (hackery) which works for my needs.
Using the onEditFormPreloadText hook, change the url and added a new parameter ('MalakHere'), edited the 'title' parameter to the altered title, then do a redirect with the new page name. In the hook function there is a check for the 'MalakHere' parameter, if found (only cause of redirect) then it will exit the function so not to create a loop.
public static function onEditFormPreloadText(string &$text, Title &$title ) {
global $wgOut;
if ( isset( $_GET["MalakHere"] ) ) {
return true;
}
$pos = strpos($text, "[[Category:Malak]]");
if ($pos) {
$url = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? "https" : "http") . "://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
$urlTitle = urlencode($_GET["title"]);
$newURL = str_replace("title=" . $urlTitle,"MalakHere=yes",$url);
$newTitle = self::newTitlePre() . $title->prefixedText;
$url = $newURL . "&title=" . $newTitle;
return $wgOut->redirect($url);
}
return true;
}
I have the following code:
var app = angular.module('mcmmo', ['ngGrid']);
app.controller('mcmmoCtrl', function($scope, $http) {
$scope.filterOptions = {
filterText: "",
useExternalFilter: true
};
$scope.totalServerItems = 0;
$scope.pagingOptions = {
pageSizes: [250, 500, 1000],
pageSize: 250,
currentPage: 1
};
$scope.setPagingData = function(data, page, pageSize){
var pagedData = data.slice((page - 1) * pageSize, page * pageSize);
$scope.myData = pagedData;
$scope.totalServerItems = data.length;
if (!$scope.$$phase) {
$scope.$apply();
}
};
$scope.getPagedDataAsync = function (pageSize, page, searchText) {
setTimeout(function () {
var data;
if (searchText) {
var ft = searchText.toLowerCase();
$http.get('stats.php').success(function (largeLoad) {
data = largeLoad.filter(function(item) {
return JSON.stringify(item).toLowerCase().indexOf(ft) != -1;
});
$scope.setPagingData(data,page,pageSize);
});
} else {
$http.get('stats.php').success(function (largeLoad) {
$scope.setPagingData(largeLoad,page,pageSize);
});
}
}, 100);
};
$scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage);
$scope.$watch('pagingOptions', function (newVal, oldVal) {
if (newVal !== oldVal && newVal.currentPage !== oldVal.currentPage) {
$scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage, $scope.filterOptions.filterText);
}
}, true);
$scope.$watch('filterOptions', function (newVal, oldVal) {
if (newVal !== oldVal) {
$scope.getPagedDataAsync($scope.pagingOptions.pageSize, $scope.pagingOptions.currentPage, $scope.filterOptions.filterText);
}
}, true);
$scope.gridOptions = {
data: 'myData',
enablePaging: true,
showFooter: true,
totalServerItems: 'totalServerItems',
pagingOptions: $scope.pagingOptions,
filterOptions: $scope.filterOptions,
plugins: [new ngGridFlexibleHeightPlugin()]
};
});
Which pulls a very large load of JSON from a php file, that's stored in a database. Here are my json results.
[{"id":"1","user":"user1","lastlogin":"1402936307","skills":[{"taming":"4","mining":"534","woodcutting":"84","repair":"26","unarmed":"0","herbalism":"108","excavation":"219","archery":"10","swords":"75","axes":"24","acrobatics":"74","fishing":"403","alchemy":"0"}]
}
Here is the PHP that outputs this:
require_once('db.php');
error_reporting(1);
$getUsers = mysqli_query($db, 'SELECT * FROM mcmmo_users LIMIT 300');
$rows = array();
while ($r = mysqli_fetch_assoc($getUsers))
{
$skills = array();
$tempRow = $r;
$getSkills = mysqli_query($db, "SELECT * FROM mcmmo_skills WHERE user_id = '" . $r['id'] . "' LIMIT 300");
while ($r = mysqli_fetch_assoc($getSkills))
{
unset($r['user_id']);
$skills[] = $r;
}
$tempRow['skills'] = $skills;
$rows[] = $tempRow;
}
header('Content-Type: application/json');
echo json_encode($rows);
And this is what my grid currently looks like:
There is a couple of things wrong here:
I don't want the id or lastlogin columns.
I'd like to rename "users".
Instead of the "skills" column, I'd like for all of the data to be in it's own column, for example, taming and mining is it's own column with it's data in it's own row.
I'm not sure how to do that with this plugin though, any help would be appreciated!
Well, maybe changing the query string would be easy to handle it. Also by the time you navigate inside the query result, you can build your own array, just use an array with key as the name of the column.
$getUsers = mysqli_query($db, 'SELECT * FROM mcmmo_users LEFT JOIN mcmmo_skills ON mcmmo_users.id = mcmmo_skills.user_id');
$rows = array();
$cont = 0;
$userSkills = array();
while ($r = mysqli_fetch_assoc($getUsers))
{
$userSkills[$cont++] = array(
"Users" => $r['user'],
"Taming" => $r["taming"],
"Mining" => $r["mining"]
);
}
header('Content-Type: application/json');
echo json_encode($userSkills);
About Pagination
Try dirPagination at https://github.com/michaelbromley/angularUtils/tree/master/src/directives/pagination
It's very simple to use it and saves a lot of time.
As one of the commenters has already suggested, you can specify different column definitions than you have received in your incoming data. When you define your grid options, you optionally specify the column definitions separately from the data displayed:
$scope.resultGridOptions = {
data: 'myData',
columnDefs: 'columndefs',
// other parameters....
Then you just need to define your columns to reference your incoming data as documented here (especially the field and displayName):
https://github.com/angular-ui/ng-grid/wiki/Defining-columns
In your case, something like
$scope.columndefs = [{field:'id', displayName:'Id'}, {field:'user', displayName:'User'}];
should only show the id and user columns with the headers Id and User. (unless I have a typo)
Just noticed the final column part of your question: I am not sure how to show so much information in the final column. ng-grid does not support variable grid height to my knowledge, so it would make it difficult unless you can figure out a way to really condense this information into a column, but I don't know enough about your domain to recommend anything that seems reasonable.
I have a little problem. I have html page and I want to convert to pdf. My index page has a list that will get to the database and click on "Download PDF", I put this list in a PDF file.
My controller:
<?php
class pdf_c extends CI_Controller{
function __construct() {
parent::__construct();
$this->load->helper(array('url', 'mediatutorialpdf'));
}
function index($download_pdf = ''){
$ret = '';
$ID = 1;
$pdf_filename = 'user_info_'.$ID.'.pdf';
$link_download = ($download_pdf == TRUE)?'':anchor(base_url().'index.php/true', 'Download PDF');
$query = $this->db->query("SELECT * FROM `ci_pdf_user` WHERE `id` = '{$ID}' LIMIT 1");
if($query->num_rows() > 0)
{
$user_info = $query->row_array();
}
$data_header = array(
'title' => 'Convert codeigniter to pdf'
);
$data_userinfo = array(
'user_info' => $user_info,
'link_download' => $link_download
);
$header = $this->load->view('header',$data_header, true);
$user_info = $this->load->view('user_table', $data_userinfo, true);
$footer = $this->load->view('footer','', true);
$output = $header.$user_info.$footer;
if($download_pdf == TRUE)
{
generate_pdf($output, $pdf_filename);
}
else
{
echo $output;
}
}
}
?>
The problem is when I click the button "Download PDF" should redirect me to the function index () and get the $ download_pdf = true. And so called generate_pdf function () that will generate the PDF.
I think the problem is in the variable $ link_download, but can not solve the problem.
Thanks
I think that you could try with:
function index(pdf = 0)...
Then check that optional parameter with:
$pdf = $this->uri->segment(2, 0); //not sure, should be 2? try it...`
And then, if $pdf=='1' (send nummber rather than string 'true') ...etc,etc...
I'm trying to set up an autocomplete that's getting its data from a csv. The csv has a list of restaurant names and food types. I've gone over some of the other posts here at SO, and the autocomplete documentation, but I think it's the two fields in the csv, rather than one field that is tripping me up.
Example csv data:
McDonald's,Fast Food
Olive Garden,Italian
Manny's,Steakhouse
Carino's,Italian
etc...
The user should be able to search by either food type or restaurant name:
<body>
<br>
<br>
<label id="lblSearchRestaurant" for="txtSearchRestaurant">Search for a Restaurant</label>
<input type="text" id="txtSearchRestaurant">
</body>
Here's what I've tried for the autocomplete setup:
$(function() {
$( "#txtSearchRestaurant" ).autocomplete({
source:searchRestaurant.php,
select: function( event, ui ) {
$( "#search" ).val( ui.item.label + " / " + ui.item.actor );
return false;
}
}).data( "ui-autocomplete" )._renderItem = function( ul, item ) {
return $( "<li>" )
.data( "item.autocomplete", item )
.append( "<a><strong>" + item.label + "</strong> / " + item.actor + "</a>" )
.appendTo( ul );
};
});
I need to serve the data using a PHP script. Here's what I have at this point:
<?php
$header = NULL;
$restaurants = array();
if (($file = fopen('restaurants.csv', 'r')) !== FALSE) {
while (($row = fgetcsv($file, 1000, ',')) !== FALSE) {
if(!$header)
$header = $row;
else
$data[] = array_combine($header, $row);
}
fclose($file);
}
$term = $_GET['term'];
foreach($restaurants as $k=>$v) {
if(preg_match("/^$term/i", $v)) { $return[] = $v; }
}
foreach($restaurants as $k => $v) {
if(preg_match("/$term/i", $v)) { $return[] = $v; }
}
echo json_encode(array_values(array_unique($return)));
None of the above has worked, so I tried formating the restaurant data in to an array, rather than just comma-separated values:
[
{name:"McDonald's",type:"Fast Food"},
{name:"Olive Garden",type:"Italian"},
{name:"Manny's",type:"Steakhouse"},
{name:"Carino's",type:"Italian"}
];
I tried that locally in the <script> tag and in a separate file, but neither worked.
So, none of the above worked for me, but I'm not the best at arrays yet and for that matter, using JSON data, so I probably don't have the php script set up properly.
I'd appreciate it if someone could point me in the right direction here.
Thanks.
Note to potential answers:
The jquery UI autocomplete, the format of the incoming data (as described above) and using PHP to get the data from the csv to the autocomplete are all requirements for this. Those three stipulations are, unfortunately, not under my control.
Here's a jsfiddle that I hope does what you want: http://jsfiddle.net/L8L6k/
The javascript has a variable "data" at the top that you will need to populate with your data, using PHP (I believe you said you could do this.)
This data is then combined into two variables:
var autocomp = new Array();
var hash = new Array();
for (var i=0; i < data.length; i++)
{
autocomp[i] = data[i].name + ' ' + data[i].type;
hash[autocomp[i]] = data[i];
}
1) A simple array, autocomp, that is just a concatenation of the two values, name and type. This is passed to the JQuery UI autocomplete as the source. This way autocomplete will search both your "item" and "type".
2) An associative array, hash, which associates the data object with the concatenated value.
Both the select function and the renderItem function use this hash to get the original data object:
$( "#txtSearchRestaurant" ).autocomplete({
source:autocomp,
select: function( event, ui ) {
var d = hash[ui.item.label];
$( "#txtSearchRestaurant" ).val( d.name + " / " + d.type );
return false;
}
}).data( "ui-autocomplete" )._renderItem = function( ul, item ) {
var d = hash[item.label];
return $( "<li>" )
.data( "item.autocomplete", d )
.append( "<a><strong>" + d.name + "</strong> / " + d.type + "</a>" )
.appendTo( ul );
};
Assuming you return array from the php (the same format that you mention in the question).
Then add this to your <script>:
function both(Restaurant) {
return Restaurant.name + ',' + Restaurant.type ;
}
$( "#txtSearchRestaurant" ).autocomplete({
source:
function( request, response ) {
$.getJSON( "/searchRestaurant.php", function(data) {
response(data.map(both));
});
}
});
Or if you need help in both PHP and javascript, first try sending array consisting of restaurant name and type as a single string element like this :
[
"McDonald's,Fast Food",
"Olive Garden,Italian",
"Manny's,Steakhouse",
"Carino's,Italian"
]
In PHP searchRestaurant.php you can do following to get the above format quickly :
$lines = file('restaurants.csv', FILE_IGNORE_NEW_LINES);
echo json_encode($lines);
In <script> add :
$( "#txtSearchRestaurant" ).autocomplete({
source:
function( request, response ) {
$.getJSON( "/searchRestaurant.php", response);
}
});
I'm not that familiar with PHP, but this generally seems like you are unclear on the concepts. Note to readers: lack of response often indicates an unclear formulation of the question.
Correct me if I'm wrong, but here is how I understand the problem from your description:
You have a CSV of <restaurant>,<cuisine> pairs.
You have an omnibox on a web page where a user can enter restaurant or cuisine.
You want the omnibox to autocomplete based on the CSV.
You want to use the jQuery autocomplete widget to help with this.
The code you have presented tells a different story. The PHP code seems to be doing pretty much nothing. It stuffs a lot of data from a file into a data[] and then does nothing with it. I have no idea what that function produces.
Setting that aside, the HTML you present is a <body> block that contains an input type and a label for that type. (That's okay for this kind of discussion.)
The JavaScript/jQuery, however, is barely related. I don't see why UI items are expected to have a label and an actor. The general display of the auto-complete suggests you want to keep the restaurant and cuisine together and show them as a single selector but you want to have them select on either. This is not something the widget provides "straight out of the box" but it has hooks where you can add code to accomplish what you want.
A good place to start is to realize that while the widget is helpful, it is not meant to deal with multivalued objects. You're going to have to do some significant customization.
I think the keys in the returned array should be 'label' and 'value'.
Maybe you could replace:
$data[] = array_combine($header, $row);
by
if (preg_match("/^$term|$term/i", $row))
$return[] = array('label'=>$header,'value'=>$row);
So it becomes (untested):
<?php
$header = NULL;
$return = array();
$term = $_GET['term'];
if (($file = fopen('restaurants.csv', 'r')) !== FALSE) {
while (($row = fgetcsv($file, 1000, ',')) !== FALSE) {
if(!$header)
$header = $row;
else
if (preg_match("/^$term|$term/i", $row))
$return[] = array('label'=>$header,'value'=>$row);
}
fclose($file);
}
echo json_encode(array_values(array_unique($return)));
?>
I'm trying this recursion function to find LatLng of 2088 diffrent addresses and replay me with only about 180 results . although all addresses are valid on google maps website .
function test(i)
{
if(i >= jsArray.length){return;}
var geocoder = new GClientGeocoder();
geocoder.getLatLng(jsArray[i], function (current) {
return function(point) {
if (!point) {
data.push("null");
//nulls.push(myindex);
} else {
data.push(point);
//alert("done");
}
test(i+1,jsArray);
}
}(i));
}
test(0);
i have developed this recursive function but it's need about 30 mins till get good results ,
function test2(i)
{
geocoder.getLocations(jsArray[i], function (current) {
return function(response) {
if (!response || response.Status.code != 200) {
//alert(address + " not found");
//test(i,jsArray);
// data.push("null");
//nulls.push(myindex);
test2(i);
} else {
var len = response.Placemark[0];
point2 = new GLatLng(
len.Point.coordinates[1],
len.Point.coordinates[0]
);
data[i] = point2;
}
}
}(i));
}
for(i =0 ; i<=jsArray.length; i++)
{
if(i==jsArray.length){
alert(data.length);
/// $("#maintable").show(100) ;
/// $("#loading").hide(100) ;
}else{
test2(i);
}
}
i still need expert one to help me :) :D
The geocoder is asynchronous (which makes using it in loops problematic) and subject to a quota and rate limits. It is not intended for displaying lots of known addresses on a map, it is intended for user entered data.
You really should geocode your points off line, save the resulting coordinates and use those coordinates to display markers on your map.
If you are using it in a loop, you shouldn't use the getLatLng method, you should use the getLocations method, which contains a status code that will let you know why it is failing (G_GEO_TOO_MANY_QUERIES
= 620, would mean you could throttle your requests and potentially get a useful result)
// jsArray is array of addresses . the length of this array is 2087 element, all addresses got from google maps .
function MYFunction(i)
{
geocoder.getLocations(jsArray[i], function (current) {
return function(response) {
if (!response || response.Status.code != 200) {
test2(i); // recursive calling
} else {
var len = response.Placemark[0];
point2 = new GLatLng(
len.Point.coordinates[1],
len.Point.coordinates[0]
);
data[i] = point2;
}
}
}(i));
} /// end of y Function
//// loop for each address and pass it to MyFunction function and start recursive function .
for(i =0 ; i<=jsArray.length; i++)
{
MYFunction(i);
}