* API method to create a new time-limited shared link * API method for retrieving the current location given a shared link token * map view that refreshes the current location every 5 secondspull/34/head
@ -0,0 +1,48 @@ | |||||
<?php | |||||
namespace App\Http\Controllers; | |||||
use Laravel\Lumen\Routing\Controller as BaseController; | |||||
use Illuminate\Http\Request; | |||||
use DB; | |||||
class Share extends BaseController | |||||
{ | |||||
private function _databaseFromToken($token) { | |||||
$share = DB::table('shares') | |||||
->where('token', $token) | |||||
->where('expires_at', '>', time()) | |||||
->first(); | |||||
if(!$share) return false; | |||||
$database = DB::table('databases')->where('id', $share->database_id)->first(); | |||||
return $database; | |||||
} | |||||
public function view(Request $request, $token) { | |||||
$database = $this->_databaseFromToken($token); | |||||
if(!$database) { | |||||
return view('share-expired'); | |||||
} | |||||
return view('share', [ | |||||
'database' => $database, | |||||
'share_token' => $token, | |||||
]); | |||||
} | |||||
public function current_location(Request $request) { | |||||
$database = $this->_databaseFromToken($request->input('token')); | |||||
$response = [ | |||||
'data' => json_decode($database->last_location), | |||||
]; | |||||
return response(json_encode($response))->header('Content-Type', 'application/json'); | |||||
} | |||||
} |
@ -0,0 +1,33 @@ | |||||
<?php | |||||
use Illuminate\Database\Schema\Blueprint; | |||||
use Illuminate\Database\Migrations\Migration; | |||||
class Shares extends Migration | |||||
{ | |||||
/** | |||||
* Run the migrations. | |||||
* | |||||
* @return void | |||||
*/ | |||||
public function up() | |||||
{ | |||||
Schema::create('shares', function (Blueprint $table) { | |||||
$table->increments('id'); | |||||
$table->unsignedInteger('database_id'); | |||||
$table->datetime('expires_at')->nullable(); | |||||
$table->string('token', 30); | |||||
$table->datetime('created_at'); | |||||
}); | |||||
} | |||||
/** | |||||
* Reverse the migrations. | |||||
* | |||||
* @return void | |||||
*/ | |||||
public function down() | |||||
{ | |||||
Schema::drop('shares'); | |||||
} | |||||
} |
@ -0,0 +1,12 @@ | |||||
html, body { | |||||
height: 100%; | |||||
} | |||||
#map { | |||||
height: 100%; | |||||
} | |||||
/* Move the map zoom controls away from the Compass logo */ | |||||
.leaflet-top .leaflet-control-zoom { | |||||
margin-top: 60px; | |||||
margin-left: 26px; | |||||
} |
@ -0,0 +1,60 @@ | |||||
var map = L.map('map', { zoomControl: false }).setView([45.516, -122.660], 14, null, null, 24); | |||||
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', { | |||||
attribution: 'Map data © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>', | |||||
maxZoom: 18, | |||||
id: 'mapbox.streets', | |||||
accessToken: 'pk.eyJ1IjoiYWFyb25wayIsImEiOiI1T0tpNjdzIn0.OQjXyI3xSt8Dj8na3l90Sg' | |||||
}).addTo(map); | |||||
new L.Control.Zoom({ position: 'topleft' }).addTo(map); | |||||
var geojsonLineOptions = { | |||||
color: "#0033ff", | |||||
weight: 4, | |||||
opacity: 0.5 | |||||
}; | |||||
var startIcon = L.icon({ | |||||
iconUrl: '/assets/map-pin-start.png', | |||||
iconSize: [18,28], | |||||
iconAnchor: [9,28] | |||||
}); | |||||
// Load the current location and show on the map | |||||
var currentLocationMarker; | |||||
function getCurrentLocation() { | |||||
$.getJSON("/share/current.json?token="+$("#share_token").val(), function(data){ | |||||
if(data.data) { | |||||
moveMarkerToPosition(data.data); | |||||
map.setView(currentLocationMarker.getLatLng()); | |||||
} | |||||
setTimeout(getCurrentLocation, 5000); | |||||
}); | |||||
} | |||||
getCurrentLocation(); | |||||
function moveMarkerToPosition(feature) { | |||||
if(feature && feature.geometry) { | |||||
var coord = pointFromGeoJSON(feature.geometry.coordinates); | |||||
if(coord) { | |||||
if(!currentLocationMarker) { | |||||
currentLocationMarker = L.marker(coord).addTo(map); | |||||
} else { | |||||
currentLocationMarker.setLatLng(coord); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
function pointFromGeoJSON(geo) { | |||||
return L.latLng(geo[1], geo[0]) | |||||
} |
@ -0,0 +1,25 @@ | |||||
<!doctype html> | |||||
<html> | |||||
<head> | |||||
<meta charset="utf-8"> | |||||
<title>Compass</title> | |||||
<link rel="stylesheet" href="/assets/semantic/semantic.min.css"> | |||||
<link rel="stylesheet" href="/assets/font-awesome-4.4.0/css/font-awesome.min.css"> | |||||
<link rel="stylesheet" href="/assets/styles.css"> | |||||
<meta name="viewport" content="width=device-width, initial-scale=1"> | |||||
<script src="/assets/jquery-1.11.3.min.js"></script> | |||||
<link rel="stylesheet" href="/assets/leaflet-0.7.3/leaflet.css" /> | |||||
<link rel="stylesheet" href="/assets/share-map.css"> | |||||
</head> | |||||
<body> | |||||
@yield('content') | |||||
<script src="/assets/leaflet-0.7.3/leaflet.js"></script> | |||||
<script src="/assets/leaflet-0.7.3/esri-leaflet.js"></script> | |||||
<script src="/assets/semantic/semantic.min.js"></script> | |||||
<script src="/assets/semantic/components/dropdown.min.js"></script> | |||||
<script src="/assets/extensions.js"></script> | |||||
<script src="/assets/share.js"></script> | |||||
</body> | |||||
</html> |
@ -0,0 +1,9 @@ | |||||
@extends('layouts.master') | |||||
@section('content') | |||||
<div style="text-align: center; margin-top: 3em;"> | |||||
This shared link has expired. | |||||
</div> | |||||
@endsection |
@ -0,0 +1,9 @@ | |||||
@extends('layouts.share') | |||||
@section('content') | |||||
<div id="map"></div> | |||||
<input type="hidden" id="share_token" value="{{ $share_token }}"> | |||||
@endsection |