* 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 | |||||