#60: Added a basic about page with a link to Github's API to fetch the latest release
This commit is contained in:
parent
89544437cd
commit
dcfcbca530
@ -15,6 +15,7 @@ use App\Http\Requests\SaveSettingsRequest;
|
||||
use App\Label;
|
||||
use App\Mail\TestMailConfig;
|
||||
use App\Photo;
|
||||
use App\Services\GithubService;
|
||||
use App\Services\PhotoService;
|
||||
use App\Storage;
|
||||
use App\User;
|
||||
@ -33,6 +34,48 @@ class DefaultController extends Controller
|
||||
View::share('is_admin', true);
|
||||
}
|
||||
|
||||
public function about()
|
||||
{
|
||||
return Theme::render('admin.about', [
|
||||
'current_version' => config('app.version'),
|
||||
'licence_text' => file_get_contents(sprintf('%s/LICENSE', dirname(dirname(dirname(dirname(__DIR__))))))
|
||||
]);
|
||||
}
|
||||
|
||||
public function aboutLatestRelease()
|
||||
{
|
||||
try
|
||||
{
|
||||
$githubService = new GithubService();
|
||||
$releaseInfo = $githubService->checkForLatestRelease();
|
||||
|
||||
// Convert the publish date so we can re-format it with the user's settings
|
||||
$publishDate = \DateTime::createFromFormat('Y-m-d\TH:i:s\Z', $releaseInfo->published_at);
|
||||
|
||||
// HTML-ify the body text
|
||||
$body = nl2br($releaseInfo->body);
|
||||
$body = preg_replace('/\*\*(.+)\*\*/', '<b>$1</b>', $body);
|
||||
|
||||
// Remove the "v" from the release name
|
||||
$version = substr($releaseInfo->name, 1);
|
||||
|
||||
// Determine if we can upgrade
|
||||
$canUpgrade = version_compare($version, config('app.version')) > 0;
|
||||
|
||||
return response()->json([
|
||||
'can_upgrade' => $canUpgrade,
|
||||
'body' => $body,
|
||||
'name' => $version,
|
||||
'publish_date' => $publishDate->format(UserConfig::get('date_format')),
|
||||
'url' => $releaseInfo->html_url
|
||||
]);
|
||||
}
|
||||
catch (\Exception $ex)
|
||||
{
|
||||
return response()->json(['error' => $ex->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
public function metadataUpgrade()
|
||||
{
|
||||
$albums = DbHelper::getAlbumsForCurrentUser();
|
||||
|
100
app/Services/GithubService.php
Normal file
100
app/Services/GithubService.php
Normal file
@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
class GithubService
|
||||
{
|
||||
private $cacheFile = null;
|
||||
private $config = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->config = config('services.github');
|
||||
$this->cacheFile = storage_path('app/github_cache.txt');
|
||||
}
|
||||
|
||||
public function checkForLatestRelease()
|
||||
{
|
||||
$releaseInfo = [];
|
||||
$etag = '';
|
||||
|
||||
if ($this->doesCacheExist())
|
||||
{
|
||||
// Get the etag from the cache
|
||||
$cacheData = $this->getCacheData();
|
||||
$etag = $cacheData->latest_release->etag;
|
||||
$releaseInfo = $cacheData->latest_release->release_info;
|
||||
}
|
||||
|
||||
// Lookup and store the version information
|
||||
$statusCode = -1;
|
||||
$result = $this->getLatestReleaseFromGithub($etag, $statusCode);
|
||||
|
||||
if ($statusCode == 200)
|
||||
{
|
||||
// Store the etag (in HTTP headers) for future reference
|
||||
$matches = [];
|
||||
$etag = '';
|
||||
if (preg_match('/^etag: "(.+)"/mi', $result[0], $matches))
|
||||
{
|
||||
$etag = $matches[1];
|
||||
}
|
||||
|
||||
$releaseInfo = json_decode($result[1]);
|
||||
}
|
||||
|
||||
if (!empty($etag))
|
||||
{
|
||||
$this->setCacheData([
|
||||
'latest_release' => [
|
||||
'etag' => $etag,
|
||||
'release_info' => $releaseInfo
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
return $releaseInfo;
|
||||
}
|
||||
|
||||
private function doesCacheExist()
|
||||
{
|
||||
return file_exists($this->cacheFile);
|
||||
}
|
||||
|
||||
private function getCacheData()
|
||||
{
|
||||
return json_decode(file_get_contents($this->cacheFile));
|
||||
}
|
||||
|
||||
private function getLatestReleaseFromGithub($etag = '', &$statusCode)
|
||||
{
|
||||
$httpHeaders = [
|
||||
sprintf('User-Agent: pandy06269/blue-twilight (v%s)', config('app.version'))
|
||||
];
|
||||
|
||||
if (!empty($etag))
|
||||
{
|
||||
$httpHeaders[] = sprintf('If-None-Match: "%s"', $etag);
|
||||
}
|
||||
|
||||
$ch = curl_init($this->config['latest_release_url']);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, $httpHeaders);
|
||||
curl_setopt($ch, CURLOPT_HEADER, true);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
$result = curl_exec($ch);
|
||||
|
||||
if ($result === false)
|
||||
{
|
||||
throw new \Exception(sprintf('Error from Github: %s', curl_error($ch)));
|
||||
}
|
||||
|
||||
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
return explode("\r\n\r\n", $result, 2);
|
||||
}
|
||||
|
||||
private function setCacheData(array $data)
|
||||
{
|
||||
file_put_contents($this->cacheFile, json_encode($data));
|
||||
}
|
||||
}
|
@ -14,6 +14,10 @@ return [
|
||||
|
|
||||
*/
|
||||
|
||||
'github' => [
|
||||
'latest_release_url' => 'https://api.github.com/repos/pandy06269/blue-twilight/releases/latest'
|
||||
],
|
||||
|
||||
'recaptcha' => [
|
||||
'verify_url' => 'https://www.google.com/recaptcha/api/siteverify'
|
||||
]
|
||||
|
@ -7,6 +7,11 @@
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.meta-label,
|
||||
.meta-value {
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
|
||||
.photo .loading {
|
||||
background-color: #ffffff;
|
||||
display: none;
|
||||
@ -23,6 +28,14 @@
|
||||
.photo .loading img {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.text-red {
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
.album-slideshow-container #image-preview {
|
||||
height: 600px;
|
||||
max-width: 100%;
|
||||
|
2
public/css/blue-twilight.min.css
vendored
2
public/css/blue-twilight.min.css
vendored
@ -1,4 +1,4 @@
|
||||
.admin-sidebar-card{margin-bottom:15px}.album-expand-handle{cursor:pointer;margin-top:5px}.photo .loading{background-color:#fff;display:none;height:100%;left:0;opacity:.8;position:absolute;text-align:center;top:0;width:100%;z-index:1000}.photo .loading img{margin-top:40px}.album-slideshow-container #image-preview{height:600px;max-width:100%;width:800px}.album-slideshow-container #image-preview img{max-width:100%}.album-slideshow-container .thumbnails{overflow-x:scroll;overflow-y:hidden;white-space:nowrap;width:auto}.stats-table .icon-col{font-size:1.4em;width:20%;vertical-align:middle}.stats-table .stat-col{font-size:1.8em;font-weight:bold;width:40%}.stats-table .text-col{font-size:1.2em;vertical-align:middle;width:40%}html{font-size:14px !important}button,input,optgroup,select,textarea{cursor:pointer;font-family:inherit !important}.album-photo-cards .card{margin-bottom:15px}.container,.container-fluid{margin-top:20px}.hidden{display:none}.tab-content{border:solid 1px #ddd;border-top:0;padding:20px}.tether-element,.tether-element:after,.tether-element:before,.tether-element *,.tether-element *:after,.tether-element *:before{box-sizing:border-box}.tether-element{position:absolute;display:none}.tether-element.tether-open{display:block}.tether-element.tether-theme-basic{max-width:100%;max-height:100%}.tether-element.tether-theme-basic .tether-content{border-radius:5px;box-shadow:0 2px 8px rgba(0,0,0,0.2);font-family:inherit;background:#fff;color:inherit;padding:1em;font-size:1.1em;line-height:1.5em}.tether-element,.tether-element:after,.tether-element:before,.tether-element *,.tether-element *:after,.tether-element *:before{box-sizing:border-box}.tether-element{position:absolute;display:none}.tether-element.tether-open{display:block}.tt-query{-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.tt-hint{color:#999}.tt-menu{width:422px;margin-top:4px;padding:4px 0;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2)}.tt-suggestion{padding:3px 20px;line-height:24px}.tt-suggestion.tt-cursor,.tt-suggestion:hover{color:#fff;background-color:#0097cf}.tt-suggestion p{margin:0}/*!
|
||||
.admin-sidebar-card{margin-bottom:15px}.album-expand-handle{cursor:pointer;margin-top:5px}.meta-label,.meta-value{vertical-align:middle !important}.photo .loading{background-color:#fff;display:none;height:100%;left:0;opacity:.8;position:absolute;text-align:center;top:0;width:100%;z-index:1000}.photo .loading img{margin-top:40px}.text-red{color:red}[v-cloak]{display:none}.album-slideshow-container #image-preview{height:600px;max-width:100%;width:800px}.album-slideshow-container #image-preview img{max-width:100%}.album-slideshow-container .thumbnails{overflow-x:scroll;overflow-y:hidden;white-space:nowrap;width:auto}.stats-table .icon-col{font-size:1.4em;width:20%;vertical-align:middle}.stats-table .stat-col{font-size:1.8em;font-weight:bold;width:40%}.stats-table .text-col{font-size:1.2em;vertical-align:middle;width:40%}html{font-size:14px !important}button,input,optgroup,select,textarea{cursor:pointer;font-family:inherit !important}.album-photo-cards .card{margin-bottom:15px}.container,.container-fluid{margin-top:20px}.hidden{display:none}.tab-content{border:solid 1px #ddd;border-top:0;padding:20px}.tether-element,.tether-element:after,.tether-element:before,.tether-element *,.tether-element *:after,.tether-element *:before{box-sizing:border-box}.tether-element{position:absolute;display:none}.tether-element.tether-open{display:block}.tether-element.tether-theme-basic{max-width:100%;max-height:100%}.tether-element.tether-theme-basic .tether-content{border-radius:5px;box-shadow:0 2px 8px rgba(0,0,0,0.2);font-family:inherit;background:#fff;color:inherit;padding:1em;font-size:1.1em;line-height:1.5em}.tether-element,.tether-element:after,.tether-element:before,.tether-element *,.tether-element *:after,.tether-element *:before{box-sizing:border-box}.tether-element{position:absolute;display:none}.tether-element.tether-open{display:block}.tt-query{-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-moz-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.tt-hint{color:#999}.tt-menu{width:422px;margin-top:4px;padding:4px 0;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);-webkit-border-radius:4px;-moz-border-radius:4px;border-radius:4px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2)}.tt-suggestion{padding:3px 20px;line-height:24px}.tt-suggestion.tt-cursor,.tt-suggestion:hover{color:#fff;background-color:#0097cf}.tt-suggestion p{margin:0}/*!
|
||||
* Bootstrap v4.0.0-beta (https://getbootstrap.com)
|
||||
* Copyright 2011-2017 The Bootstrap Authors
|
||||
* Copyright 2011-2017 Twitter, Inc.
|
||||
|
@ -22198,6 +22198,53 @@ return Tether;
|
||||
|
||||
}));
|
||||
|
||||
/**
|
||||
* This model is used by admin/about.blade.php, to perform a version check against Github.
|
||||
* @constructor
|
||||
*/
|
||||
function AboutViewModel(urls) {
|
||||
this.el = '#about-app';
|
||||
|
||||
this.data = {
|
||||
can_upgrade: false,
|
||||
is_loading: true,
|
||||
version_body: '',
|
||||
version_date: '',
|
||||
version_name: '',
|
||||
version_url: ''
|
||||
};
|
||||
|
||||
this.computed = {
|
||||
};
|
||||
|
||||
this.methods = {
|
||||
init: function () {
|
||||
var self = this;
|
||||
|
||||
$.ajax(
|
||||
urls.latest_release_url,
|
||||
{
|
||||
complete: function() {
|
||||
self.is_loading = false;
|
||||
},
|
||||
dataType: 'json',
|
||||
error: function (xhr, textStatus, errorThrown) {
|
||||
},
|
||||
method: 'GET',
|
||||
success: function (data) {
|
||||
self.version_body = data.body;
|
||||
self.version_date = data.publish_date;
|
||||
self.version_name = data.name;
|
||||
self.version_url = data.url;
|
||||
|
||||
// Set this last so any watchers on this property execute after all version data has been set
|
||||
self.can_upgrade = data.can_upgrade;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This model is used by admin/analyse_album.blade.php, to analyse all images.
|
||||
* @constructor
|
||||
|
2
public/js/blue-twilight.min.js
vendored
2
public/js/blue-twilight.min.js
vendored
File diff suppressed because one or more lines are too long
@ -7,6 +7,11 @@
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.meta-label,
|
||||
.meta-value {
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
|
||||
.photo .loading {
|
||||
background-color: #ffffff;
|
||||
display: none;
|
||||
@ -22,4 +27,12 @@
|
||||
|
||||
.photo .loading img {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.text-red {
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
47
resources/assets/js/admin.js
Normal file
47
resources/assets/js/admin.js
Normal file
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* This model is used by admin/about.blade.php, to perform a version check against Github.
|
||||
* @constructor
|
||||
*/
|
||||
function AboutViewModel(urls) {
|
||||
this.el = '#about-app';
|
||||
|
||||
this.data = {
|
||||
can_upgrade: false,
|
||||
is_loading: true,
|
||||
version_body: '',
|
||||
version_date: '',
|
||||
version_name: '',
|
||||
version_url: ''
|
||||
};
|
||||
|
||||
this.computed = {
|
||||
};
|
||||
|
||||
this.methods = {
|
||||
init: function () {
|
||||
var self = this;
|
||||
|
||||
$.ajax(
|
||||
urls.latest_release_url,
|
||||
{
|
||||
complete: function() {
|
||||
self.is_loading = false;
|
||||
},
|
||||
dataType: 'json',
|
||||
error: function (xhr, textStatus, errorThrown) {
|
||||
},
|
||||
method: 'GET',
|
||||
success: function (data) {
|
||||
self.version_body = data.body;
|
||||
self.version_date = data.publish_date;
|
||||
self.version_name = data.name;
|
||||
self.version_url = data.url;
|
||||
|
||||
// Set this last so any watchers on this property execute after all version data has been set
|
||||
self.can_upgrade = data.can_upgrade;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
@ -4,6 +4,19 @@ return [
|
||||
'create_album_link' => 'Create album',
|
||||
'panel_header' => 'Actions',
|
||||
],
|
||||
'about' => [
|
||||
'current_version' => 'You are running version',
|
||||
'github_link' => 'Github Project Website',
|
||||
'intro' => 'Blue Twilight is an <a href="https://www.andyheathershaw.uk/software/" target="_blank">App by Andy</a>. It is made with <i class="fa fa-heart text-red"></i> by UK software developer <a href="https://www.andyheathershaw.uk" target="_blank">Andy Heathershaw</a>.',
|
||||
'latest_version_loading' => 'Checking for the latest release on Github...',
|
||||
'licence_header' => 'Licence',
|
||||
'links_header' => 'Useful Links',
|
||||
'title' => 'About Blue Twilight',
|
||||
'up_to_date' => 'Good job - your installation of Blue Twilight is up-to-date!',
|
||||
'user_guide_link' => 'User Guide',
|
||||
'versions_header' => 'About & Version',
|
||||
'website_link' => 'Main Website'
|
||||
],
|
||||
'album_appearance_heading' => 'Appearance',
|
||||
'album_appearance_intro' => 'The settings shown below control how this album appears to visitors in the gallery.',
|
||||
'album_basic_info_heading' => 'Album information',
|
||||
|
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
return [
|
||||
'breadcrumb' => [
|
||||
'about' => 'About',
|
||||
'admin' => 'Admin',
|
||||
'albums' => 'Albums',
|
||||
'create_album' => 'Create album',
|
||||
|
79
resources/views/themes/base/admin/about.blade.php
Normal file
79
resources/views/themes/base/admin/about.blade.php
Normal file
@ -0,0 +1,79 @@
|
||||
@extends(Theme::viewName('layout'))
|
||||
@section('title', trans('admin.about.title'))
|
||||
|
||||
@section('breadcrumb')
|
||||
<li class="breadcrumb-item"><a href="{{ route('home') }}"><i class="fa fa-fw fa-home"></i></a></li>
|
||||
<li class="breadcrumb-item"><a href="{{ route('admin') }}">@lang('navigation.breadcrumb.admin')</a></li>
|
||||
<li class="breadcrumb-item active">@lang('navigation.breadcrumb.about')</li>
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h1>@lang('admin.about.title')</h1>
|
||||
<p>@lang('admin.about.intro')</p>
|
||||
|
||||
<ul class="nav nav-tabs mt-5">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="tab" href="#versions-tabpanel" role="tab">@lang('admin.about.versions_header')</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#licence-tabpanel" role="tab">@lang('admin.about.licence_header')</a>
|
||||
</li>
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false">@lang('admin.about.links_header')</a>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item" href="https://showmy.photos" target="_blank">@lang('admin.about.website_link')</a>
|
||||
<a class="dropdown-item" href="https://www.andyheathershaw.uk/software/blue-twilight-php-photo-gallery/manual/" target="_blank">@lang('admin.about.user_guide_link')</a>
|
||||
<a class="dropdown-item" href="https://github.com/pandy06269/blue-twilight" target="_blank">@lang('admin.about.github_link')</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="versions-tabpanel" role="tabpanel">
|
||||
<div class="row" id="about-app">
|
||||
<div class="col-md-6 text-center">
|
||||
<p>@lang('admin.about.current_version')</p>
|
||||
<p class="m-0" style="font-size: 2.5rem;">{{ $current_version }}</p>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 text-center pt-2">
|
||||
<div v-if="is_loading">
|
||||
<p><img src="{{ asset('ripple.svg') }}"></p>
|
||||
<p class="m-0">@lang('admin.about.latest_version_loading')</p>
|
||||
</div>
|
||||
|
||||
<p v-cloak v-if="!is_loading && !can_upgrade" class="text-success">
|
||||
<i class="fa fa-check"></i> @lang('admin.about.up_to_date')
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane" id="licence-tabpanel" role="tabpanel">
|
||||
{!! nl2br($licence_text) !!}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function()
|
||||
{
|
||||
var viewModel = new AboutViewModel({
|
||||
latest_release_url: '{{ route('admin.aboutLatestRelease') }}'
|
||||
});
|
||||
|
||||
var app = new Vue(viewModel);
|
||||
app.$watch('can_upgrade', function(value)
|
||||
{
|
||||
alert('update available');
|
||||
});
|
||||
app.init();
|
||||
});
|
||||
</script>
|
||||
@endpush
|
@ -5,7 +5,7 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="meta-label">@lang('admin.sysinfo_widget.app_version')</td>
|
||||
<td class="meta-value">{{ $app_version }}</td>
|
||||
<td class="meta-value">{{ $app_version }} <a href="{{ route('admin.about') }}" class="btn btn-info ml-2"><i class="fa fa-question"></i></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="meta-label">@lang('admin.sysinfo_widget.hostname')</td>
|
||||
|
@ -16,6 +16,8 @@ Auth::routes();
|
||||
// Administration
|
||||
Route::group(['prefix' => 'admin'], function () {
|
||||
Route::get('/', 'Admin\DefaultController@index')->name('admin');
|
||||
Route::get('/about', 'Admin\DefaultController@about')->name('admin.about');
|
||||
Route::get('/about/latest-release', 'Admin\DefaultController@aboutLatestRelease')->name('admin.aboutLatestRelease');
|
||||
Route::get('/photo-metadata', 'Admin\DefaultController@metadataUpgrade')->name('admin.metadataUpgrade');
|
||||
Route::post('quick-upload', 'Admin\DefaultController@quickUpload')->name('admin.quickUpload');
|
||||
Route::post('settings/save', 'Admin\DefaultController@saveSettings')->name('admin.saveSettings');
|
||||
|
Loading…
Reference in New Issue
Block a user