Improved Bootstrap experience and services improvements #154
@ -1,7 +1,7 @@
|
||||
APP_ENV=local
|
||||
APP_ENV=production
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_LOG_LEVEL=debug
|
||||
APP_DEBUG=false
|
||||
APP_LOG_LEVEL=warning
|
||||
APP_URL=http://localhost
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
|
30
Gruntfile.js
30
Gruntfile.js
@ -14,7 +14,9 @@ module.exports = function(grunt)
|
||||
const sass = require('node-sass');
|
||||
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
grunt.loadNpmTasks('grunt-contrib-cssmin');
|
||||
grunt.loadNpmTasks('grunt-contrib-concat');
|
||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||
grunt.loadNpmTasks('grunt-curl');
|
||||
grunt.loadNpmTasks('grunt-dart-sass');
|
||||
grunt.loadNpmTasks('grunt-exec');
|
||||
@ -97,6 +99,23 @@ module.exports = function(grunt)
|
||||
ext: '.css'
|
||||
}]
|
||||
},
|
||||
},
|
||||
cssmin: {
|
||||
bt_css: {
|
||||
files: {
|
||||
'public/css/blue-twilight.min.css': ['public/css/blue-twilight.css']
|
||||
}
|
||||
}
|
||||
},
|
||||
uglify: {
|
||||
bt_js: {
|
||||
options: {
|
||||
sourceMap: true
|
||||
},
|
||||
files: {
|
||||
'public/js/blue-twilight.min.js': ['public/js/blue-twilight.js']
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -121,7 +140,18 @@ module.exports = function(grunt)
|
||||
'concat:bt_js'
|
||||
]);
|
||||
|
||||
grunt.registerTask('build-css-release', [
|
||||
'build-css-debug',
|
||||
'cssmin:bt_css'
|
||||
]);
|
||||
|
||||
grunt.registerTask('build-js-release', [
|
||||
'build-js-debug',
|
||||
'uglify:bt_js'
|
||||
]);
|
||||
|
||||
// Shortcut tasks for the ones above
|
||||
grunt.registerTask('clean-all', ['clean:build_css', 'clean:build_js', 'clean:output']);
|
||||
grunt.registerTask('build-debug', ['clean-all', 'build-css-debug', 'build-js-debug']);
|
||||
grunt.registerTask('build-release', ['clean-all', 'build-css-release', 'build-js-release']);
|
||||
};
|
@ -2,7 +2,7 @@
|
||||
|
||||
return [
|
||||
// Version number of Blue Twilight
|
||||
'version' => '2.1.2',
|
||||
'version' => '2.2.0-beta.1',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace AppBootstrap;
|
||||
|
||||
use App\Services\GiteaService;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
|
||||
/**
|
||||
* This class handles the downloading and extracting of the vendors directory.
|
||||
@ -13,17 +14,54 @@ use App\Services\GiteaService;
|
||||
*/
|
||||
class Bootstrapper
|
||||
{
|
||||
/**
|
||||
* Path to /public/bootstrap
|
||||
* @var string
|
||||
*/
|
||||
private $bootstrapDir;
|
||||
|
||||
/**
|
||||
* Path to /app/config
|
||||
* @var string
|
||||
*/
|
||||
private $configDir;
|
||||
|
||||
/**
|
||||
* Path to / - the app's root
|
||||
* @var string
|
||||
*/
|
||||
private $rootDir;
|
||||
|
||||
/**
|
||||
* Path to /public/bootstrap/temp
|
||||
* @var string
|
||||
*/
|
||||
private $tempDir;
|
||||
|
||||
/**
|
||||
* Path to /vendor
|
||||
* @var string
|
||||
*/
|
||||
private $vendorDir;
|
||||
|
||||
/**
|
||||
* Path to /public/bootstrap/views
|
||||
* @var string
|
||||
*/
|
||||
private $viewsDir;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->rootDir = dirname(__DIR__);
|
||||
$this->configDir = sprintf('%s/config', dirname(dirname($this->rootDir)));
|
||||
$this->tempDir = sprintf('%s/temp', $this->rootDir);
|
||||
$this->viewsDir = sprintf('%s/views', $this->rootDir);
|
||||
$this->bootstrapDir = dirname(__DIR__);
|
||||
$this->rootDir = dirname(dirname($this->bootstrapDir));
|
||||
|
||||
$this->configDir = sprintf('%s/config', dirname(dirname($this->bootstrapDir)));
|
||||
$this->tempDir = sprintf('%s/temp', $this->bootstrapDir);
|
||||
$this->vendorDir = sprintf('%s/vendor', $this->rootDir);
|
||||
$this->viewsDir = sprintf('%s/views', $this->bootstrapDir);
|
||||
|
||||
chdir($this->rootDir);
|
||||
putenv('HOME=' . $this->rootDir);
|
||||
}
|
||||
|
||||
public function handleRequest()
|
||||
@ -40,13 +78,21 @@ class Bootstrapper
|
||||
$this->download();
|
||||
return;
|
||||
|
||||
case 'extract':
|
||||
$this->extract();
|
||||
return;
|
||||
|
||||
case 'finalise':
|
||||
$this->finalise();
|
||||
return;
|
||||
|
||||
default:
|
||||
throw new \Exception(sprintf('ERROR: Action \'%s\' was not recognised.', $_GET['act']));
|
||||
throw new \Exception(sprintf('Action \'%s\' was not recognised.', $_GET['act']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function download()
|
||||
protected function download()
|
||||
{
|
||||
$appConfig = require_once sprintf('%s/app.php', $this->configDir);
|
||||
$servicesConfig = require_once sprintf('%s/services.php', $this->configDir);
|
||||
@ -58,16 +104,17 @@ class Bootstrapper
|
||||
|
||||
if (is_null($releaseInfo))
|
||||
{
|
||||
throw new \Exception(sprintf('No release info found in Gitea for Blue Twilight version \'%\'', $versionNumber));
|
||||
throw new \Exception(sprintf('No release info found in Gitea for Blue Twilight version \'%s\'', $versionNumber));
|
||||
}
|
||||
else if (!isset($releaseInfo->assets))
|
||||
{
|
||||
throw new \Exception(sprintf('No assets found in Gitea for Blue Twilight version \'%\'', $versionNumber));
|
||||
throw new \Exception(sprintf('No assets found in Gitea for Blue Twilight version \'%s\'', $versionNumber));
|
||||
}
|
||||
|
||||
$vendorsPrefix = 'vendors';
|
||||
$vendorsSuffix = '.tar.gz';
|
||||
$selectedAsset = null;
|
||||
|
||||
foreach ($releaseInfo->assets as $asset)
|
||||
{
|
||||
/*
|
||||
@ -87,13 +134,68 @@ class Bootstrapper
|
||||
|
||||
if (is_null($selectedAsset))
|
||||
{
|
||||
throw new \Exception('No vendors.tar.gz found in Gitea for Blue Twilight version \'%\'', $versionNumber);
|
||||
throw new \Exception(sprintf('No vendors.tar.gz found in Gitea for Blue Twilight version \'%s\'', $versionNumber));
|
||||
}
|
||||
|
||||
$targetFileName = sprintf('%s/vendors.tar.gz', $this->tempDir);
|
||||
$targetFileName = $this->getVendorsTempFileName();
|
||||
|
||||
$this->downloadFile($selectedAsset->browser_download_url, $targetFileName);
|
||||
var_dump('done');
|
||||
$this->json([
|
||||
'result' => true,
|
||||
'fileName' => $targetFileName
|
||||
]);
|
||||
}
|
||||
|
||||
protected function extract()
|
||||
{
|
||||
$targetFileName = $this->getVendorsTempFileName();
|
||||
if (!file_exists($targetFileName) || !is_readable($targetFileName))
|
||||
{
|
||||
throw new \Exception(sprintf('The file \'%s\' does not exist or is not readable', $targetFileName));
|
||||
}
|
||||
|
||||
$phar = new \PharData($targetFileName);
|
||||
$phar->extractTo($this->rootDir, null, true);
|
||||
|
||||
// We should always have a vendor/autoload.php
|
||||
$vendorsTestFile = $this->getVendorsAutoloadFileName();
|
||||
|
||||
if (file_exists($vendorsTestFile))
|
||||
{
|
||||
$this->json([
|
||||
'result' => true,
|
||||
'testFile' => $vendorsTestFile
|
||||
]);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new \Exception('The extraction failed');
|
||||
}
|
||||
}
|
||||
|
||||
protected function finalise()
|
||||
{
|
||||
$result = [
|
||||
'envFileCreated' => false
|
||||
];
|
||||
|
||||
$result['envFileCreated'] = $this->createEnvFileIfNotExist();
|
||||
|
||||
require sprintf('%s/bootstrap/autoload.php', $this->rootDir);
|
||||
|
||||
$app = require_once sprintf('%s/bootstrap/app.php', $this->rootDir);
|
||||
|
||||
$kernel = $app->make(\Illuminate\Contracts\Console\Kernel::class);
|
||||
$kernel->bootstrap();
|
||||
|
||||
if ($result['envFileCreated'])
|
||||
{
|
||||
Artisan::call('key:generate');
|
||||
}
|
||||
|
||||
$kernel->terminate(null, null);
|
||||
|
||||
$this->json($result);
|
||||
}
|
||||
|
||||
private function downloadFile($sourceURL, $targetFilename)
|
||||
@ -120,6 +222,44 @@ class Bootstrapper
|
||||
@fclose($tempFilename);
|
||||
}
|
||||
|
||||
private function createEnvFileIfNotExist()
|
||||
{
|
||||
$envFile = $this->getEnvFileName();
|
||||
|
||||
if (!file_exists($envFile))
|
||||
{
|
||||
copy($this->getEnvExampleFileName(), $envFile);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function getEnvExampleFileName()
|
||||
{
|
||||
return sprintf('%s/.env.example', $this->rootDir);
|
||||
}
|
||||
|
||||
private function getEnvFileName()
|
||||
{
|
||||
return sprintf('%s/.env', $this->rootDir);
|
||||
}
|
||||
|
||||
private function getVendorsAutoloadFileName()
|
||||
{
|
||||
return sprintf('%s/autoload.php', $this->vendorDir);
|
||||
}
|
||||
|
||||
private function getVendorsTempFileName()
|
||||
{
|
||||
return sprintf('%s/vendors.tar.gz', $this->tempDir);
|
||||
}
|
||||
|
||||
private function json($data)
|
||||
{
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
private function view($name, array $viewData = [])
|
||||
{
|
||||
$viewFile = sprintf('%s/%s.php', $this->viewsDir, $name);
|
||||
|
@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="stylesheet" href="../css/blue-twilight.css">
|
||||
<link rel="stylesheet" href="../css/blue-twilight.min.css">
|
||||
<title><?php echo $appName; ?></title>
|
||||
<style type="text/css">
|
||||
* {
|
||||
@ -28,7 +28,7 @@
|
||||
<p class="mb-0 mt-4"><button class="btn btn-primary btn-lg" @click.prevent="bootstrap">Let's Go</button></p>
|
||||
</div>
|
||||
<div v-else>
|
||||
<ul>
|
||||
<ul v-cloak>
|
||||
<li class="operation mb-3" v-for="operation in operations">
|
||||
<div class="status mr-1">
|
||||
<img src="images/waiting.svg" v-if="!operation.isRunning && !operation.isCompleted"></img>
|
||||
@ -45,7 +45,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="../js/blue-twilight.js"></script>
|
||||
<script src="../js/blue-twilight.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(function()
|
||||
{
|
||||
|
@ -10260,6 +10260,29 @@ a.text-dark:hover, a.text-dark:focus {
|
||||
[v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
#bootstrapper [v-cloak] {
|
||||
display: none;
|
||||
}
|
||||
#bootstrapper .operation .status {
|
||||
display: inline-block;
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
}
|
||||
#bootstrapper .operation .status i,
|
||||
#bootstrapper .operation .status img {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
#bootstrapper .operation .status span {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#bootstrapper ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.activity-grid {
|
||||
font-size: smaller;
|
||||
}
|
||||
|
6
public/css/blue-twilight.min.css
vendored
Normal file
6
public/css/blue-twilight.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -35700,6 +35700,12 @@ function AnalyseAlbumViewModel() {
|
||||
});
|
||||
item.isSuccessful = false;
|
||||
item.isPending = false;
|
||||
|
||||
var indexToRemove = self.imagesInProgress.indexOf(item);
|
||||
if (indexToRemove > -1)
|
||||
{
|
||||
self.imagesInProgress.splice(indexToRemove, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -35824,6 +35830,7 @@ function EditPhotosViewModel(album_id, language, urls) {
|
||||
deletePhoto: function (e) {
|
||||
var self = this;
|
||||
this.selectPhotoSingle(e.target);
|
||||
var parent = $(e.target).closest('.photo');
|
||||
|
||||
var photo_id = self.photoIDs[0];
|
||||
this.photoIDs = [];
|
||||
@ -35846,7 +35853,7 @@ function EditPhotosViewModel(album_id, language, urls) {
|
||||
$('.loading', parent).show();
|
||||
|
||||
$.post(url, {'_method': 'DELETE'}, function (data) {
|
||||
window.location.reload();
|
||||
$(parent).remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -35875,10 +35882,11 @@ function EditPhotosViewModel(album_id, language, urls) {
|
||||
}
|
||||
|
||||
$('.loading', parent).show();
|
||||
$.post(url, function () {
|
||||
$.post(url, function (response) {
|
||||
var image = $('img.photo-thumbnail', parent);
|
||||
var originalUrl = image.data('original-src');
|
||||
image.attr('src', originalUrl + "&_=" + new Date().getTime());
|
||||
|
||||
// response from server is the URL to the modified image
|
||||
image.attr('src', response);
|
||||
|
||||
$('.loading', parent).hide();
|
||||
});
|
||||
@ -35958,10 +35966,10 @@ function EditPhotosViewModel(album_id, language, urls) {
|
||||
url = url.replace(/\/0$/, '/' + this.photoIDs[0]);
|
||||
|
||||
$('.loading', parent).show();
|
||||
$.post(url, function () {
|
||||
$.post(url, function (response) {
|
||||
var image = $('img.photo-thumbnail', parent);
|
||||
var originalUrl = image.data('original-src');
|
||||
image.attr('src', originalUrl + "&_=" + new Date().getTime());
|
||||
|
||||
image.attr('src', response.thumbnail_url);
|
||||
|
||||
$('.loading', parent).hide();
|
||||
});
|
||||
@ -36310,6 +36318,79 @@ function UploadPhotosViewModel(album_id, queue_token, language, urls) {
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* This model is used by the system bootstrapper in public/bootstrap.
|
||||
* @constructor
|
||||
*/
|
||||
function BootstrapperViewModel() {
|
||||
this.el = '#bootstrapper';
|
||||
this.data = {
|
||||
isCompleted: false,
|
||||
isRunning: false,
|
||||
operations: []
|
||||
}
|
||||
this.methods = {
|
||||
bootstrap: function()
|
||||
{
|
||||
this.operations.push({
|
||||
'isCompleted': false,
|
||||
'isRunning': false,
|
||||
'name': 'Removing any previous versions',
|
||||
'url': '?act=removePrevious'
|
||||
});
|
||||
|
||||
this.operations.push({
|
||||
'isCompleted': false,
|
||||
'isRunning': false,
|
||||
'name': 'Downloading new files',
|
||||
'url': '?act=download'
|
||||
});
|
||||
|
||||
this.operations.push({
|
||||
'isCompleted': false,
|
||||
'isRunning': false,
|
||||
'name': 'Extracting new files',
|
||||
'url': '?act=extract'
|
||||
});
|
||||
|
||||
this.operations.push({
|
||||
'isCompleted': false,
|
||||
'isRunning': false,
|
||||
'name': 'Cleaning up',
|
||||
'url': '?act=finalise'
|
||||
});
|
||||
|
||||
this.isRunning = true;
|
||||
|
||||
this.runOperation(this.operations[0], 0);
|
||||
},
|
||||
runOperation: function(operation, index)
|
||||
{
|
||||
var self = this;
|
||||
operation.isRunning = true;
|
||||
|
||||
$.post(operation.url)
|
||||
.done(function(result)
|
||||
{
|
||||
operation.isRunning = false;
|
||||
operation.isCompleted = true;
|
||||
|
||||
index++;
|
||||
if (index < self.operations.length)
|
||||
{
|
||||
self.runOperation(self.operations[index], index);
|
||||
}
|
||||
else
|
||||
{
|
||||
//self.isRunning = false;
|
||||
self.isCompleted = true;
|
||||
|
||||
window.location = '../';
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
/*!
|
||||
* Chart.js
|
||||
* http://chartjs.org/
|
||||
@ -54804,6 +54885,27 @@ module.exports = function(Chart) {
|
||||
|
||||
},{"25":25,"45":45,"6":6}]},{},[7])(7)
|
||||
});
|
||||
function ExternalServiceViewModel()
|
||||
{
|
||||
this.el = '#external-service-options';
|
||||
this.data = {
|
||||
service_type: ''
|
||||
};
|
||||
this.computed = {
|
||||
hasOAuthStandardOptions: function()
|
||||
{
|
||||
// This logic must be mirrored in App\ExternalService
|
||||
return this.service_type === 'facebook' ||
|
||||
this.service_type === 'google' ||
|
||||
this.service_type === 'twitter';
|
||||
},
|
||||
isDropbox: function()
|
||||
{
|
||||
// This logic must be mirrored in App\ExternalService
|
||||
return this.service_type === 'dropbox';
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This model is used by gallery/explore_users.blade.php, to handle following/unfollowing users profiles.
|
||||
* @constructor
|
||||
|
2
public/js/blue-twilight.min.js
vendored
Normal file
2
public/js/blue-twilight.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
public/js/blue-twilight.min.js.map
Normal file
1
public/js/blue-twilight.min.js.map
Normal file
File diff suppressed because one or more lines are too long
8
resources/js/bootstrapper.js
vendored
8
resources/js/bootstrapper.js
vendored
@ -10,7 +10,7 @@ function BootstrapperViewModel() {
|
||||
operations: []
|
||||
}
|
||||
this.methods = {
|
||||
bootstrap()
|
||||
bootstrap: function()
|
||||
{
|
||||
this.operations.push({
|
||||
'isCompleted': false,
|
||||
@ -37,14 +37,14 @@ function BootstrapperViewModel() {
|
||||
'isCompleted': false,
|
||||
'isRunning': false,
|
||||
'name': 'Cleaning up',
|
||||
'url': '?act=cleanup'
|
||||
'url': '?act=finalise'
|
||||
});
|
||||
|
||||
this.isRunning = true;
|
||||
|
||||
this.runOperation(this.operations[0], 0);
|
||||
},
|
||||
runOperation(operation, index)
|
||||
runOperation: function(operation, index)
|
||||
{
|
||||
var self = this;
|
||||
operation.isRunning = true;
|
||||
@ -64,6 +64,8 @@ function BootstrapperViewModel() {
|
||||
{
|
||||
//self.isRunning = false;
|
||||
self.isCompleted = true;
|
||||
|
||||
window.location = '../';
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -5,14 +5,14 @@ function ExternalServiceViewModel()
|
||||
service_type: ''
|
||||
};
|
||||
this.computed = {
|
||||
hasOAuthStandardOptions()
|
||||
hasOAuthStandardOptions: function()
|
||||
{
|
||||
// This logic must be mirrored in App\ExternalService
|
||||
return this.service_type === 'facebook' ||
|
||||
this.service_type === 'google' ||
|
||||
this.service_type === 'twitter';
|
||||
},
|
||||
isDropbox()
|
||||
isDropbox: function()
|
||||
{
|
||||
// This logic must be mirrored in App\ExternalService
|
||||
return this.service_type === 'dropbox';
|
||||
|
2
resources/sass/bootstrapper.scss
vendored
2
resources/sass/bootstrapper.scss
vendored
@ -1,5 +1,7 @@
|
||||
#bootstrapper
|
||||
{
|
||||
[v-cloak] { display: none }
|
||||
|
||||
.operation
|
||||
{
|
||||
.status
|
||||
|
Loading…
Reference in New Issue
Block a user