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