#3: Merged the two photo charts into one and added a "number at-a-glance" widget on the statistics page

This commit is contained in:
Andy Heathershaw 2017-09-10 17:02:15 +01:00
parent c72c4cc45c
commit b7285888cf
16 changed files with 4800 additions and 70 deletions

View File

@ -96,6 +96,7 @@ class ConfigHelper
'hotlink_protection' => false,
'items_per_page' => 12,
'items_per_page_admin' => 10,
'public_statistics' => true,
'recaptcha_enabled_registration' => false,
'recaptcha_secret_key' => '',
'recaptcha_site_key' => '',

View File

@ -0,0 +1,24 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Configuration;
use App\Facade\UserConfig;
use App\Http\Controllers\Controller;
use Symfony\Component\HttpFoundation\Request;
class StatisticsController extends Controller
{
public function save(Request $request)
{
$isPublicStatsEnabled = strtolower($request->get('enable_public_statistics')) == 'on';
/** @var Configuration $config */
$config = UserConfig::getOrCreateModel('public_statistics');
$config->value = $isPublicStatsEnabled;
$config->save();
$request->session()->flash('success', trans('admin.statistics_prefs_saved_message'));
return redirect(route('statistics.index'));
}
}

View File

@ -3,7 +3,9 @@
namespace App\Http\Controllers\Gallery;
use App\Facade\Theme;
use App\Helpers\DbHelper;
use App\Http\Controllers\Controller;
use App\Label;
use App\Photo;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
@ -12,7 +14,10 @@ class StatisticsController extends Controller
{
public function albumSizeByPhotosChart(Request $request)
{
$this->authorizeForUser($this->getUser(), 'statistics.public-access');
$stats = DB::table('photos')
->whereIn('photos.album_id', DbHelper::getAlbumIDsForCurrentUser())
->join('albums', 'albums.id', '=', 'photos.album_id')
->groupBy('albums.name')
->select('albums.name', DB::raw('count(photos.id) as photo_count'))
@ -38,7 +43,10 @@ class StatisticsController extends Controller
public function albumSizeByPhotoSizeChart(Request $request)
{
$this->authorizeForUser($this->getUser(), 'statistics.public-access');
$stats = DB::table('photos')
->whereIn('photos.album_id', DbHelper::getAlbumIDsForCurrentUser())
->join('albums', 'albums.id', '=', 'photos.album_id')
->groupBy('albums.name')
->select('albums.name', DB::raw('sum(photos.file_size) as photo_size'))
@ -64,11 +72,14 @@ class StatisticsController extends Controller
public function camerasChart(Request $request)
{
$this->authorizeForUser($this->getUser(), 'statistics.public-access');
$stats = DB::table('photos')
->where([
['camera_make', '!=', ''],
['camera_model', '!=', '']
])
->whereIn('album_id', DbHelper::getAlbumIDsForCurrentUser())
->groupBy('camera_make', 'camera_model')
->select('camera_make', 'camera_model', DB::raw('count(*) as photo_count'))
->orderBy('photo_count', 'desc')
@ -99,6 +110,8 @@ class StatisticsController extends Controller
public function fileSizeChart(Request $request)
{
$this->authorizeForUser($this->getUser(), 'statistics.public-access');
$labels = [
trans('gallery.statistics.file_sizes_legend.small'),
trans('gallery.statistics.file_sizes_legend.medium'),
@ -107,7 +120,7 @@ class StatisticsController extends Controller
];
$data = [0, 0, 0, 0];
$stats = DB::table('photos');
$stats = DB::table('photos')->whereIn('album_id', DbHelper::getAlbumIDsForCurrentUser());
$stats->chunk(100, function($photos) use (&$data)
{
foreach ($photos as $photo)
@ -140,15 +153,59 @@ class StatisticsController extends Controller
public function index(Request $request)
{
return Theme::render('gallery.statistics');
$this->authorizeForUser($this->getUser(), 'statistics.public-access');
// Numbers for at-a-glance
$albumIDs = DbHelper::getAlbumIDsForCurrentUser();
$albumCount = count($albumIDs);
$labelCount = Label::all()->count();
$photoCount = Photo::whereIn('album_id', $albumIDs)->count();
return Theme::render('gallery.statistics', [
'album_count' => $albumCount,
'label_count' => $labelCount,
'photo_count' => $photoCount
]);
}
public function photosCombined(Request $request)
{
$this->authorizeForUser($this->getUser(), 'statistics.public-access');
$labels = [];
$data = [
['label' => trans('gallery.statistics.photos_combined.taken'), 'values' => []],
['label' => trans('gallery.statistics.photos_combined.uploaded'), 'values' => []]
];
foreach ($this->lastXMonthsDates(18) as $date)
{
$fromDate = sprintf('%04d-%02d-01 00:00:00', $date[0], $date[1]);
$toDate = sprintf('%04d-%02d-%02d 23:59:59', $date[0], $date[1], cal_days_in_month(CAL_GREGORIAN, $date[1], $date[0]));
$photoCountTaken = Photo::whereBetween('taken_at', array($fromDate, $toDate))->count();
$photoCountUploaded = Photo::whereBetween('created_at', array($fromDate, $toDate))->count();
$labels[] = date('M Y', strtotime($fromDate));
$data[0]['values'][] = $photoCountTaken;
$data[1]['values'][] = $photoCountUploaded;
}
return response()->json([
'labels' => array_reverse($labels),
'data' => array_reverse($data)
]);
}
public function photosTaken12Months(Request $request)
{
$this->authorizeForUser($this->getUser(), 'statistics.public-access');
$labels = [];
$data = [];
foreach ($this->last12MonthsDates() as $date)
foreach ($this->lastXMonthsDates() as $date)
{
$fromDate = sprintf('%04d-%02d-01 00:00:00', $date[0], $date[1]);
$toDate = sprintf('%04d-%02d-%02d 23:59:59', $date[0], $date[1], cal_days_in_month(CAL_GREGORIAN, $date[1], $date[0]));
@ -167,10 +224,12 @@ class StatisticsController extends Controller
public function photosUploaded12Months(Request $request)
{
$this->authorizeForUser($this->getUser(), 'statistics.public-access');
$labels = [];
$data = [];
foreach ($this->last12MonthsDates() as $date)
foreach ($this->lastXMonthsDates() as $date)
{
$fromDate = sprintf('%04d-%02d-01 00:00:00', $date[0], $date[1]);
$toDate = sprintf('%04d-%02d-%02d 23:59:59', $date[0], $date[1], cal_days_in_month(CAL_GREGORIAN, $date[1], $date[0]));
@ -187,14 +246,14 @@ class StatisticsController extends Controller
]);
}
private function last12MonthsDates()
private function lastXMonthsDates($x = 12)
{
$year = intval(date('Y'));
$month = intval(date('m'));
$datesNeeded = [];
while (count($datesNeeded) < 12)
while (count($datesNeeded) < $x)
{
$datesNeeded[] = [$year, $month];
@ -211,26 +270,28 @@ class StatisticsController extends Controller
private function rotateColoursForData(array $data = [])
{
$colours = ["#d54d36",
"#59c669",
"#aa5ccf",
"#85b83a",
"#5f6cd9",
"#bbb248",
"#ca49a1",
"#479341",
"#d94b70",
"#52b395",
"#7b589e",
"#da8f32",
"#6e8bd0",
"#8a722c",
"#46aed7",
"#aa5839",
"#d48cca",
"#64803f",
"#a5506d",
"#e19774"];
$colours = [
'#d54d36',
'#59c669',
'#aa5ccf',
'#85b83a',
'#5f6cd9',
'#bbb248',
'#ca49a1',
'#479341',
'#d94b70',
'#52b395',
'#7b589e',
'#da8f32',
'#6e8bd0',
'#8a722c',
'#46aed7',
'#aa5839',
'#d48cca',
'#64803f',
'#a5506d',
'#e19774'
];
$result = [];
$lastIndex = 0;

View File

@ -77,6 +77,11 @@ class GlobalConfiguration
{
View::share('error', $request->session()->get('error'));
}
if ($request->session()->has('success'))
{
View::share('success', $request->session()->get('success'));
}
}
private function addLabelsToView()

View File

@ -87,6 +87,11 @@ class AuthServiceProvider extends ServiceProvider
return $can;
});
Gate::define('statistics.public-access', function ($user)
{
return UserConfig::get('public_statistics') || !$user->isAnonymous();
});
}
private function userHasAdminPermission(User $user, $permissionDescription)

View File

@ -4,6 +4,7 @@ namespace App;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Support\Facades\Auth;
class User extends Authenticatable
{
@ -41,6 +42,14 @@ class User extends Authenticatable
return $user;
}
public static function currentOrAnonymous()
{
$user = Auth::user();
return (is_null($user)
? User::anonymous()
: $user);
}
public function groups()
{
return $this->belongsToMany(Group::class, 'user_groups');

View File

@ -35,6 +35,22 @@
white-space: nowrap;
width: auto;
}
.stats-table .icon-col {
font-size: 1.4em;
width: 60px;
vertical-align: middle;
}
.stats-table .stat-col {
font-size: 1.8em;
font-weight: bold;
}
.stats-table .text-col {
font-size: 1.2em;
vertical-align: middle;
}
html {
font-size: 14px !important;
}
@ -10519,3 +10535,737 @@ a.text-dark:focus, a.text-dark:hover {
overflow: visible;
clip: auto;
}
/**
* selectize.css (v0.12.4)
* Copyright (c) 20132015 Brian Reavis & contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
* @author Brian Reavis <brian@thirdroute.com>
*/
.selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder {
visibility: visible !important;
background: #f2f2f2 !important;
background: rgba(0, 0, 0, 0.06) !important;
border: 0 none !important;
-webkit-box-shadow: inset 0 0 12px 4px #ffffff;
box-shadow: inset 0 0 12px 4px #ffffff;
}
.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after {
content: '!';
visibility: hidden;
}
.selectize-control.plugin-drag_drop .ui-sortable-helper {
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
.selectize-dropdown-header {
position: relative;
padding: 5px 8px;
border-bottom: 1px solid #d0d0d0;
background: #f8f8f8;
-webkit-border-radius: 3px 3px 0 0;
-moz-border-radius: 3px 3px 0 0;
border-radius: 3px 3px 0 0;
}
.selectize-dropdown-header-close {
position: absolute;
right: 8px;
top: 50%;
color: #303030;
opacity: 0.4;
margin-top: -12px;
line-height: 20px;
font-size: 20px !important;
}
.selectize-dropdown-header-close:hover {
color: #000000;
}
.selectize-dropdown.plugin-optgroup_columns .optgroup {
border-right: 1px solid #f2f2f2;
border-top: 0 none;
float: left;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child {
border-right: 0 none;
}
.selectize-dropdown.plugin-optgroup_columns .optgroup:before {
display: none;
}
.selectize-dropdown.plugin-optgroup_columns .optgroup-header {
border-top: 0 none;
}
.selectize-control.plugin-remove_button [data-value] {
position: relative;
padding-right: 24px !important;
}
.selectize-control.plugin-remove_button [data-value] .remove {
z-index: 1;
/* fixes ie bug (see #392) */
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 17px;
text-align: center;
font-weight: bold;
font-size: 12px;
color: inherit;
text-decoration: none;
vertical-align: middle;
display: inline-block;
padding: 2px 0 0 0;
border-left: 1px solid #d0d0d0;
-webkit-border-radius: 0 2px 2px 0;
-moz-border-radius: 0 2px 2px 0;
border-radius: 0 2px 2px 0;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.selectize-control.plugin-remove_button [data-value] .remove:hover {
background: rgba(0, 0, 0, 0.05);
}
.selectize-control.plugin-remove_button [data-value].active .remove {
border-left-color: #cacaca;
}
.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover {
background: none;
}
.selectize-control.plugin-remove_button .disabled [data-value] .remove {
border-left-color: #ffffff;
}
.selectize-control.plugin-remove_button .remove-single {
position: absolute;
right: 28px;
top: 6px;
font-size: 23px;
}
.selectize-control {
position: relative;
}
.selectize-dropdown,
.selectize-input,
.selectize-input input {
color: #303030;
font-family: inherit;
font-size: 13px;
line-height: 18px;
-webkit-font-smoothing: inherit;
}
.selectize-input,
.selectize-control.single .selectize-input.input-active {
background: #ffffff;
cursor: text;
display: inline-block;
}
.selectize-input {
border: 1px solid #d0d0d0;
padding: 8px 8px;
display: inline-block;
width: 100%;
overflow: hidden;
position: relative;
z-index: 1;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
.selectize-control.multi .selectize-input.has-items {
padding: 6px 8px 3px;
}
.selectize-input.full {
background-color: #ffffff;
}
.selectize-input.disabled,
.selectize-input.disabled * {
cursor: default !important;
}
.selectize-input.focus {
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
}
.selectize-input.dropdown-active {
-webkit-border-radius: 3px 3px 0 0;
-moz-border-radius: 3px 3px 0 0;
border-radius: 3px 3px 0 0;
}
.selectize-input > * {
vertical-align: baseline;
display: -moz-inline-stack;
display: inline-block;
zoom: 1;
*display: inline;
}
.selectize-control.multi .selectize-input > div {
cursor: pointer;
margin: 0 3px 3px 0;
padding: 2px 6px;
background: #f2f2f2;
color: #303030;
border: 0 solid #d0d0d0;
}
.selectize-control.multi .selectize-input > div.active {
background: #e8e8e8;
color: #303030;
border: 0 solid #cacaca;
}
.selectize-control.multi .selectize-input.disabled > div,
.selectize-control.multi .selectize-input.disabled > div.active {
color: #7d7d7d;
background: #ffffff;
border: 0 solid #ffffff;
}
.selectize-input > input {
display: inline-block !important;
padding: 0 !important;
min-height: 0 !important;
max-height: none !important;
max-width: 100% !important;
margin: 0 2px 0 0 !important;
text-indent: 0 !important;
border: 0 none !important;
background: none !important;
line-height: inherit !important;
-webkit-user-select: auto !important;
-webkit-box-shadow: none !important;
box-shadow: none !important;
}
.selectize-input > input::-ms-clear {
display: none;
}
.selectize-input > input:focus {
outline: none !important;
}
.selectize-input::after {
content: ' ';
display: block;
clear: left;
}
.selectize-input.dropdown-active::before {
content: ' ';
display: block;
position: absolute;
background: #f0f0f0;
height: 1px;
bottom: 0;
left: 0;
right: 0;
}
.selectize-dropdown {
position: absolute;
z-index: 10;
border: 1px solid #d0d0d0;
background: #ffffff;
margin: -1px 0 0 0;
border-top: 0 none;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
-webkit-border-radius: 0 0 3px 3px;
-moz-border-radius: 0 0 3px 3px;
border-radius: 0 0 3px 3px;
}
.selectize-dropdown [data-selectable] {
cursor: pointer;
overflow: hidden;
}
.selectize-dropdown [data-selectable] .highlight {
background: rgba(125, 168, 208, 0.2);
-webkit-border-radius: 1px;
-moz-border-radius: 1px;
border-radius: 1px;
}
.selectize-dropdown [data-selectable],
.selectize-dropdown .optgroup-header {
padding: 5px 8px;
}
.selectize-dropdown .optgroup:first-child .optgroup-header {
border-top: 0 none;
}
.selectize-dropdown .optgroup-header {
color: #303030;
background: #ffffff;
cursor: default;
}
.selectize-dropdown .active {
background-color: #f5fafd;
color: #495c68;
}
.selectize-dropdown .active.create {
color: #495c68;
}
.selectize-dropdown .create {
color: rgba(48, 48, 48, 0.5);
}
.selectize-dropdown-content {
overflow-y: auto;
overflow-x: hidden;
max-height: 200px;
-webkit-overflow-scrolling: touch;
}
.selectize-control.single .selectize-input,
.selectize-control.single .selectize-input input {
cursor: pointer;
}
.selectize-control.single .selectize-input.input-active,
.selectize-control.single .selectize-input.input-active input {
cursor: text;
}
.selectize-control.single .selectize-input:after {
content: ' ';
display: block;
position: absolute;
top: 50%;
right: 15px;
margin-top: -3px;
width: 0;
height: 0;
border-style: solid;
border-width: 5px 5px 0 5px;
border-color: #808080 transparent transparent transparent;
}
.selectize-control.single .selectize-input.dropdown-active:after {
margin-top: -4px;
border-width: 0 5px 5px 5px;
border-color: transparent transparent #808080 transparent;
}
.selectize-control.rtl.single .selectize-input:after {
left: 15px;
right: auto;
}
.selectize-control.rtl .selectize-input > input {
margin: 0 4px 0 -2px !important;
}
.selectize-control .selectize-input.disabled {
opacity: 0.5;
background-color: #fafafa;
}
/**
* selectize.bootstrap3.css (v0.12.4) - Bootstrap 3 Theme
* Copyright (c) 20132015 Brian Reavis & contributors
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
* file except in compliance with the License. You may obtain a copy of the License at:
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
* ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*
* @author Brian Reavis <brian@thirdroute.com>
*/
.selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder {
visibility: visible !important;
background: #f2f2f2 !important;
background: rgba(0, 0, 0, 0.06) !important;
border: 0 none !important;
-webkit-box-shadow: inset 0 0 12px 4px #ffffff;
box-shadow: inset 0 0 12px 4px #ffffff;
}
.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after {
content: '!';
visibility: hidden;
}
.selectize-control.plugin-drag_drop .ui-sortable-helper {
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
.selectize-dropdown-header {
position: relative;
padding: 3px 12px;
border-bottom: 1px solid #d0d0d0;
background: #f8f8f8;
-webkit-border-radius: 4px 4px 0 0;
-moz-border-radius: 4px 4px 0 0;
border-radius: 4px 4px 0 0;
}
.selectize-dropdown-header-close {
position: absolute;
right: 12px;
top: 50%;
color: #333333;
opacity: 0.4;
margin-top: -12px;
line-height: 20px;
font-size: 20px !important;
}
.selectize-dropdown-header-close:hover {
color: #000000;
}
.selectize-dropdown.plugin-optgroup_columns .optgroup {
border-right: 1px solid #f2f2f2;
border-top: 0 none;
float: left;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child {
border-right: 0 none;
}
.selectize-dropdown.plugin-optgroup_columns .optgroup:before {
display: none;
}
.selectize-dropdown.plugin-optgroup_columns .optgroup-header {
border-top: 0 none;
}
.selectize-control.plugin-remove_button [data-value] {
position: relative;
padding-right: 24px !important;
}
.selectize-control.plugin-remove_button [data-value] .remove {
z-index: 1;
/* fixes ie bug (see #392) */
position: absolute;
top: 0;
right: 0;
bottom: 0;
width: 17px;
text-align: center;
font-weight: bold;
font-size: 12px;
color: inherit;
text-decoration: none;
vertical-align: middle;
display: inline-block;
padding: 1px 0 0 0;
border-left: 1px solid rgba(0, 0, 0, 0);
-webkit-border-radius: 0 2px 2px 0;
-moz-border-radius: 0 2px 2px 0;
border-radius: 0 2px 2px 0;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
.selectize-control.plugin-remove_button [data-value] .remove:hover {
background: rgba(0, 0, 0, 0.05);
}
.selectize-control.plugin-remove_button [data-value].active .remove {
border-left-color: rgba(0, 0, 0, 0);
}
.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover {
background: none;
}
.selectize-control.plugin-remove_button .disabled [data-value] .remove {
border-left-color: rgba(77, 77, 77, 0);
}
.selectize-control.plugin-remove_button .remove-single {
position: absolute;
right: 28px;
top: 6px;
font-size: 23px;
}
.selectize-control {
position: relative;
}
.selectize-dropdown,
.selectize-input,
.selectize-input input {
color: #333333;
font-family: inherit;
font-size: inherit;
line-height: 20px;
-webkit-font-smoothing: inherit;
}
.selectize-input,
.selectize-control.single .selectize-input.input-active {
background: #ffffff;
cursor: text;
display: inline-block;
}
.selectize-input {
border: 1px solid #cccccc;
padding: 6px 12px;
display: inline-block;
width: 100%;
overflow: hidden;
position: relative;
z-index: 1;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-box-shadow: none;
box-shadow: none;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.selectize-control.multi .selectize-input.has-items {
padding: 5px 12px 2px;
}
.selectize-input.full {
background-color: #ffffff;
}
.selectize-input.disabled,
.selectize-input.disabled * {
cursor: default !important;
}
.selectize-input.focus {
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
}
.selectize-input.dropdown-active {
-webkit-border-radius: 4px 4px 0 0;
-moz-border-radius: 4px 4px 0 0;
border-radius: 4px 4px 0 0;
}
.selectize-input > * {
vertical-align: baseline;
display: -moz-inline-stack;
display: inline-block;
zoom: 1;
*display: inline;
}
.selectize-control.multi .selectize-input > div {
cursor: pointer;
margin: 0 3px 3px 0;
padding: 1px 3px;
background: #efefef;
color: #333333;
border: 0 solid rgba(0, 0, 0, 0);
}
.selectize-control.multi .selectize-input > div.active {
background: #428bca;
color: #ffffff;
border: 0 solid rgba(0, 0, 0, 0);
}
.selectize-control.multi .selectize-input.disabled > div,
.selectize-control.multi .selectize-input.disabled > div.active {
color: #808080;
background: #ffffff;
border: 0 solid rgba(77, 77, 77, 0);
}
.selectize-input > input {
display: inline-block !important;
padding: 0 !important;
min-height: 0 !important;
max-height: none !important;
max-width: 100% !important;
margin: 0 !important;
text-indent: 0 !important;
border: 0 none !important;
background: none !important;
line-height: inherit !important;
-webkit-user-select: auto !important;
-webkit-box-shadow: none !important;
box-shadow: none !important;
}
.selectize-input > input::-ms-clear {
display: none;
}
.selectize-input > input:focus {
outline: none !important;
}
.selectize-input::after {
content: ' ';
display: block;
clear: left;
}
.selectize-input.dropdown-active::before {
content: ' ';
display: block;
position: absolute;
background: #ffffff;
height: 1px;
bottom: 0;
left: 0;
right: 0;
}
.selectize-dropdown {
position: absolute;
z-index: 10;
border: 1px solid #d0d0d0;
background: #ffffff;
margin: -1px 0 0 0;
border-top: 0 none;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
-webkit-border-radius: 0 0 4px 4px;
-moz-border-radius: 0 0 4px 4px;
border-radius: 0 0 4px 4px;
}
.selectize-dropdown [data-selectable] {
cursor: pointer;
overflow: hidden;
}
.selectize-dropdown [data-selectable] .highlight {
background: rgba(255, 237, 40, 0.4);
-webkit-border-radius: 1px;
-moz-border-radius: 1px;
border-radius: 1px;
}
.selectize-dropdown [data-selectable],
.selectize-dropdown .optgroup-header {
padding: 3px 12px;
}
.selectize-dropdown .optgroup:first-child .optgroup-header {
border-top: 0 none;
}
.selectize-dropdown .optgroup-header {
color: #777777;
background: #ffffff;
cursor: default;
}
.selectize-dropdown .active {
background-color: #f5f5f5;
color: #262626;
}
.selectize-dropdown .active.create {
color: #262626;
}
.selectize-dropdown .create {
color: rgba(51, 51, 51, 0.5);
}
.selectize-dropdown-content {
overflow-y: auto;
overflow-x: hidden;
max-height: 200px;
-webkit-overflow-scrolling: touch;
}
.selectize-control.single .selectize-input,
.selectize-control.single .selectize-input input {
cursor: pointer;
}
.selectize-control.single .selectize-input.input-active,
.selectize-control.single .selectize-input.input-active input {
cursor: text;
}
.selectize-control.single .selectize-input:after {
content: ' ';
display: block;
position: absolute;
top: 50%;
right: 17px;
margin-top: -3px;
width: 0;
height: 0;
border-style: solid;
border-width: 5px 5px 0 5px;
border-color: #333333 transparent transparent transparent;
}
.selectize-control.single .selectize-input.dropdown-active:after {
margin-top: -4px;
border-width: 0 5px 5px 5px;
border-color: transparent transparent #333333 transparent;
}
.selectize-control.rtl.single .selectize-input:after {
left: 17px;
right: auto;
}
.selectize-control.rtl .selectize-input > input {
margin: 0 4px 0 -2px !important;
}
.selectize-control .selectize-input.disabled {
opacity: 0.5;
background-color: #ffffff;
}
.selectize-dropdown,
.selectize-dropdown.form-control {
height: auto;
padding: 0;
margin: 2px 0 0 0;
z-index: 1000;
background: #ffffff;
border: 1px solid #cccccc;
border: 1px solid rgba(0, 0, 0, 0.15);
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
}
.selectize-dropdown .optgroup-header {
font-size: 12px;
line-height: 1.42857143;
}
.selectize-dropdown .optgroup:first-child:before {
display: none;
}
.selectize-dropdown .optgroup:before {
content: ' ';
display: block;
height: 1px;
margin: 9px 0;
overflow: hidden;
background-color: #e5e5e5;
margin-left: -12px;
margin-right: -12px;
}
.selectize-dropdown-content {
padding: 5px 0;
}
.selectize-dropdown-header {
padding: 6px 12px;
}
.selectize-input {
min-height: 34px;
}
.selectize-input.dropdown-active {
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.selectize-input.dropdown-active::before {
display: none;
}
.selectize-input.focus {
border-color: #66afe9;
outline: 0;
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);
}
.has-error .selectize-input {
border-color: #a94442;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
}
.has-error .selectize-input:focus {
border-color: #843534;
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;
}
.selectize-control.multi .selectize-input.has-items {
padding-left: 9px;
padding-right: 9px;
}
.selectize-control.multi .selectize-input > div {
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
.form-control.selectize-control {
padding: 0;
height: auto;
border: none;
background: none;
-webkit-box-shadow: none;
box-shadow: none;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -10,3 +10,19 @@
white-space: nowrap;
width: auto;
}
.stats-table .icon-col {
font-size: 1.4em;
width: 60px;
vertical-align: middle;
}
.stats-table .stat-col {
font-size: 1.8em;
font-weight: bold;
}
.stats-table .text-col {
font-size: 1.2em;
vertical-align: middle;
}

View File

@ -193,6 +193,9 @@ return [
'settings_test_email_summary_successful' => 'Successful!',
'settings_test_email_successful' => 'A test e-mail was submitted to your mail server successfully.',
'settings_title' => 'Settings',
'statistics_enable_public_checkbox' => 'Anyone can view these statistics',
'statistics_enable_public_intro' => 'You can allow visitors to your gallery to view your statistics based on the albums and photos they have access to.',
'statistics_prefs_saved_message' => 'The statistics preferences were saved successfully.',
'stats_widget' => [
'albums' => 'album|albums',
'groups' => 'group|groups',

View File

@ -34,7 +34,17 @@ return [
'huge' => 'Huge (>5MB)'
],
'intro' => 'This page displays some interesting graphs and statistics about :gallery_name.',
'taken_12_months' => 'Photos taken in the last 12 months',
'numbers' => [
'albums' => 'album|albums',
'chart_title' => 'At-a-glance',
'labels' => 'label|labels',
'photos' => 'photo|photos'
],
'photos_combined' => [
'chart_title' => 'Activity in the last 18 months',
'taken' => 'Taken',
'uploaded' => 'Uploaded'
],
'title' => 'Statistics',
'uploaded_12_months' => 'Photos uploaded in the last 12 months',
]

View File

@ -11,16 +11,33 @@
<div class="row">
<div class="col">
<h1>@lang('gallery.statistics.title')</h1>
<div class="alert alert-info" style="margin-bottom: 30px;">
<div class="alert alert-info mb-3">
<i class="fa fa-fw fa-info"></i> @lang('gallery.statistics.intro', ['gallery_name' => UserConfig::get('app_name')])
</div>
@can('admin:configure')
<div class="card mb-3">
<div class="card-body">
<p>@lang('admin.statistics_enable_public_intro')</p>
<form method="post" action="{{ route('admin.statistics.save') }}">
{{ csrf_field() }}
<label class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" name="enable_public_statistics" @if (UserConfig::get('public_statistics')) checked="checked"@endif/>
<span class="custom-control-indicator"></span>
<span class="custom-control-description">@lang('admin.statistics_enable_public_checkbox')</span>
</label><br/>
<button class="btn btn-success"><i class="fa fa-floppy-o"></i> @lang('forms.save_action')</button>
</form>
</div>
</div>
@endcan
</div>
</div>
<div class="row">
<div class="col-md-6 mb-4">
<div class="col-md-8 mb-4">
<div class="card">
<div class="card-header">@lang('gallery.statistics.taken_12_months')</div>
<div class="card-header">@lang('gallery.statistics.photos_combined.chart_title')</div>
<div class="card-body text-center" id="taken-12m-graph">
<canvas id="taken-12m-chart" style="display: none;"></canvas>
<img class="loading" src="{{ asset('ripple.svg') }}"/>
@ -28,12 +45,29 @@
</div>
</div>
<div class="col-md-6 mb-4">
<div class="col-md-4 mb-4">
<div class="card">
<div class="card-header">@lang('gallery.statistics.uploaded_12_months')</div>
<div class="card-body text-center" id="uploaded-12m-graph">
<canvas id="uploaded-12m-chart" style="display: none;"></canvas>
<img class="loading" src="{{ asset('ripple.svg') }}"/>
<div class="card-header">@lang('gallery.statistics.numbers.chart_title')</div>
<div class="card-body text-center" style="padding: 0;">
<table class="table table-striped stats-table mb-0">
<tbody>
<tr>
<td class="icon-col"><i class="fa fa-photo"></i></td>
<td class="stat-col">{{ number_format($photo_count, 0) }}</td>
<td class="text-col">{{ trans_choice('gallery.statistics.numbers.photos', $photo_count) }}</td>
</tr>
<tr>
<td class="icon-col"><i class="fa fa-book"></i></td>
<td class="stat-col">{{ number_format($album_count, 0) }}</td>
<td class="text-col">{{ trans_choice('gallery.statistics.numbers.albums', $album_count) }}</td>
</tr>
<tr>
<td class="icon-col"><i class="fa fa-tags"></i></td>
<td class="stat-col">{{ number_format($label_count, 0) }}</td>
<td class="text-col">{{ trans_choice('gallery.statistics.numbers.labels', $label_count) }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
@ -91,7 +125,7 @@
<script type="text/javascript">
$(document).ready(function() {
/* Photos taken in the last 12 months */
$.get('{{ route('statistics.taken12Months') }}', function(result)
$.get('{{ route('statistics.photosCombined') }}', function(result)
{
$('.loading', '#taken-12m-graph').hide();
$('canvas', '#taken-12m-graph').show();
@ -101,42 +135,23 @@
data: {
datasets: [
{
borderColor: '#0F2240',
data: result.data,
fill: false
}
],
labels: result.labels
},
options: {
legend: {
display: false
}
}
});
});
/* Photos uploaded in the last 12 months */
$.get('{{ route('statistics.uploaded12Months') }}', function(result)
{
$('.loading', '#uploaded-12m-graph').hide();
$('canvas', '#uploaded-12m-graph').show();
var myLineChart = new Chart($('#uploaded-12m-chart'),
{
type: 'line',
data: {
datasets: [
borderColor: '#5f6cd9',
data: result.data[0].values,
fill: false,
label: result.data[0].label
},
{
borderColor: '#0F2240',
data: result.data,
fill: false
borderColor: '#59c669',
data: result.data[1].values,
fill: false,
label: result.data[1].label
}
],
labels: result.labels
},
options: {
legend: {
display: false
position: 'bottom'
}
}
});

View File

@ -41,7 +41,7 @@
</li>
@endif
@if (count($albums) > 0)
@if (count($albums) > 0 && \App\User::currentOrAnonymous()->can('statistics.public-access'))
<li class="nav-item ml-2">
<a class="nav-link" href="{{ route('statistics.index') }}"><i class="fa fa-bar-chart"></i> @lang('navigation.navbar.statistics')</a>
</li>

View File

@ -20,6 +20,7 @@ Route::group(['prefix' => 'admin'], function () {
Route::post('settings/save', 'Admin\DefaultController@saveSettings')->name('admin.saveSettings');
Route::post('settings/test-email', 'Admin\DefaultController@testMailSettings')->name('admin.testMailSettings');
Route::get('settings', 'Admin\DefaultController@settings')->name('admin.settings');
Route::post('statistics/save', 'Admin\StatisticsController@save')->name('admin.statistics.save');
// Album management
Route::get('albums/{id}/analyse/{queue_token}', 'Admin\AlbumController@analyse')->name('albums.analyse');
@ -80,6 +81,7 @@ Route::get('/statistics/album-size-photo-count', 'Gallery\StatisticsController@a
Route::get('/statistics/album-size-photo-size', 'Gallery\StatisticsController@albumSizeByPhotoSizeChart')->name('statistics.albumSizePhotoSize');
Route::get('/statistics/cameras', 'Gallery\StatisticsController@camerasChart')->name('statistics.cameras');
Route::get('/statistics/file-sizes', 'Gallery\StatisticsController@fileSizeChart')->name('statistics.fileSizes');
Route::get('/statistics/photos-combined', 'Gallery\StatisticsController@photosCombined')->name('statistics.photosCombined');
Route::get('/statistics/taken-12m', 'Gallery\StatisticsController@photosTaken12Months')->name('statistics.taken12Months');
Route::get('/statistics/uploaded-12m', 'Gallery\StatisticsController@photosUploaded12Months')->name('statistics.uploaded12Months');
Route::get('a/{albumUrlAlias}', 'Gallery\AlbumController@index')