*/
public function index()
{
- $books = $this->entityRepo->getAllPaginated('book', 16);
+ $books = $this->entityRepo->getAllPaginated('book', 20);
$recents = $this->signedIn ? $this->entityRepo->getRecentlyViewed('book', 4, 0) : false;
- $popular = $this->entityRepo->getPopular('book', 3, 0);
- $books_display = $this->currentUser->books_display;
+ $popular = $this->entityRepo->getPopular('book', 4, 0);
+ $new = $this->entityRepo->getRecentlyCreated('book', 4, 0);
$this->setPageTitle('Books');
- return view('books/index', ['books' => $books, 'recents' => $recents, 'popular' => $popular, 'books_display' => $books_display] );
+ return view('books/index', [
+ 'books' => $books,
+ 'recents' => $recents,
+ 'popular' => $popular,
+ 'new' => $new
+ ]);
}
/**
$bookChildren = $this->entityRepo->getBookChildren($book);
Views::add($book);
$this->setPageTitle($book->getShortName());
- return view('books/show', ['book' => $book, 'current' => $book, 'bookChildren' => $bookChildren]);
+ return view('books/show', [
+ 'book' => $book,
+ 'current' => $book,
+ 'bookChildren' => $bookChildren,
+ 'activity' => Activity::entityActivity($book, 20, 0)
+ ]);
}
/**
'name' => 'required|string|max:255',
'description' => 'string|max:1000'
]);
- $book = $this->entityRepo->updateFromInput('book', $book, $request->all());
- Activity::add($book, 'book_update', $book->id);
- return redirect($book->getUrl());
+ $book = $this->entityRepo->updateFromInput('book', $book, $request->all());
+ Activity::add($book, 'book_update', $book->id);
+ return redirect($book->getUrl());
}
/**
"use strict";
require("babel-polyfill");
+ require('./dom-polyfills');
// Url retrieval function
window.baseUrl = function(path) {
class EventManager {
constructor() {
this.listeners = {};
+ this.stack = [];
}
emit(eventName, eventData) {
+ this.stack.push({name: eventName, data: eventData});
if (typeof this.listeners[eventName] === 'undefined') return this;
let eventsToStart = this.listeners[eventName];
for (let i = 0; i < eventsToStart.length; i++) {
}
}
- window.Events = new EventManager();
+ window.$events = new EventManager();
const Vue = require("vue");
const axios = require("axios");
return resp;
}, err => {
if (typeof err.response === "undefined" || typeof err.response.data === "undefined") return Promise.reject(err);
- if (typeof err.response.data.error !== "undefined") window.Events.emit('error', err.response.data.error);
- if (typeof err.response.data.message !== "undefined") window.Events.emit('error', err.response.data.message);
+ if (typeof err.response.data.error !== "undefined") window.$events.emit('error', err.response.data.error);
+ if (typeof err.response.data.message !== "undefined") window.$events.emit('error', err.response.data.message);
});
window.$http = axiosInstance;
Vue.prototype.$http = axiosInstance;
- Vue.prototype.$events = window.Events;
+ Vue.prototype.$events = window.$events;
// AngularJS - Create application and load components
// Load in angular specific items
const Directives = require('./directives');
const Controllers = require('./controllers');
- Directives(ngApp, window.Events);
- Controllers(ngApp, window.Events);
+ Directives(ngApp, window.$events);
+ Controllers(ngApp, window.$events);
//Global jQuery Config & Extensions
};
});
+// Global jQuery Elements
+let notifications = $('.notification');
+let successNotification = notifications.filter('.pos');
+let errorNotification = notifications.filter('.neg');
+let warningNotification = notifications.filter('.warning');
+// Notification Events
+window.Events.listen('success', function (text) {
+ successNotification.hide();
+ successNotification.find('span').text(text);
+ setTimeout(() => {
+ successNotification.show();
+ }, 1);
+});
+window.Events.listen('warning', function (text) {
+ warningNotification.find('span').text(text);
+ warningNotification.show();
+});
+window.Events.listen('error', function (text) {
+ errorNotification.find('span').text(text);
+ errorNotification.show();
+});
+
+// Notification hiding
+notifications.click(function () {
+ $(this).fadeOut(100);
+});
+
+// Chapter page list toggles
+$('.chapter-toggle').click(function (e) {
+ e.preventDefault();
+ $(this).toggleClass('open');
+ $(this).closest('.chapter').find('.inset-list').slideToggle(180);
+});
+
+// Back to top button
+$('#back-to-top').click(function() {
+ $('#header').smoothScrollTo();
+});
+let scrollTopShowing = false;
+let scrollTop = document.getElementById('back-to-top');
+let scrollTopBreakpoint = 1200;
+window.addEventListener('scroll', function() {
+ let scrollTopPos = document.documentElement.scrollTop || document.body.scrollTop || 0;
+ if (!scrollTopShowing && scrollTopPos > scrollTopBreakpoint) {
+ scrollTop.style.display = 'block';
+ scrollTopShowing = true;
+ setTimeout(() => {
+ scrollTop.style.opacity = 0.4;
+ }, 1);
+ } else if (scrollTopShowing && scrollTopPos < scrollTopBreakpoint) {
+ scrollTop.style.opacity = 0;
+ scrollTopShowing = false;
+ setTimeout(() => {
+ scrollTop.style.display = 'none';
+ }, 500);
+ }
+});
+
+// Common jQuery actions
+$('[data-action="expand-entity-list-details"]').click(function() {
+ $('.entity-list.compact').find('p').not('.empty-text').slideToggle(240);
+});
+
+// Toggle thumbnail::hide image and reduce grid size
+$(document).ready(function(){
+ $('[data-action="expand-thumbnail"]').click(function(){
+ $('.gallery-item').toggleClass("collapse").find('img').slideToggle(50);
+ });
+});
+
+// Popup close
+$('.popup-close').click(function() {
+ $(this).closest('.overlay').fadeOut(240);
+});
+$('.overlay').click(function(event) {
+ if (!$(event.target).hasClass('overlay')) return;
+ $(this).fadeOut(240);
+});
+
// Detect IE for css
if(navigator.userAgent.indexOf('MSIE')!==-1
|| navigator.appVersion.indexOf('Trident/') > 0
}
.center-box {
- margin: $-xl auto 0 auto;
- padding: $-m $-xxl $-xl $-xxl;
+ margin: $-xxl auto 0 auto;
width: 420px;
max-width: 100%;
display: inline-block;
text-align: left;
vertical-align: top;
- //border: 1px solid #DDD;
input {
width: 100%;
}
- &.login {
- background-color: #EEE;
- box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.1);
- border: 1px solid #DDD;
- }
}
+.gallery-item {
+ margin-bottom: 32px;
+ height: 350px;
+ overflow: hidden;
+ border: 1px solid #ccc;
+ h4 {
+ font-size: 1.2em;
+ text-align: center;
+ height: 55px;
+ padding: 0px 12px;
+ }
+ p {
+ font-size: 0.8em;
+ text-align: center;
+ padding: 0px 12px;
+ }
+ &.collapse {
+ height: 150px;
+ }
+}
+
+.gallery-image {
+ margin-top: 5%;
+ text-align: center;
+ img {
+ border-radius: 3px;
+ }
+}
+
+.cover {
+ height: 192px;
+ width: 120px;
+ border-radius: 3px;
+ }
'save' => 'Save',
'continue' => 'Continue',
'select' => 'Select',
+ 'more' => 'More',
/**
* Form Labels
'name' => 'Name',
'description' => 'Description',
'role' => 'Role',
-
+ 'cover_image' => 'Cover image',
+ 'cover_image_description' => 'The image should be in a height/width ratio of 1.6:1.',
/**
* Actions
*/
'remove' => 'Remove',
'add' => 'Add',
-
/**
* Misc
*/
'no_items' => 'No items available',
'back_to_top' => 'Back to top',
'toggle_details' => 'Toggle Details',
-
+ 'toggle_thumbnails' => 'Toggle Thumbnails',
-
+ 'details' => 'Details',
/**
* Header
*/
'app_logo_desc' => 'This image should be 43px in height. <br>Large images will be scaled down.',
'app_primary_color' => 'Application primary color',
'app_primary_color_desc' => 'This should be a hex value. <br>Leave empty to reset to the default color.',
+ 'app_homepage' => 'Application Homepage',
+ 'app_homepage_desc' => 'Select a page to show on the homepage instead of the default view. Page permissions are ignored for selected pages.',
+ 'app_homepage_default' => 'Default homepage view chosen',
/**
* Registration settings
'users_external_auth_id' => 'External Authentication ID',
'users_password_warning' => 'Only fill the below if you would like to change your password:',
'users_system_public' => 'This user represents any guest users that visit your instance. It cannot be used to log in but is assigned automatically.',
+ 'users_books_display_type' => 'Preferred layout for books viewing',
'users_delete' => 'Delete User',
'users_delete_named' => 'Delete user :userName',
'users_delete_warning' => 'This will fully delete this user with the name \':userName\' from the system.',
- @extends('base')
+ @extends('simple-layout')
- @section('content')
+ @section('toolbar')
+ <div class="col-sm-8 faded">
+ <div class="breadcrumbs">
+ <a href="{{ baseUrl('/books') }}" class="text-button"><i class="zmdi zmdi-book"></i>{{ trans('entities.books') }}</a>
+ <span class="sep">»</span>
+ <a href="{{ baseUrl('/books/create') }}" class="text-button"><i class="zmdi zmdi-plus"></i>{{ trans('entities.books_create') }}</a>
+ </div>
+ </div>
+ @stop
- <div class="container small" ng-non-bindable>
- <h1>{{ trans('entities.books_create') }}</h1>
- <form action="{{ baseUrl("/books") }}" method="POST" enctype="multipart/form-data">
- @include('books/form')
- </form>
+ @section('body')
+
+ <div ng-non-bindable class="container small">
+ <p> </p>
+ <div class="card">
+ <h3><i class="zmdi zmdi-plus"></i> {{ trans('entities.books_create') }}</h3>
+ <div class="body">
- <form action="{{ baseUrl("/books") }}" method="POST">
++ <form action="{{ baseUrl("/books") }}" method="POST" enctype="multipart/form-data">
+ @include('books/form')
+ </form>
+ </div>
+ </div>
</div>
-
+<p class="margin-top large"><br></p>
+ @include('components.image-manager', ['imageType' => 'cover'])
@stop
- @extends('base')
+ @extends('simple-layout')
- @section('content')
-
- <div class="faded-small toolbar">
- <div class="container">
- <div class="row">
- <div class="col-sm-12 faded">
- @include('books._breadcrumbs', ['book' => $book])
- </div>
- </div>
- </div>
+ @section('toolbar')
+ <div class="col-sm-12 faded">
+ @include('books._breadcrumbs', ['book' => $book])
</div>
+ @stop
+
+ @section('body')
<div class="container small" ng-non-bindable>
- <h1>{{ trans('entities.books_edit') }}</h1>
- <form action="{{ $book->getUrl() }}" method="POST">
- <input type="hidden" name="_method" value="PUT">
- @include('books/form', ['model' => $book])
- </form>
+ <p> </p>
+ <div class="card">
+ <h3><i class="zmdi zmdi-edit"></i> {{ trans('entities.books_edit') }}</h3>
+ <div class="body">
+ <form action="{{ $book->getUrl() }}" method="POST">
+ <input type="hidden" name="_method" value="PUT">
+ @include('books/form', ['model' => $book])
+ </form>
+ </div>
+ </div>
</div>
-
+@include('components.image-manager', ['imageType' => 'cover'])
@stop
<label for="description">{{ trans('common.description') }}</label>
@include('form/textarea', ['name' => 'description'])
</div>
- <div class="form-group">
- <a href="{{ isset($book) ? $book->getUrl() : baseUrl('/books') }}" class="button muted">{{ trans('common.cancel') }}</a>
+<div class="form-group" id="logo-control">
+ <label for="user-avatar">{{ trans('common.cover_image') }}</label>
+ <p class="small">{{ trans('common.cover_image_description') }}</p>
+
+ @include('components.image-picker', [
+ 'resizeHeight' => '192',
+ 'resizeWidth' => '120',
+ 'showRemove' => true,
+ 'defaultImage' => baseUrl('/default.png'),
+ 'currentImage' => @isset($model) ? $model->getBookCover(80) : baseUrl('/default.png') ,
+ 'currentId' => @isset($model) ? $model->image : 0,
+ 'name' => 'image',
+ 'imageClass' => 'cover'
+ ])
+</div>
+
+ <div class="form-group text-right">
+ <a href="{{ isset($book) ? $book->getUrl() : baseUrl('/books') }}" class="button outline">{{ trans('common.cancel') }}</a>
<button type="submit" class="button pos">{{ trans('entities.books_save') }}</button>
</div>
- @extends('base')
-
-
- @section('content')
+ @extends('simple-layout')
+ @section('toolbar')
@include('settings/navbar', ['selected' => 'users'])
+ @stop
+
+ @section('body')
<div class="container small">
- <form action="{{ baseUrl("/settings/users/{$user->id}") }}" method="post">
- <div class="row">
- <div class="col-sm-8">
- <h1>{{ $user->id === $currentUser->id ? trans('settings.users_edit_profile') : trans('settings.users_edit') }}</h1>
- </div>
- <div class="col-sm-4">
- <p></p>
- @if($authMethod !== 'system')
- <a href="{{ baseUrl("/settings/users/{$user->id}/delete") }}" class="neg button float right">{{ trans('settings.users_delete') }}</a>
- @endif
- </div>
- </div>
- <div class="row">
- <div class="col-sm-6" ng-non-bindable>
- {!! csrf_field() !!}
- <input type="hidden" name="_method" value="put">
- @include('users.forms.' . $authMethod, ['model' => $user])
+ <p> </p>
+ <div class="card">
+ <h3><i class="zmdi-edit zmdi"></i> {{ $user->id === $currentUser->id ? trans('settings.users_edit_profile') : trans('settings.users_edit') }}</h3>
+ <div class="body">
+ <form action="{{ baseUrl("/settings/users/{$user->id}") }}" method="post">
+ <div class="row">
+ <div class="col-sm-6" ng-non-bindable>
+ {!! csrf_field() !!}
+ <input type="hidden" name="_method" value="put">
+ @include('users.forms.' . $authMethod, ['model' => $user])
- </div>
- <div class="col-sm-6">
- <div class="form-group" id="logo-control">
- <label for="user-avatar">{{ trans('settings.users_avatar') }}</label>
- <p class="small">{{ trans('settings.users_avatar_desc') }}</p>
+ </div>
+ <div class="col-sm-6">
+ <div class="form-group" id="logo-control">
+ <label for="user-avatar">{{ trans('settings.users_avatar') }}</label>
+ <p class="small">{{ trans('settings.users_avatar_desc') }}</p>
- @include('components.image-picker', [
- 'resizeHeight' => '512',
- 'resizeWidth' => '512',
- 'showRemove' => false,
- 'defaultImage' => baseUrl('/user_avatar.png'),
- 'currentImage' => $user->getAvatar(80),
- 'currentId' => $user->image_id,
- 'name' => 'image_id',
- 'imageClass' => 'avatar large'
- ])
- </div>
- <div class="form-group">
- <label for="user-language">{{ trans('settings.users_preferred_language') }}</label>
- <select name="setting[language]" id="user-language">
- @foreach(trans('settings.language_select') as $lang => $label)
- <option @if(setting()->getUser($user, 'language') === $lang) selected @endif value="{{ $lang }}">{{ $label }}</option>
- @endforeach
- </select>
+ @include('components.image-picker', [
+ 'resizeHeight' => '512',
+ 'resizeWidth' => '512',
+ 'showRemove' => false,
+ 'defaultImage' => baseUrl('/user_avatar.png'),
+ 'currentImage' => $user->getAvatar(80),
+ 'currentId' => $user->image_id,
+ 'name' => 'image_id',
+ 'imageClass' => 'avatar large'
+ ])
+ </div>
+ <div class="form-group">
+ <label for="user-language">{{ trans('settings.users_preferred_language') }}</label>
+ <select name="setting[language]" id="user-language">
+ @foreach(trans('settings.language_select') as $lang => $label)
+ <option @if(setting()->getUser($user, 'language') === $lang) selected @endif value="{{ $lang }}">{{ $label }}</option>
+ @endforeach
+ </select>
+ </div>
+ </div>
</div>
- <div class="form-group">
- <label for="books_display">{{ trans('settings.users_books_display_type') }}</label>
- <select name="books_display" id="books_display">
- <option @if($user->books_display === 'grid') selected @endif value="grid">Grid</option>
- <option @if($user->books_display === 'list') selected @endif value="list">List</option>
- </select>
+ <div class="form-group text-right">
+ <a href="{{ baseUrl($currentUser->can('users-manage') ? "/settings/users" : "/") }}" class="button outline">{{ trans('common.cancel') }}</a>
+ @if($authMethod !== 'system')
+ <a href="{{ baseUrl("/settings/users/{$user->id}/delete") }}" class="neg button">{{ trans('settings.users_delete') }}</a>
+ @endif
+ <button class="button pos" type="submit">{{ trans('common.save') }}</button>
</div>
- </div>
- </div>
- <div class="form-group">
- <a href="{{ baseUrl($currentUser->can('users-manage') ? "/settings/users" : "/") }}" class="button muted">{{ trans('common.cancel') }}</a>
- <button class="button pos" type="submit">{{ trans('common.save') }}</button>
+ </form>
</div>
- </form>
-
- <hr class="margin-top large">
+ </div>
@if($currentUser->id === $user->id && count($activeSocialDrivers) > 0)
- <h3>{{ trans('settings.users_social_accounts') }}</h3>
- <p class="text-muted">{{ trans('settings.users_social_accounts_info') }}</p>
- <div class="row">
- @foreach($activeSocialDrivers as $driver => $enabled)
- <div class="col-sm-3 col-xs-6 text-center">
- <div>@icon($driver, ['width' => 56])</div>
- <div>
- @if($user->hasSocialAccount($driver))
- <a href="{{ baseUrl("/login/service/{$driver}/detach") }}" class="button neg">{{ trans('settings.users_social_disconnect') }}</a>
- @else
- <a href="{{ baseUrl("/login/service/{$driver}") }}" class="button pos">{{ trans('settings.users_social_connect') }}</a>
- @endif
+ <div class="card">
+ <h3><i class="zmdi zmdi-sign-in"></i> {{ trans('settings.users_social_accounts') }}</h3>
+ <div class="body">
+ <p class="text-muted">{{ trans('settings.users_social_accounts_info') }}</p>
+ <div class="container">
+ <div class="row">
+ @foreach($activeSocialDrivers as $driver => $enabled)
+ <div class="col-sm-4 col-xs-6 text-center">
+ <div>@icon($driver, ['width' => 56])</div>
+ <div>
+ @if($user->hasSocialAccount($driver))
+ <a href="{{ baseUrl("/login/service/{$driver}/detach") }}" class="button neg">{{ trans('settings.users_social_disconnect') }}</a>
+ @else
+ <a href="{{ baseUrl("/login/service/{$driver}") }}" class="button pos">{{ trans('settings.users_social_connect') }}</a>
+ @endif
+ </div>
+ <div> </div>
+ </div>
+ @endforeach
</div>
</div>
- @endforeach
+ </div>
</div>
@endif
<p class="margin-top large"><br></p>
@include('components.image-manager', ['imageType' => 'user'])
--@stop
++@stop