1
0
mirror of https://github.com/Kovah/LinkAce.git synced 2025-03-15 04:09:39 +01:00

Implement bookmarklet feature (#5)

This commit is contained in:
Kovah 2019-01-10 22:39:01 +01:00
parent 7622a5f3a9
commit 1629f712bc
No known key found for this signature in database
GPG Key ID: AAAA031BA9830D7B
16 changed files with 441 additions and 151 deletions

View File

@ -11,6 +11,7 @@ class LinkAce
{
/**
* Get the title of an HTML page b
*
* @param string $url
* @return string|string[]
*/
@ -40,4 +41,18 @@ class LinkAce
return $title;
}
/**
* Generate the code for the bookmarklet
*
* @return mixed|string
*/
public static function generateBookmarkletCode()
{
$bm_code = "javascript:javascript:(function(){var%20url%20=%20location.href;var%20title%20=%20document.title%20||%20url;window.open('##URL##?u='%20+%20encodeURIComponent(url)+'&t='%20+%20encodeURIComponent(title),'_blank','menubar=no,height=720,width=600,toolbar=no,scrollbars=yes,status=no,dialog=1');})();";
$bm_code = str_replace('##URL##', route('bookmarklet-add'), $bm_code);
return $bm_code;
}
}

View File

@ -0,0 +1,72 @@
<?php
namespace App\Http\Controllers\App;
use App\Http\Controllers\Controller;
use App\Models\Category;
use Illuminate\Http\Request;
class BookmarkletController extends Controller
{
/**
* Show the application dashboard.
*
* @param Request $request
* @return \Illuminate\Http\Response
*/
public function getLinkAddForm(Request $request)
{
$new_url = $request->get('u');
$new_title = $request->get('t');
// Rredirect to the login if the user is not logged in
if (!auth()->check()) {
// Save details for the link in the session
session(['bookmarklet.new_url' => $new_url]);
session(['bookmarklet.new_title' => $new_title]);
session(['bookmarklet.login_redirect' => true]);
return redirect()->route('bookmarklet-login');
}
if ($new_url === null) {
// Receive the link details from the session
$new_url = session('bookmarklet.new_url');
$new_title = session('bookmarklet.new_title');
session()->remove('bookmarklet.new_url');
session()->remove('bookmarklet.new_title');
}
session(['bookmarklet.create' => true]);
return view('actions.bookmarklet.create')
->with('bookmark_url', $new_url)
->with('bookmark_title', $new_title)
->with('categories', Category::parentOnly()
->byUser(auth()->user()->id)
->orderBy('name', 'asc')
->get()
);
}
/**
* Show the application dashboard.
*
* @return \Illuminate\Http\Response
*/
public function getCompleteView()
{
return view('actions.bookmarklet.complete');
}
/**
* Show the application dashboard.
*
* @return \Illuminate\Http\Response
*/
public function getLoginForm()
{
return view('actions.bookmarklet.login');
}
}

View File

@ -2,6 +2,7 @@
namespace App\Http\Controllers\App;
use App\Helper\LinkAce;
use App\Http\Controllers\Controller;
use App\Http\Requests\UserSettingsUpdateRequest;
use App\Models\Setting;
@ -22,7 +23,12 @@ class UserSettingsController extends Controller
*/
public function getUserSettings()
{
return view('actions.settings.user', ['user' => auth()->user()]);
$bookmarklet_code = LinkAce::generateBookmarkletCode();
return view('actions.settings.user', [
'user' => auth()->user(),
'bookmarklet_code' => $bookmarklet_code,
]);
}
/**

View File

@ -23,9 +23,18 @@ class LoginController extends Controller
/**
* Where to redirect users after login.
*
* @var string
* @return string
*/
protected $redirectTo = '/dashboard';
protected function redirectTo()
{
// Redirect to the bookmarklet form after login from the bookmarklet
if (session('bookmarklet.login_redirect')) {
session()->remove('bookmarklet.login_redirect');
return route('bookmarklet-add');
}
return '/dashboard';
}
/**
* Create a new controller instance.

View File

@ -79,11 +79,22 @@ class LinkController extends Controller
alert(trans('link.added_successfully'), 'success');
$is_bookmarklet = session('bookmarklet.create');
if ($request->get('reload_view')) {
session()->flash('reload_view', true);
if ($is_bookmarklet) {
return redirect()->route('bookmarklet-add');
}
return redirect()->route('links.create');
}
if ($is_bookmarklet) {
return redirect()->route('bookmarklet-complete');
}
return redirect()->route('links.show', [$link->id]);
}

View File

@ -1,5 +1,5 @@
// Layout styles
body {
body:not(.bookmarklet) {
padding-top: $nav-link-height + ($navbar-padding-y * 2);
}

View File

@ -36,4 +36,7 @@ return [
'no' => 'No',
'no_results_found' => 'No :model found.',
'bookmarklet_close' => 'This bookmarklet window automatically closes in <span class="bm-timer">5</span> seconds.',
'open_linkace' => 'Open LinkAce',
];

View File

@ -13,6 +13,10 @@ return [
'save_settings' => 'Save Settings',
'settings_saved' => 'Settings successfully updated!',
'bookmarklet' => 'Bookmarklet',
'bookmarklet_button' => 'Drag this to your Bookmarks or right-click and save it as a bookmark',
'bookmarklet_help' => 'Add this Bookmarklet to your browser to quickly add links from the sites you visit without having to open LinkAce manually.',
'change_password' => 'Change Password',
'old_password' => 'Old Password',
'new_password' => 'New Password',

View File

@ -0,0 +1,26 @@
@extends('layouts.bookmarklet')
@section('content')
<div class="mt-3">
<p>@lang('linkace.bookmarklet_close')</p>
<a href="{{ route('front') }}" target="_blank" class="btn btn-primary">
<i class="fa fa-arrow-left fa-mr"></i>
@lang('linkace.open_linkace')
</a>
</div>
@endsection
@push('scripts')
<script>
var timer = $('.bm-timer');
window.setInterval(function () {
timer.text(parseInt(timer.text()) - 1);
}, 1000);
window.setTimeout(function () {
window.close();
}, 5000);
</script>
@endpush

View File

@ -0,0 +1,7 @@
@extends('layouts.bookmarklet')
@section('content')
@include('models.links._create-form')
@endsection

View File

@ -0,0 +1,78 @@
@extends('layouts.bookmarklet')
@section('content')
<div class="card">
<div class="card-header">
@lang('linkace.login')
</div>
<div class="card-body">
<form method="POST" action="{{ route('login') }}" aria-label="@lang('linkace.login')">
@csrf
<div class="form-group">
<div class="input-group mb-3">
<div class="input-group-prepend">
<div class="input-group-text">
<i class="fa fa-fw fa-envelope"></i>
</div>
</div>
<input type="email" name="email" id="email" class="form-control"
value="{{ old('email') }}"
placeholder="@lang('linkace.email')" aria-label="@lang('linkace.email')" required
autofocus>
</div>
@if ($errors->has('email'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('email') }}
</p>
@endif
</div>
<div class="form-group">
<div class="input-group mb-3">
<div class="input-group-prepend">
<div class="input-group-text">
<i class="fa fa-fw fa-lock"></i>
</div>
</div>
<input type="password" name="password" id="password" class="form-control"
placeholder="@lang('linkace.password')" aria-label="@lang('linkace.password')">
</div>
@if ($errors->has('password'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('password') }}
</p>
@endif
</div>
<div class="row mt-4">
<div class="col-8">
<div class="custom-control custom-checkbox pt-1">
<input type="hidden" name="remember_me" value="0">
<input type="checkbox" class="custom-control-input" id="remember_me"
@if(old('remember_me')) checked @endif>
<label class="custom-control-label" for="remember_me">
@lang('linkace.remember_me')
</label>
</div>
</div>
<div class="col-4">
<button type="submit" class="btn btn-primary btn-block">
@lang('linkace.login')
</button>
</div>
</div>
</form>
</div>
</div>
@endsection

View File

@ -3,6 +3,23 @@
@section('content')
<div class="card">
<div class="card-header">
@lang('settings.bookmarklet')
</div>
<div class="card-body">
<p>@lang('settings.bookmarklet_help')</p>
<a href="{{ $bookmarklet_code }}" class="btn btn-primary">
@lang('settings.bookmarklet')
</a>
<p class="small">@lang('settings.bookmarklet_button')</p>
</div>
</div>
<div class="card mt-4">
<div class="card-header">
@lang('settings.user_settings')
</div>
@ -164,7 +181,7 @@
</div>
</div>
<div class="card mt-5">
<div class="card mt-4">
<div class="card-header">
@lang('settings.change_password')
</div>

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'LinkAce') }}</title>
<link href="{{ asset('assets/app.css') }}" rel="stylesheet">
</head>
<body class="bookmarklet">
<div id="app">
<main class="main container">
<div class="mb-3 text-center">
<img src="{{ asset('assets/img/logo_linkace.svg') }}" alt="@lang('linkace.linkace')"
width="81" height="30">
</div>
@include('partials.alerts')
@yield('content')
</main>
<script src="{{ asset('assets/dependencies.js') }}"></script>
@stack('scripts')
</div>
</body>
</html>

View File

@ -0,0 +1,140 @@
<div class="card">
<header class="card-header">
@lang('link.add')
</header>
<div class="card-body">
<form action="{{ route('links.store') }}" method="POST">
@csrf
<div class="form-group">
<label class="label" for="url">@lang('link.url')</label>
<input name="url" id="url" type="url"
class="form-control form-control-lg{{ $errors->has('url') ? ' is-invalid' : '' }}"
placeholder="@lang('link.url')" value="{{ old('url') ?: $bookmark_url ?? '' }}"
required autofocus>
@if ($errors->has('url'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('url') }}
</p>
@endif
</div>
<div class="row">
<div class="col">
<div class="form-group">
<label class="label" for="title">@lang('link.title')</label>
<input name="title" id="title"
class="form-control{{ $errors->has('title') ? ' is-invalid' : '' }}"
type="text" placeholder="@lang('link.title')"
value="{{ old('title') ?: $bookmark_title ?? '' }}">
@if ($errors->has('title'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('title') }}
</p>
@endif
</div>
<div class="form-group">
<label for="description">@lang('link.description')</label>
<textarea name="description" id="description" rows="4" class="form-control"
placeholder="@lang('link.description')">{{ old('description') }}</textarea>
@if ($errors->has('description'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('description') }}
</p>
@endif
</div>
</div>
<div class="col">
<div class="form-group">
<label for="category_id">@lang('category.category')</label>
<select id="category_id" name="category_id"
class="custom-select{{ $errors->has('category_id') ? ' is-invalid' : '' }}">
<option value="0">@lang('category.select_category')</option>
@foreach($categories as $category)
<option value="{{ $category->id }}">
{{ $category->name }}
</option>
@if($category->childCategories)
@foreach($category->childCategories as $child_category)
<option value="{{ $child_category->id }}">
&rightarrow; {{ $child_category->name }}
</option>
@endforeach
@endif
@endforeach
</select>
@if ($errors->has('category_id'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('category_id') }}
</p>
@endif
</div>
<div class="form-group">
<label for="tags">@lang('tag.tags')</label>
<input name="tags" id="tags" type="text" placeholder="@lang('tag.tags')"
value="{{ old('tags') }}">
@if ($errors->has('url'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('tags') }}
</p>
@endif
</div>
<div class="form-group">
<label for="is_private">@lang('linkace.is_private')</label>
<select id="is_private" name="is_private"
class="custom-select{{ $errors->has('is_private') ? ' is-invalid' : '' }}">
<option value="0">
@lang('linkace.no')
</option>
<option value="1" @if(usersettings('private_default') === '1') selected @endif>
@lang('linkace.yes')
</option>
</select>
@if ($errors->has('is_private'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('is_private') }}
</p>
@endif
</div>
</div>
</div>
<div class="mt-3 d-flex align-items-center">
@if(!isset($bookmark_url))
<div class="custom-control custom-checkbox ml-auto mr-4">
<input class="custom-control-input" type="checkbox" id="reload_view" name="reload_view"
@if(session('reload_view')) checked @endif>
<label class="custom-control-label" for="reload_view">
@lang('linkace.continue_adding')
</label>
</div>
@endif
<button type="submit" class="btn btn-primary">
<i class="fa fa-save fa-mr"></i> @lang('link.add')
</button>
</div>
</form>
</div>
</div>
@push('scripts')
@include('models.links._tags-js')
@endpush

View File

@ -2,142 +2,6 @@
@section('content')
<div class="card">
<header class="card-header">
@lang('link.add')
</header>
<div class="card-body">
<form action="{{ route('links.store') }}" method="POST">
@csrf
<div class="form-group">
<label class="label" for="url">@lang('link.url')</label>
<input name="url" id="url" type="url"
class="form-control form-control-lg{{ $errors->has('url') ? ' is-invalid' : '' }}"
placeholder="@lang('link.url')" value="{{ old('url') }}"
required autofocus>
@if ($errors->has('url'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('url') }}
</p>
@endif
</div>
<div class="row">
<div class="col">
<div class="form-group">
<label class="label" for="title">@lang('link.title')</label>
<input name="title" id="title"
class="form-control{{ $errors->has('title') ? ' is-invalid' : '' }}"
type="text" placeholder="@lang('link.title')" value="{{ old('title') }}">
@if ($errors->has('title'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('title') }}
</p>
@endif
</div>
<div class="form-group">
<label for="description">@lang('link.description')</label>
<textarea name="description" id="description" rows="4" class="form-control"
placeholder="@lang('link.description')">{{ old('description') }}</textarea>
@if ($errors->has('description'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('description') }}
</p>
@endif
</div>
</div>
<div class="col">
<div class="form-group">
<label for="category_id">@lang('category.category')</label>
<select id="category_id" name="category_id"
class="custom-select{{ $errors->has('category_id') ? ' is-invalid' : '' }}">
<option value="0">@lang('category.select_category')</option>
@foreach($categories as $category)
<option value="{{ $category->id }}">
{{ $category->name }}
</option>
@if($category->childCategories)
@foreach($category->childCategories as $child_category)
<option value="{{ $child_category->id }}">
&rightarrow; {{ $child_category->name }}
</option>
@endforeach
@endif
@endforeach
</select>
@if ($errors->has('category_id'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('category_id') }}
</p>
@endif
</div>
<div class="form-group">
<label for="tags">@lang('tag.tags')</label>
<input name="tags" id="tags" type="text" placeholder="@lang('tag.tags')"
value="{{ old('tags') }}">
@if ($errors->has('url'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('tags') }}
</p>
@endif
</div>
<div class="form-group">
<label for="is_private">@lang('linkace.is_private')</label>
<select id="is_private" name="is_private"
class="custom-select{{ $errors->has('is_private') ? ' is-invalid' : '' }}">
<option value="0">
@lang('linkace.no')
</option>
<option value="1" @if(usersettings('private_default') === '1') selected @endif>
@lang('linkace.yes')
</option>
</select>
@if ($errors->has('is_private'))
<p class="invalid-feedback" role="alert">
{{ $errors->first('is_private') }}
</p>
@endif
</div>
</div>
</div>
<div class="mt-3 d-flex align-items-center">
<div class="custom-control custom-checkbox ml-auto mr-4">
<input class="custom-control-input" type="checkbox" id="reload_view" name="reload_view"
@if(session('reload_view')) checked @endif>
<label class="custom-control-label" for="reload_view">
@lang('linkace.continue_adding')
</label>
</div>
<button type="submit" class="btn btn-primary">
<i class="fa fa-save fa-mr"></i> @lang('link.add')
</button>
</div>
</form>
</div>
</div>
@push('scripts')
@include('models.links._tags-js')
@endpush
@include('models.links._create-form')
@endsection

View File

@ -20,19 +20,25 @@ Route::get('/', function () {
})->name('front');
// Authentication Routes
$this->get('login', 'Auth\LoginController@showLoginForm')->name('login');
$this->post('login', 'Auth\LoginController@login');
$this->post('logout', 'Auth\LoginController@logout')->name('logout');
Route::get('login', 'Auth\LoginController@showLoginForm')->name('login');
Route::post('login', 'Auth\LoginController@login');
Route::post('logout', 'Auth\LoginController@logout')->name('logout');
// Registration Routes (disabled, use the `artisan registeruser` command)
//$this->get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
//$this->post('register', 'Auth\RegisterController@register');
//Route::get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
//Route::post('register', 'Auth\RegisterController@register');
// Password Reset Routes
$this->get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
$this->post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
$this->get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');
$this->post('password/reset', 'Auth\ResetPasswordController@reset');
Route::get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');
Route::post('password/reset', 'Auth\ResetPasswordController@reset');
Route::prefix('bookmarklet')->group(function () {
Route::get('add', 'App\BookmarkletController@getLinkAddForm')->name('bookmarklet-add');
Route::get('show', 'App\BookmarkletController@getCompleteView')->name('bookmarklet-complete');
Route::get('login', 'App\BookmarkletController@getLoginForm')->name('bookmarklet-login');
});
// Model routes
Route::group(['middleware' => ['auth']], function () {