updated grav to 1.7.14

This commit is contained in:
2021-05-12 10:33:19 +02:00
parent 3d5bc71a7c
commit bd4137e00d
553 changed files with 53214 additions and 18091 deletions
-5
View File
@@ -1,5 +0,0 @@
<?php
/** Moved from non-namespaced classes to Grav Framework */
class_alias(Grav\Framework\Parsedown\Parsedown::class, '\Parsedown');
class_alias(Grav\Framework\Parsedown\ParsedownExtra::class, '\ParsedownExtra');
-70
View File
@@ -1,70 +0,0 @@
div.phpdebugbar {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.phpdebugbar pre {
padding: 1rem;
}
.phpdebugbar div.phpdebugbar-header > div > * {
padding: 5px 15px;
}
.phpdebugbar div.phpdebugbar-header > div.phpdebugbar-header-right > * {
padding: 5px 8px;
}
.phpdebugbar div.phpdebugbar-header, .phpdebugbar a.phpdebugbar-restore-btn {
background-image: url(grav.png);
}
.phpdebugbar a.phpdebugbar-restore-btn {
width: 13px;
}
.phpdebugbar a.phpdebugbar-tab.phpdebugbar-active {
background: #3DB9EC;
color: #fff;
margin-top: -1px;
padding-top: 6px;
}
.phpdebugbar .phpdebugbar-widgets-toolbar {
border-top: 1px solid #ddd;
padding-left: 5px;
padding-right: 2px;
padding-top: 2px;
background-color: #fafafa !important;
width: auto !important;
left: 0;
right: 0;
}
.phpdebugbar .phpdebugbar-widgets-toolbar input {
background: transparent !important;
}
.phpdebugbar .phpdebugbar-widgets-toolbar .phpdebugbar-widgets-filter {
}
.phpdebugbar input[type=text] {
padding: 0;
display: inline;
}
.phpdebugbar dl.phpdebugbar-widgets-varlist, ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label {
font-family: "DejaVu Sans Mono", Menlo, Monaco, Consolas, Courier, monospace;
font-size: 12px;
}
ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label {
text-shadow: -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff;
top: 0;
}
.phpdebugbar pre, .phpdebugbar code {
margin: 0;
font-size: 14px;
}
+2
View File
@@ -0,0 +1,2 @@
/** Clockwork Debugger CSS **/
.clockwork-badge{position:fixed;z-index:10;bottom:0;left:0;padding:2px 4px;background-color:#eee;border:1px solid #ccc;border-bottom:0;border-left:0;display:flex;align-items:center}.clockwork-badge:hover{width:auto}.clockwork-badge:hover:after{content:'Grav Clockwork debugger enabled. Install Clockwork Browser extension (Chrome or Firefox), open your Developer tools and then select the Clockwork tab.'}.clockwork-badge:after{margin-left:10px;font-family:Monaco,Consolas,"Lucida Console",monospace;font-size:12px;line-height:1.5;color:#666}.clockwork-badge i{display:block;float:left;height:22px;width:22px;min-width:22px;background-size:contain;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAA/1BMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeHh4AAAD///8EBAT7+/sLCwv29vYVFRUvLy/t7e3m5ubCwsKxsbE/Pz+mpqZMTEwcHBzy8vLp6emfn5+AgIA2Njbi4uLf39+rq6tzc3NWVlYhISHa2trW1tbS0tLMzMy7u7uZmZmUlJSMjIxvb29kZGRHR0c7Ozt5eXkqKiq1tbWQkJBqampbW1tSUlLHx8eHh4ckJCRDQ0M3wD42AAAAI3RSTlMA/PibTbQ0x76TVAlw4LhZLOuEYCAN9Hjx0a2ppGZEGYw97djhXHwAAATZSURBVFjDlVcHW+MwDO1eFCjj2McNOzvdpXTTXVbL/P+/5SQ7QSSX5Di1X1onfi/Sk+Q4sTDbKqWK+YuznZ2zi3wxVdqK/Zf92M1nT9gnO8rmd398GX6Z3xaoOFoiAQcx3E5efgmeSuN8F6Xg1x3G06l/wjNpMR1B0uif4EhnIuFb+0diIoFXk3IVfokisR+h52GO4JKgyjmfaMhAFNlSaPR7DpwI+lzn/E4QKIqmKIJirxCMP4izBPPZPXhgXwMBYgULw0nfg/BF5scDbslb7QeJ08yqqTEmGYoB95d4H8ETL8+n9wBqrLu6ao3bBsMwAnxISf/9BHcqxNB8Y7cWl3Zz7TAUfPrvAT6AoNEFFXvsjutL01yOuMrtBxnFXsmT/1wQHmdWAFNnI3uI48Yj0FUcHbKf62GfUfr8eeQt7Uk3mQZpZNoVRPEui5vtEz5zFEpgWnyqVBZMc6oaGNriH2hGVZ0OxEvInPeMaZWJBA7vmPbCr5jjws5HBnAUxvDMH40aCIf4G5BjRQSs8E8HFFYf8bGxgDvD55bzGhwWkoBcuIyHR/AMdaCagxXDhtL6tSqoWpd4BMnlIR+Or+rYTK/a3EAGcc6e4AWHISnWv20iCCojsHoVlQdjrMexFF2C7UMg2A2WEGWbQhXN6l3eXC6XGp4b9qxbuEB2EBGBwtocrK90cVG5mbRXm6vmx/0phq1sIAGKDgLOBiN1MrO5a9aDl+D0W6x0Ar9BCTRuIIANa90Y7LrLVRXzwVtDInCqMRWcf2bUOEAsa4wJqFowQALL9EiAtVRk8QC4OW+1pOM9jIaVASwYagyNXDj+W0NcfuZNzjtXOiL0Zzg30Llj+ptfxQs4+vBPNiL5PawFCBkgXpUaVtqGl+A8dgZHL34BcBUQrwPptToW+o37Ku+UH9eYByJIx3YkAeFnMFuGO7S5gEp7YhXxa5OOAM39RXDPXb0qmpROsswZe+twXdU55oUIZAiEv3bD1UFwIYKkmGqytPCDCwKFQCKK0yL7qtSAPX54UAbtsLuBHkb9zyLmPQSNjsSgmQwKUOIfEY8F8t4B34DvndJY9BA8tNBJq1Nev9axmaStFcQLhgYoCTo0salkIaW8OUDdWjMTR2sHPhrAFZqx6cqcKE4pl2BJJ4K6hfwvqNgAnXfKX/HU6X3Zrhnu0k7tLNZtTBRv1hkwTDBY1NzFU6doDYjJbWdQkQhWwuU7/LvhTh3SDoco4ECL4i5dwURbc8NdDZz2IwKicE8d0KIqWetLE3+lL4hvUuGSeRfVWNLfj/gpOw4smBJBkKQHCzlHGwvAj4woB1gq5NGGLSXtORBPnUQPV5/MPVkDMxbpwG7w4x0xL6Ltxka0A/4NBvV09UVk4DoSn/jl2+JQS9q9KYawisAD4CfhsZ4TH3htylsdEHARIQBusqCKyUpymycgbbkkXEXjT3z7/oKQFTFVuZD2FMJHZIDsO5x2d4aAr2jR+GLwZhtAb028/0yJ9J8dE87jQyKObcjtTXT8dH+fDuKF4/eiPwzH44wTf/yUi6wrpRIOZ9lM1EtXAifFI+CJn9+iX/t2xMQwOMth/UZbASi8btAwR9FHWSpJr75g9Oqbin3VDg+SpwlP6k6TB4ex/7JvmcJx8jydy6XPk8eFTKhyfwCgX71MSvaBHgAAAABJRU5ErkJggg==)}
+3
View File
@@ -0,0 +1,3 @@
/** Clockwork Debugger JS **/
document.addEventListener("DOMContentLoaded",function () {
var e=document.createElement("div");e.appendChild(document.createElement("i")),e.className="clockwork-badge",document.body.appendChild(e)});
+70
View File
@@ -0,0 +1,70 @@
div.phpdebugbar {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.phpdebugbar pre {
padding: 1rem;
}
.phpdebugbar div.phpdebugbar-header > div > * {
padding: 5px 15px;
}
.phpdebugbar div.phpdebugbar-header > div.phpdebugbar-header-right > * {
padding: 5px 8px;
}
.phpdebugbar div.phpdebugbar-header, .phpdebugbar a.phpdebugbar-restore-btn {
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAA/1BMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeHh4AAAD///8EBAT7+/sLCwv29vYVFRUvLy/t7e3m5ubCwsKxsbE/Pz+mpqZMTEwcHBzy8vLp6emfn5+AgIA2Njbi4uLf39+rq6tzc3NWVlYhISHa2trW1tbS0tLMzMy7u7uZmZmUlJSMjIxvb29kZGRHR0c7Ozt5eXkqKiq1tbWQkJBqampbW1tSUlLHx8eHh4ckJCRDQ0M3wD42AAAAI3RSTlMA/PibTbQ0x76TVAlw4LhZLOuEYCAN9Hjx0a2ppGZEGYw97djhXHwAAATZSURBVFjDlVcHW+MwDO1eFCjj2McNOzvdpXTTXVbL/P+/5SQ7QSSX5Di1X1onfi/Sk+Q4sTDbKqWK+YuznZ2zi3wxVdqK/Zf92M1nT9gnO8rmd398GX6Z3xaoOFoiAQcx3E5efgmeSuN8F6Xg1x3G06l/wjNpMR1B0uif4EhnIuFb+0diIoFXk3IVfokisR+h52GO4JKgyjmfaMhAFNlSaPR7DpwI+lzn/E4QKIqmKIJirxCMP4izBPPZPXhgXwMBYgULw0nfg/BF5scDbslb7QeJ08yqqTEmGYoB95d4H8ETL8+n9wBqrLu6ao3bBsMwAnxISf/9BHcqxNB8Y7cWl3Zz7TAUfPrvAT6AoNEFFXvsjutL01yOuMrtBxnFXsmT/1wQHmdWAFNnI3uI48Yj0FUcHbKf62GfUfr8eeQt7Uk3mQZpZNoVRPEui5vtEz5zFEpgWnyqVBZMc6oaGNriH2hGVZ0OxEvInPeMaZWJBA7vmPbCr5jjws5HBnAUxvDMH40aCIf4G5BjRQSs8E8HFFYf8bGxgDvD55bzGhwWkoBcuIyHR/AMdaCagxXDhtL6tSqoWpd4BMnlIR+Or+rYTK/a3EAGcc6e4AWHISnWv20iCCojsHoVlQdjrMexFF2C7UMg2A2WEGWbQhXN6l3eXC6XGp4b9qxbuEB2EBGBwtocrK90cVG5mbRXm6vmx/0phq1sIAGKDgLOBiN1MrO5a9aDl+D0W6x0Ar9BCTRuIIANa90Y7LrLVRXzwVtDInCqMRWcf2bUOEAsa4wJqFowQALL9EiAtVRk8QC4OW+1pOM9jIaVASwYagyNXDj+W0NcfuZNzjtXOiL0Zzg30Llj+ptfxQs4+vBPNiL5PawFCBkgXpUaVtqGl+A8dgZHL34BcBUQrwPptToW+o37Ku+UH9eYByJIx3YkAeFnMFuGO7S5gEp7YhXxa5OOAM39RXDPXb0qmpROsswZe+twXdU55oUIZAiEv3bD1UFwIYKkmGqytPCDCwKFQCKK0yL7qtSAPX54UAbtsLuBHkb9zyLmPQSNjsSgmQwKUOIfEY8F8t4B34DvndJY9BA8tNBJq1Nev9axmaStFcQLhgYoCTo0salkIaW8OUDdWjMTR2sHPhrAFZqx6cqcKE4pl2BJJ4K6hfwvqNgAnXfKX/HU6X3Zrhnu0k7tLNZtTBRv1hkwTDBY1NzFU6doDYjJbWdQkQhWwuU7/LvhTh3SDoco4ECL4i5dwURbc8NdDZz2IwKicE8d0KIqWetLE3+lL4hvUuGSeRfVWNLfj/gpOw4smBJBkKQHCzlHGwvAj4woB1gq5NGGLSXtORBPnUQPV5/MPVkDMxbpwG7w4x0xL6Ltxka0A/4NBvV09UVk4DoSn/jl2+JQS9q9KYawisAD4CfhsZ4TH3htylsdEHARIQBusqCKyUpymycgbbkkXEXjT3z7/oKQFTFVuZD2FMJHZIDsO5x2d4aAr2jR+GLwZhtAb028/0yJ9J8dE87jQyKObcjtTXT8dH+fDuKF4/eiPwzH44wTf/yUi6wrpRIOZ9lM1EtXAifFI+CJn9+iX/t2xMQwOMth/UZbASi8btAwR9FHWSpJr75g9Oqbin3VDg+SpwlP6k6TB4ex/7JvmcJx8jydy6XPk8eFTKhyfwCgX71MSvaBHgAAAABJRU5ErkJggg==);
}
.phpdebugbar a.phpdebugbar-restore-btn {
width: 13px;
}
.phpdebugbar a.phpdebugbar-tab.phpdebugbar-active {
background: #3DB9EC;
color: #fff;
margin-top: -1px;
padding-top: 6px;
}
.phpdebugbar .phpdebugbar-widgets-toolbar {
border-top: 1px solid #ddd;
padding-left: 5px;
padding-right: 2px;
padding-top: 2px;
background-color: #fafafa !important;
width: auto !important;
left: 0;
right: 0;
}
.phpdebugbar .phpdebugbar-widgets-toolbar input {
background: transparent !important;
}
.phpdebugbar .phpdebugbar-widgets-toolbar .phpdebugbar-widgets-filter {
}
.phpdebugbar input[type=text] {
padding: 0;
display: inline;
}
.phpdebugbar dl.phpdebugbar-widgets-varlist, ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label {
font-family: "DejaVu Sans Mono", Menlo, Monaco, Consolas, Courier, monospace;
font-size: 12px;
}
ul.phpdebugbar-widgets-timeline li span.phpdebugbar-widgets-label {
text-shadow: -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff;
top: 0;
}
.phpdebugbar pre, .phpdebugbar code {
margin: 0;
font-size: 14px;
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

File diff suppressed because one or more lines are too long
+2 -1
View File
@@ -39,12 +39,13 @@ form:
.command:
type: text
label: PLUGIN_ADMIN.COMMAND
placeholder: 'cd ~;ls -lah;'
placeholder: 'ls'
validate:
required: true
.args:
type: text
label: PLUGIN_ADMIN.EXTRA_ARGUMENTS
placeholder: '-lah'
.at:
type: cron
label: PLUGIN_ADMIN.SCHEDULER_RUNAT
+12
View File
@@ -105,3 +105,15 @@ form:
validate:
type: commalist
sanitize_svg:
type: toggle
label: PLUGIN_ADMIN.SANITIZE_SVG
help: PLUGIN_ADMIN.SANITIZE_SVG_HELP
highlight: 1
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
default: true
validate:
type: bool
File diff suppressed because it is too large Load Diff
+8
View File
@@ -0,0 +1,8 @@
title: Flex User Accounts
description: Manage your User Accounts in Flex.
type: flex-objects
# Deprecated in Grav 1.7.0-rc.4: file was renamed to user-accounts.yaml
extends@:
type: user-accounts
context: blueprints://flex
@@ -0,0 +1,17 @@
form:
compatibility:
type: tab
title: Compatibility
fields:
object.compat.events:
type: toggle
toggleable: true
label: Admin event compatibility
help: Enables onAdminSave and onAdminAfterSave events for plugins
highlight: 1
default: 1
options:
1: PLUGIN_ADMIN.ENABLED
0: PLUGIN_ADMIN.DISABLED
validate:
type: bool
+212
View File
@@ -0,0 +1,212 @@
title: Pages
description: Manage your Grav Pages in Flex.
type: flex-objects
# Extends a page (blueprint gets overridden inside the object)
extends@:
type: default
context: blueprints://pages
#
# HIGHLY SPECIALIZED FLEX TYPE, AVOID USING PAGES AS BASE FOR YOUR OWN TYPE.
#
# Flex configuration
config:
# Administration Configuration (needs Flex Objects plugin)
admin:
# Admin router
router:
path: '/pages'
# Permissions
permissions:
# Primary permissions
admin.pages:
type: crudl
label: Pages
admin.configuration.pages:
type: default
label: Pages Configuration
# Admin menu
menu:
list:
route: '/pages'
title: PLUGIN_ADMIN.PAGES
icon: fa-file-text
authorize: ['admin.pages.list', 'admin.super']
priority: 5
# Admin template type (folder)
template: pages
# Allowed admin actions
actions:
list: true
create: true
read: true
update: true
delete: true
# List view
list:
# Fields shown in the list view
fields:
published:
width: 8
alias: header.published
visible:
width: 8
field:
label: Visible
type: toggle
menu:
link: edit
alias: header.menu
full_route:
field:
label: Route
type: text
link: edit
sort:
field: key
name:
width: 8
field:
label: Type
type: text
translations:
width: 8
field:
label: Translations
type: text
# updated_date:
# alias: header.update_date
# Extra options
options:
# Default number of records for pagination
per_page: 20
# Default ordering
order:
by: key
dir: asc
# TODO: not used yet
buttons:
back:
icon: reply
title: PLUGIN_ADMIN.BACK
add:
icon: plus
label: PLUGIN_ADMIN.ADD
edit:
title:
template: "{% if object.root %}Root <small>( &lt;root&gt; ){% else %}{{ (form.value('header.title') ?? form.value('folder'))|e }} <small>( {{ (object.getRoute().toString(false) ?: '/')|e }} )</small>{% endif %}"
# TODO: not used yet
buttons:
back:
icon: reply
title: PLUGIN_ADMIN.BACK
preview:
icon: eye
title: PLUGIN_ADMIN.PREVIEW
add:
icon: plus
label: PLUGIN_ADMIN.ADD
copy:
icon: copy
label: PLUGIN_ADMIN.COPY
move:
icon: arrows
label: PLUGIN_ADMIN.MOVE
delete:
icon: close
label: PLUGIN_ADMIN.DELETE
save:
icon: check
label: PLUGIN_ADMIN.SAVE
# Preview View
preview:
enabled: true
# Configure view
configure:
authorize: 'admin.configuration.pages'
# Site Configuration
site:
# Hide from flex types
hidden: true
templates:
collection:
# Lookup for the template layout files for collections of objects
paths:
- 'flex/{TYPE}/collection/{LAYOUT}{EXT}'
object:
# Lookup for the template layout files for objects
paths:
- 'flex/{TYPE}/object/{LAYOUT}{EXT}'
defaults:
# Default template {TYPE}; overridden by filename of this blueprint if template folder exists
type: pages
# Default template {LAYOUT}; can be overridden in render calls (usually Twig in templates)
layout: default
# Default filters for frontend.
filter:
- withPublished
# Data Configuration
data:
object: 'Grav\Common\Flex\Types\Pages\PageObject'
collection: 'Grav\Common\Flex\Types\Pages\PageCollection'
index: 'Grav\Common\Flex\Types\Pages\PageIndex'
storage:
class: 'Grav\Common\Flex\Types\Pages\Storage\PageStorage'
options:
formatter:
class: 'Grav\Framework\File\Formatter\MarkdownFormatter'
folder: 'page://'
# Keep index file in filesystem to speed up lookups
indexed: true
# Set default ordering of the pages
ordering:
storage_key: ASC
search:
# Search options
options:
contains: 1
# Fields to be searched
fields:
- key
- menu
- title
- name
blueprints:
configure:
fields:
import@:
type: configure/compat
context: blueprints://flex
# Regular form definition
form:
fields:
lang:
type: hidden
value: ''
tabs:
fields:
security:
type: tab
title: PLUGIN_ADMIN.SECURITY
import@:
type: partials/security
context: blueprints://pages
@@ -0,0 +1,70 @@
form:
validation: loose
fields:
tabs:
type: tabs
fields:
cache:
type: tab
title: Caching
fields:
object.cache.index.enabled:
type: toggle
toggleable: true
label: PLUGIN_ADMIN.FLEX_INDEX_CACHE_ENABLED
highlight: 1
config-default@: system.flex.cache.index.enabled
options:
1: PLUGIN_ADMIN.ENABLED
0: PLUGIN_ADMIN.DISABLED
validate:
type: bool
object.cache.index.lifetime:
type: text
toggleable: true
label: PLUGIN_ADMIN.FLEX_INDEX_CACHE_LIFETIME
config-default@: system.flex.cache.index.lifetime
validate:
type: int
object.cache.object.enabled:
type: toggle
toggleable: true
label: PLUGIN_ADMIN.FLEX_OBJECT_CACHE_ENABLED
highlight: 1
config-default@: system.flex.cache.object.enabled
options:
1: PLUGIN_ADMIN.ENABLED
0: PLUGIN_ADMIN.DISABLED
validate:
type: bool
object.cache.object.lifetime:
type: text
toggleable: true
label: PLUGIN_ADMIN.FLEX_OBJECT_CACHE_LIFETIME
config-default@: system.flex.cache.object.lifetime
validate:
type: int
object.cache.render.enabled:
type: toggle
toggleable: true
label: PLUGIN_ADMIN.FLEX_RENDER_CACHE_ENABLED
highlight: 1
config-default@: system.flex.cache.render.enabled
options:
1: PLUGIN_ADMIN.ENABLED
0: PLUGIN_ADMIN.DISABLED
validate:
type: bool
object.cache.render.lifetime:
type: text
toggleable: true
label: PLUGIN_ADMIN.FLEX_RENDER_CACHE_LIFETIME
config-default@: system.flex.cache.render.lifetime
validate:
type: int
+142
View File
@@ -0,0 +1,142 @@
title: User Accounts
description: Manage your User Accounts in Flex.
type: flex-objects
# Extends user account
extends@:
type: account
context: blueprints://user
#
# HIGHLY SPECIALIZED FLEX TYPE, AVOID USING USER ACCOUNTS AS BASE FOR YOUR OWN TYPE.
#
# Flex configuration
config:
# Administration Configuration (needs Flex Objects plugin)
admin:
# Admin router
router:
path: '/accounts/users'
actions:
configure:
path: '/accounts/configure'
redirects:
'/user': '/accounts/users'
'/accounts': '/accounts/users'
# Permissions
permissions:
# Primary permissions
admin.users:
type: crudl
label: User Accounts
admin.configuration.users:
type: default
label: Accounts Configuration
# Admin menu
menu:
base:
location: '/accounts'
route: '/accounts/users'
index: 0
title: PLUGIN_ADMIN.ACCOUNTS
icon: fa-users
authorize: ['admin.users.list', 'admin.super']
priority: 6
# Admin template type (folder)
template: user-accounts
# List view
list:
# Fields shown in the list view
fields:
username:
link: edit
search: true
field:
label: PLUGIN_ADMIN.USERNAME
email:
search: true
fullname:
search: true
# Extra options
options:
per_page: 20
order:
by: username
dir: asc
# Edit view
edit:
title:
template: "{{ form.value('fullname') ?? form.value('username') }} &lt;{{ form.value('email') }}&gt;"
# Configure view
configure:
hidden: true
authorize: 'admin.configuration.users'
form: 'accounts'
title:
template: "{{ 'PLUGIN_ADMIN.ACCOUNTS'|tu }} {{ 'PLUGIN_ADMIN.CONFIGURATION'|tu }}"
# Site Configuration
site:
# Hide from flex types
hidden: true
templates:
collection:
# Lookup for the template layout files for collections of objects
paths:
- 'flex/{TYPE}/collection/{LAYOUT}{EXT}'
object:
# Lookup for the template layout files for objects
paths:
- 'flex/{TYPE}/object/{LAYOUT}{EXT}'
defaults:
# Default template {TYPE}; overridden by filename of this blueprint if template folder exists
type: user-accounts
# Default template {LAYOUT}; can be overridden in render calls (usually Twig in templates)
layout: default
# Data Configuration
data:
object: 'Grav\Common\Flex\Types\Users\UserObject'
collection: 'Grav\Common\Flex\Types\Users\UserCollection'
index: 'Grav\Common\Flex\Types\Users\UserIndex'
storage:
class: 'Grav\Common\Flex\Types\Users\Storage\UserFileStorage'
options:
formatter:
class: 'Grav\Framework\File\Formatter\YamlFormatter'
folder: 'account://'
pattern: '{FOLDER}/{KEY}{EXT}'
indexed: true
key: username
case_sensitive: false
search:
options:
contains: 1
fields:
- key
- email
blueprints:
configure:
fields:
import@:
type: configure/compat
context: blueprints://flex
# Regular form definition
form:
fields:
username:
flex-disabled@: exists
disabled: false
flex-readonly@: exists
readonly: false
validate:
required: true
+123
View File
@@ -0,0 +1,123 @@
title: User Groups
description: Manage your User Groups in Flex.
type: flex-objects
# Extends user group
extends@:
type: group
context: blueprints://user
# Flex configuration
config:
# Administration Configuration (needs Flex Objects plugin)
admin:
# Admin router
router:
path: '/accounts/groups'
actions:
configure:
path: '/accounts/configure'
redirects:
'/groups': '/accounts/groups'
'/accounts': '/accounts/groups'
# Permissions
permissions:
# Primary permissions
admin.users:
type: crudl
label: User Accounts
admin.configuration.users:
type: default
label: Accounts Configuration
# Admin menu
menu:
base:
location: '/accounts'
route: '/accounts/groups'
index: 1
title: PLUGIN_ADMIN.ACCOUNTS
icon: fa-users
authorize: ['admin.users.list', 'admin.super']
priority: 6
# Admin template type (folder)
template: user-groups
# List view
list:
# Fields shown in the list view
fields:
groupname:
link: edit
search: true
readableName:
search: true
description:
search: true
# Extra options
options:
per_page: 20
order:
by: groupname
dir: asc
# Edit view
edit:
title:
template: "{{ form.value('readableName') ?? form.value('groupname') }}"
# Configure view
configure:
hidden: true
authorize: 'admin.configuration.users'
form: 'accounts'
title:
template: "{{ 'PLUGIN_ADMIN.ACCOUNTS'|tu }} {{ 'PLUGIN_ADMIN.CONFIGURATION'|tu }}"
# Site Configuration
site:
# Hide from flex types
hidden: true
templates:
collection:
# Lookup for the template layout files for collections of objects
paths:
- 'flex/{TYPE}/collection/{LAYOUT}{EXT}'
object:
# Lookup for the template layout files for objects
paths:
- 'flex/{TYPE}/object/{LAYOUT}{EXT}'
defaults:
# Default template {TYPE}; overridden by filename of this blueprint if template folder exists
type: user-groups
# Default template {LAYOUT}; can be overridden in render calls (usually Twig in templates)
layout: default
# Data Configuration
data:
object: 'Grav\Common\Flex\Types\UserGroups\UserGroupObject'
collection: 'Grav\Common\Flex\Types\UserGroups\UserGroupCollection'
index: 'Grav\Common\Flex\Types\UserGroups\UserGroupIndex'
storage:
class: 'Grav\Framework\Flex\Storage\SimpleStorage'
options:
formatter:
class: 'Grav\Framework\File\Formatter\YamlFormatter'
folder: 'user://config/groups.yaml'
key: groupname
search:
options:
contains: 1
fields:
- key
- groupname
- description
blueprints:
configure:
fields:
import@:
type: configure/compat
context: blueprints://flex
+1 -1
View File
@@ -121,7 +121,7 @@ form:
underline: true
folder:
type: text
type: folder-slug
label: PLUGIN_ADMIN.FOLDER_NAME
validate:
rule: slug
+2 -4
View File
@@ -1,4 +1,4 @@
title: PLUGIN_ADMIN.MODULAR
title: PLUGIN_ADMIN.MODULE
extends@: default
form:
@@ -13,7 +13,7 @@ form:
modular_title:
type: spacer
title: PLUGIN_ADMIN.MODULAR_SETUP
title: PLUGIN_ADMIN.MODULE_SETUP
header.content.items:
type: text
@@ -34,5 +34,3 @@ form:
help: '"desc" or "asc" are valid values'
placeholder: desc
size: small
@@ -0,0 +1,71 @@
form:
fields:
_site:
type: section
title: PLUGIN_ADMIN.PAGE_ACCESS
underline: true
fields:
header.login.visibility_requires_access:
type: toggle
toggleable: true
label: PLUGIN_ADMIN.PAGE_VISIBILITY_REQUIRES_ACCESS
help: PLUGIN_ADMIN.PAGE_VISIBILITY_REQUIRES_ACCESS_HELP
highlight: 0
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
validate:
type: bool
header.access:
type: acl_picker
label: PLUGIN_ADMIN.PAGE_ACCESS
help: PLUGIN_ADMIN.PAGE_ACCESS_HELP
ignore_empty: true
data_type: access
validate:
type: array
value_type: bool
_admin:
security@: {or: [admin.super, admin.configuration.pages]}
type: section
title: PLUGIN_ADMIN.PAGE PERMISSIONS
underline: true
fields:
header.permissions.inherit:
type: toggle
toggleable: true
label: PLUGIN_ADMIN.PAGE_INHERIT_PERMISSIONS
help: PLUGIN_ADMIN.PAGE_INHERIT_PERMISSIONS_HELP
highlight: 1
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
validate:
type: bool
header.permissions.authors:
type: list
toggleable: true
label: PLUGIN_ADMIN.PAGE_AUTHORS
help: PLUGIN_ADMIN.PAGE_AUTHORS_HELP
fields:
value:
type: text
placeholder: PLUGIN_ADMIN.USERNAME
style: vertical
header.permissions.groups:
ignore@: true
type: acl_picker
label: PLUGIN_ADMIN.PAGE_GROUPS
help: PLUGIN_ADMIN.PAGE_GROUPS_HELP
ignore_empty: true
data_type: permissions
+16
View File
@@ -0,0 +1,16 @@
title: PLUGIN_ADMIN.ROOT
rules:
slug:
pattern: '[a-zA-Zа-яA-Я0-9_\-]+'
min: 1
max: 200
form:
validation: loose
fields:
tabs:
type: tabs
active: 1
+13
View File
@@ -67,6 +67,15 @@ form:
default: 'en'
help: PLUGIN_ADMIN.LANGUAGE_HELP
content_editor:
type: select
label: PLUGIN_ADMIN.CONTENT_EDITOR
size: medium
classes: fancy
data-options@: 'Grav\Plugin\Admin\Admin::contentEditor'
default: 'default'
help: PLUGIN_ADMIN.CONTENT_EDITOR_HELP
twofa_check:
type: conditional
condition: config.plugins.admin.twofa_enabled
@@ -99,6 +108,8 @@ form:
sublabel: PLUGIN_ADMIN.2FA_SECRET_HELP
security:
security@: admin.super
title: PLUGIN_ADMIN.ACCESS_LEVELS
@@ -121,7 +132,9 @@ form:
access:
security@: admin.super
type: permissions
check_authorize: true
label: PLUGIN_ADMIN.PERMISSIONS
ignore_empty: true
validate:
type: array
value_type: bool
+1 -1
View File
@@ -13,6 +13,6 @@ form:
label: PLUGIN_ADMIN.USERNAME
help: PLUGIN_ADMIN.USERNAME_HELP
unset-disabled@: true
unset-readonly@: true
unset-readonly@: true
validate:
required: true
-39
View File
@@ -1,39 +0,0 @@
title: User Accounts
description: User Accounts
type: flex-objects
extends@: 'user/account'
config:
admin:
list:
fields:
username:
link: edit
search: true
email:
search: true
fullname:
search: true
options:
per_page: 20
order:
by: username
dir: asc
menu:
list:
route: '/accounts'
title: Accounts
icon: fa-users
authorize: ['admin.users', 'admin.accounts', 'admin.super']
form:
fields:
username:
flex-disabled@: exists
disabled: false
flex-readonly@: exists
readonly: false
validate:
required: true
+47 -29
View File
@@ -1,37 +1,55 @@
title: Group
rules:
slug:
pattern: '[a-zA-Zа-яA-Я0-9_\-]+'
min: 1
max: 200
form:
validation: loose
validation: loose
fields:
spacer:
type: spacer
text: '<br>'
fields:
groupname:
type: text
size: large
label: PLUGIN_ADMIN.GROUP_NAME
flex-disabled@: exists
flex-readonly@: exists
validate:
required: true
rule: slug
groupname:
type: text
size: large
label: PLUGIN_ADMIN.NAME
disabled: true
readonly: true
readableName:
type: text
size: large
label: PLUGIN_ADMIN.DISPLAY_NAME
readableName:
type: text
size: large
label: PLUGIN_ADMIN_PRO.READABLE_NAME
description:
type: text
size: large
label: PLUGIN_ADMIN.DESCRIPTION
description:
type: text
size: large
label: PLUGIN_ADMIN.DESCRIPTION
icon:
type: text
size: small
label: PLUGIN_ADMIN.ICON
icon:
type: text
size: small
label: PLUGIN_ADMIN_PRO.ICON
enabled:
type: toggle
label: PLUGIN_ADMIN.ENABLED
highlight: 1
default: 1
options:
1: PLUGIN_ADMIN.YES
0: PLUGIN_ADMIN.NO
validate:
type: bool
access:
type: permissions
label: PLUGIN_ADMIN.PERMISSIONS
ignore_empty: true
validate:
type: array
access:
type: permissions
check_authorize: false
label: PLUGIN_ADMIN.PERMISSIONS
ignore_empty: true
validate:
type: array
value_type: bool
+7
View File
@@ -1,5 +1,11 @@
title: PLUGIN_ADMIN_PRO.ADD_GROUP
rules:
slug:
pattern: '[a-zA-Zа-яA-Я0-9_\-]+'
min: 1
max: 200
form:
validation: loose
fields:
@@ -14,3 +20,4 @@ form:
help: PLUGIN_ADMIN_PRO.GROUP_NAME_HELP
validate:
required: true
rule: slug
+7 -3
View File
@@ -24,6 +24,10 @@ types:
type: image
thumb: media/thumb-png.png
mime: image/png
webp:
type: image
thumb: media/thumb-webp.png
mime: image/webp
gif:
type: animated
thumb: media/thumb-gif.png
@@ -103,7 +107,7 @@ types:
docx:
type: file
thumb: media/thumb-docx.png
mime: application/msword
mime: application/vnd.openxmlformats-officedocument.wordprocessingml.document
xls:
type: file
thumb: media/thumb-xls.png
@@ -111,7 +115,7 @@ types:
xlsx:
type: file
thumb: media/thumb-xlsx.png
mime: application/vnd.ms-excel
mime: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
ppt:
type: file
thumb: media/thumb-ppt.png
@@ -119,7 +123,7 @@ types:
pptx:
type: file
thumb: media/thumb-pptx.png
mime: application/vnd.ms-powerpoint
mime: application/vnd.openxmlformats-officedocument.presentationml.presentation
pps:
type: file
thumb: media/thumb-pps.png
+53
View File
@@ -0,0 +1,53 @@
actions:
site:
type: access
label: Site
admin:
type: access
label: Admin
admin.pages:
type: access
label: Pages
admin.users:
type: access
label: User Accounts
types:
default:
type: access
crud:
type: compact
letters:
c:
action: create
label: PLUGIN_ADMIN.CREATE
r:
action: read
label: PLUGIN_ADMIN.READ
u:
action: update
label: PLUGIN_ADMIN.UPDATE
d:
action: delete
label: PLUGIN_ADMIN.DELETE
crudp:
type: crud
letters:
p:
action: publish
label: PLUGIN_ADMIN.PUBLISH
crudl:
type: crud
letters:
l:
action: list
label: PLUGIN_ADMIN.LIST
crudpl:
type: crud
use:
- crudp
- crudl
+1
View File
@@ -36,3 +36,4 @@ uploads_dangerous_extensions:
- htm
- js
- exe
sanitize_svg: true
-16
View File
@@ -1,16 +0,0 @@
schemes:
image:
type: Stream
paths:
- user://images
- system://images
page:
type: ReadOnlyStream
paths:
- user://pages
account:
type: ReadOnlyStream
paths:
- user://accounts
+44 -13
View File
@@ -20,18 +20,21 @@ languages:
supported: [] # List of languages supported. eg: [en, fr, de]
default_lang: # Default is the first supported language. Must be one of the supported languages
include_default_lang: true # Include the default lang prefix in all URLs
pages_fallback_only: false # Only fallback to find page content through supported languages
translations: true # Enable translations by default
include_default_lang_file_extension: true # If true, include language code for the default language in file extension: default.en.md
translations: true # If false, translation keys are used instead of translated strings
translations_fallback: true # Fallback through supported translations if active lang doesn't exist
session_store_active: false # Store active language in session
http_accept_language: false # Attempt to set the language based on http_accept_language header in the browser
override_locale: false # Override the default or system locale with language specific one
content_fallback: {} # Custom language fallbacks. eg: {fr: ['fr', 'en']}
pages_fallback_only: false # DEPRECATED: Use `content_fallback` instead
home:
alias: '/home' # Default path for home, ie /
hide_in_urls: false # Hide the home route in URLs
pages:
type: regular # EXPERIMENTAL: Page type: regular or flex
theme: quark # Default theme (defaults to "quark" theme)
order:
by: default # Order pages by "default", "alpha" or "date"
@@ -70,11 +73,11 @@ pages:
expires: 604800 # Page expires time in seconds (604800 seconds = 7 days)
cache_control: # Can be blank for no setting, or a valid `cache-control` text value
last_modified: false # Set the last modified date header based on file modification timestamp
etag: false # Set the etag header tag
etag: true # Set the etag header tag
vary_accept_encoding: false # Add `Vary: Accept-Encoding` header
redirect_default_route: false # Automatically redirect to a page's default route
redirect_default_code: 302 # Default code to use for redirects
redirect_trailing_slash: true # Handle automatically or 302 redirect a trailing / URL
redirect_default_code: 302 # Default code to use for redirects: 301|302|303
redirect_trailing_slash: 1 # Always redirect trailing slash with redirect code 0|1|301|302 (0: no redirect, 1: use default code)
redirect_default_route: 0 # Always redirect to page's default route using code 0|1|301|302, also removes .htm and .html extensions
ignore_files: [.DS_Store] # Files to ignore in Pages
ignore_folders: [.git, .idea] # Folders to ignore in Pages
ignore_hidden: true # Ignore all Hidden files and folders
@@ -100,14 +103,18 @@ cache:
allow_webserver_gzip: false # If true, `content-encoding: identity` but connection isn't closed before `onShutDown()` event
redis:
socket: false # Path to redis unix socket (e.g. /var/run/redis/redis.sock), false = use server and port to connect
password: # Optional password
database: # Optional database ID
twig:
cache: true # Set to true to enable Twig caching
debug: true # Enable Twig debug
auto_reload: true # Refresh cache on changes
autoescape: false # Autoescape Twig vars (DEPRECATED, always enabled in strict mode)
autoescape: true # Autoescape Twig vars (DEPRECATED, always enabled in strict mode)
undefined_functions: true # Allow undefined functions
undefined_filters: true # Allow undefined filters
safe_functions: [] # List of PHP functions which are allowed to be used as Twig functions
safe_filters: [] # List of PHP functions which are allowed to be used as Twig filters
umask_fix: false # By default Twig creates cached files as 755, fix switches this to 775
assets: # Configuration for Assets Manager (JS, CSS)
@@ -122,6 +129,7 @@ assets: # Configuration for Assets Mana
js_pipeline_before_excludes: true # Render the pipeline before any excluded files
js_minify: true # Minify the JS during pipelining
enable_asset_timestamp: false # Enable asset timestamps
enable_asset_sri: false # Enable asset SRI
collections:
jquery: system://assets/jquery/jquery-2.x.min.js
@@ -136,6 +144,8 @@ log:
debugger:
enabled: false # Enable Grav debugger and following settings
provider: clockwork # Debugger provider: debugbar | clockwork
censored: false # Censor potentially sensitive information (POST parameters, cookies, files, configuration and most array/object data in log messages)
shutdown:
close_connection: true # Close the connection before calling onShutdown(). false for debugging
@@ -144,8 +154,14 @@ images:
cache_all: false # Cache all image by default
cache_perms: '0755' # MUST BE IN QUOTES!! Default cache folder perms. Usually '0755' or '0775'
debug: false # Show an overlay over images indicating the pixel depth of the image when working with retina for example
auto_fix_orientation: false # Automatically fix the image orientation based on the Exif data
auto_fix_orientation: true # Automatically fix the image orientation based on the Exif data
seofriendly: false # SEO-friendly processed image names
cls: # Cumulative Layout Shift: See https://web.dev/optimize-cls/
auto_sizes: false # Automatically add height/width to image
aspect_ratio: false # Reserve space with aspect ratio style
retina_scale: 1 # scale to adjust auto-sizes for better handling of HiDPI resolutions
defaults:
loading: auto # Let browser pick [auto|lazy|eager]
media:
enable_media_timestamp: false # Enable media timestamps
@@ -161,8 +177,10 @@ session:
uniqueness: path # Should sessions be `path` based or `security.salt` based
secure: false # Set session secure. If true, indicates that communication for this cookie must be over an encrypted transmission. Enable this only on sites that run exclusively on HTTPS
httponly: true # Set session HTTP only. If true, indicates that cookies should be used only over HTTP, and JavaScript modification is not allowed.
samesite: Lax # Set session SameSite. Possible values are Lax, Strict and None. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite
split: true # Sessions should be independent between site and plugins (such as admin)
path:
domain: # Domain used by sessions.
path: # Path used by sessions.
gpm:
releases: stable # Set to either 'stable' or 'testing'
@@ -172,9 +190,22 @@ gpm:
official_gpm_only: true # By default GPM direct-install will only allow URLs via the official GPM proxy to ensure security
accounts:
type: data # Account type: data or flex
storage: file # Flex storage type: file or folder
type: regular # EXPERIMENTAL: Account type: regular or flex
storage: file # EXPERIMENTAL: Flex storage type: file or folder
flex:
cache:
index:
enabled: true # Set to true to enable Flex index caching. Is used to cache timestamps in files
lifetime: 60 # Lifetime of cached index in seconds (0 = infinite)
object:
enabled: true # Set to true to enable Flex object caching. Is used to cache object data
lifetime: 600 # Lifetime of cached objects in seconds (0 = infinite)
render:
enabled: true # Set to true to enable Flex render caching. Is used to cache rendered output
lifetime: 600 # Lifetime of cached HTML in seconds (0 = infinite)
strict_mode:
yaml_compat: true # Grav 1.5+: Enables YAML backwards compatibility
twig_compat: true # Grav 1.5+: Enables deprecated Twig autoescape setting (autoescape: false)
yaml_compat: false # Set to true to enable YAML backwards compatibility
twig_compat: false # Set to true to enable deprecated Twig settings (autoescape: false)
blueprint_compat: false # Set to true to enable backward compatible strict support for blueprints
+70 -22
View File
@@ -1,42 +1,90 @@
<?php
/**
* @package Grav\Core
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
// Some standard defines
define('GRAV', true);
define('GRAV_VERSION', '1.6.27');
define('GRAV_VERSION', '1.7.14');
define('GRAV_SCHEMA', '1.7.0_2020-11-20_1');
define('GRAV_TESTING', false);
define('DS', '/');
// PHP minimum requirement
if (!defined('GRAV_PHP_MIN')) {
define('GRAV_PHP_MIN', '7.1.3');
define('GRAV_PHP_MIN', '7.3.6');
}
// Directories and Paths
if (!defined('GRAV_ROOT')) {
define('GRAV_ROOT', str_replace(DIRECTORY_SEPARATOR, DS, getcwd()));
// Directory separator
if (!defined('DS')) {
define('DS', '/');
}
define('ROOT_DIR', GRAV_ROOT . '/');
define('USER_PATH', 'user/');
define('USER_DIR', ROOT_DIR . USER_PATH);
define('CACHE_DIR', ROOT_DIR . 'cache/');
// Absolute path to Grav root. This is where Grav is installed into.
if (!defined('GRAV_ROOT')) {
$path = rtrim(str_replace(DIRECTORY_SEPARATOR, DS, getenv('GRAV_ROOT') ?: getcwd()), DS);
define('GRAV_ROOT', $path);
}
// Absolute path to Grav webroot. This is the path where your site is located in.
if (!defined('GRAV_WEBROOT')) {
$path = rtrim(getenv('GRAV_WEBROOT') ?: GRAV_ROOT, DS);
define('GRAV_WEBROOT', $path);
}
// Relative path to user folder. This path needs to be located under GRAV_WEBROOT.
if (!defined('GRAV_USER_PATH')) {
$path = rtrim(getenv('GRAV_USER_PATH') ?: 'user', DS);
define('GRAV_USER_PATH', $path);
}
// Absolute or relative path to system folder. Defaults to GRAV_ROOT/system
// If system folder is outside of webroot, see https://github.com/getgrav/grav/issues/3297#issuecomment-810294972
if (!defined('GRAV_SYSTEM_PATH')) {
$path = rtrim(getenv('GRAV_SYSTEM_PATH') ?: 'system', DS);
define('GRAV_SYSTEM_PATH', $path);
}
// Absolute or relative path to cache folder. Defaults to GRAV_ROOT/cache
if (!defined('GRAV_CACHE_PATH')) {
$path = rtrim(getenv('GRAV_CACHE_PATH') ?: 'cache', DS);
define('GRAV_CACHE_PATH', $path);
}
// Absolute or relative path to logs folder. Defaults to GRAV_ROOT/logs
if (!defined('GRAV_LOG_PATH')) {
$path = rtrim(getenv('GRAV_LOG_PATH') ?: 'logs', DS);
define('GRAV_LOG_PATH', $path);
}
// Absolute or relative path to tmp folder. Defaults to GRAV_ROOT/tmp
if (!defined('GRAV_TMP_PATH')) {
$path = rtrim(getenv('GRAV_TMP_PATH') ?: 'tmp', DS);
define('GRAV_TMP_PATH', $path);
}
// Absolute or relative path to backup folder. Defaults to GRAV_ROOT/backup
if (!defined('GRAV_BACKUP_PATH')) {
$path = rtrim(getenv('GRAV_BACKUP_PATH') ?: 'backup', DS);
define('GRAV_BACKUP_PATH', $path);
}
unset($path);
// INTERNAL: Do not use!
define('USER_DIR', GRAV_WEBROOT . '/' . GRAV_USER_PATH . '/');
define('CACHE_DIR', (!preg_match('`^(/|[a-z]:[\\\/])`ui', GRAV_CACHE_PATH) ? GRAV_ROOT . '/' : '') . GRAV_CACHE_PATH . '/');
// DEPRECATED: Do not use!
define('ASSETS_DIR', ROOT_DIR . 'assets/');
define('IMAGES_DIR', ROOT_DIR . 'images/');
define('ACCOUNTS_DIR', USER_DIR .'accounts/');
define('PAGES_DIR', USER_DIR .'pages/');
define('DATA_DIR', USER_DIR .'data/');
define('SYSTEM_DIR', ROOT_DIR .'system/');
define('LIB_DIR', SYSTEM_DIR .'src/');
define('PLUGINS_DIR', USER_DIR .'plugins/');
define('THEMES_DIR', USER_DIR .'themes/');
define('VENDOR_DIR', ROOT_DIR .'vendor/');
define('LOG_DIR', ROOT_DIR .'logs/');
define('CACHE_PATH', GRAV_CACHE_PATH . DS);
define('USER_PATH', GRAV_USER_PATH . DS);
define('ROOT_DIR', GRAV_ROOT . DS);
define('ASSETS_DIR', GRAV_WEBROOT . '/assets/');
define('IMAGES_DIR', GRAV_WEBROOT . '/images/');
define('ACCOUNTS_DIR', USER_DIR . 'accounts/');
define('PAGES_DIR', USER_DIR . 'pages/');
define('DATA_DIR', USER_DIR . 'data/');
define('PLUGINS_DIR', USER_DIR . 'plugins/');
define('THEMES_DIR', USER_DIR . 'themes/');
define('SYSTEM_DIR', (!preg_match('`^(/|[a-z]:[\\\/])`ui', GRAV_SYSTEM_PATH) ? GRAV_ROOT . '/' : '') . GRAV_SYSTEM_PATH . '/');
define('LIB_DIR', SYSTEM_DIR . 'src/');
define('VENDOR_DIR', GRAV_ROOT . '/vendor/');
define('LOG_DIR', (!preg_match('`^(/|[a-z]:[\\\/])`ui', GRAV_LOG_PATH) ? GRAV_ROOT . '/' : '') . GRAV_LOG_PATH . '/');
// END DEPRECATED
// Some extensions
+2 -4
View File
@@ -2,7 +2,7 @@
/**
* @package Grav\Core
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@@ -10,8 +10,6 @@ if (!defined('GRAV_ROOT')) {
die();
}
use Grav\Installer\Install;
require_once __DIR__ . '/src/Grav/Installer/Install.php';
return Install::instance();
return Grav\Installer\Install::instance();
+21 -2
View File
@@ -1,6 +1,17 @@
---
GRAV:
FRONTMATTER_ERROR_PAGE: "---\nالعنوان: %1$s\n---\n# خطأ: مادة أمامية غير صحيحة\n\nمسار: '%2$s'\n\n**%3$s**\n\n, , ,\n\n%4$s\n, , ,"
INFLECTOR_UNCOUNTABLE:
- 'معدّات'
- 'معلومات'
- 'أرز'
- 'مال'
- 'نوع'
- 'سلسلة'
- 'سمك'
- 'خروف'
INFLECTOR_IRREGULAR:
'person': 'أشخاص'
NICETIME:
NO_DATE_PROVIDED: لم يتم تقديم التاريخ
BAD_DATE: تاريخ خاطئ
@@ -37,8 +48,8 @@ GRAV:
YR_PLURAL: سنوات
DEC_PLURAL: عقود
FORM:
VALIDATION_FAIL: <b>فشل التحقق من صحة:</b>
INVALID_INPUT: إدخال غير صحيح في
VALIDATION_FAIL: '<b>فشل التحقق من صحة:</b>'
INVALID_INPUT: 'إدخال غير صحيح في'
MISSING_REQUIRED_FIELD: 'حقل مطلوب مفقود:'
MONTHS_OF_THE_YEAR:
- 'كانون الثاني'
@@ -61,3 +72,11 @@ GRAV:
- 'الجمعة'
- 'السبت'
- 'الأحد'
CRON:
EVERY: كل
EVERY_HOUR: كل ساعة
EVERY_MINUTE: كل دقيقة
EVERY_DAY_OF_WEEK: كل يوم في الأسبوع
EVERY_DAY_OF_MONTH: كل يوم في الشهر
EVERY_MONTH: ' كل شهر'
TEXT_PERIOD: كل <b />
+2 -2
View File
@@ -36,8 +36,8 @@ GRAV:
YR_PLURAL: г
DEC_PLURAL: дстлт
FORM:
VALIDATION_FAIL: <b>Неуспешна проверка:</b>
INVALID_INPUT: Невалидно въвеждане в
VALIDATION_FAIL: '<b>Неуспешна проверка:</b>'
INVALID_INPUT: 'Невалидно въвеждане в'
MISSING_REQUIRED_FIELD: 'Липсва задължително поле:'
MONTHS_OF_THE_YEAR:
- 'януари'
+11 -2
View File
@@ -1,6 +1,15 @@
---
GRAV:
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# S'ha produït un error: Frontmatter invàlid\n\nRuta: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
INFLECTOR_UNCOUNTABLE:
- 'equipment'
- 'informació'
- 'rice'
- 'money'
- 'species'
- 'series'
- 'fish'
- 'sheep'
NICETIME:
NO_DATE_PROVIDED: No s'ha proporcionat data
BAD_DATE: Data invàlida
@@ -36,8 +45,8 @@ GRAV:
YR_PLURAL: anys
DEC_PLURAL: dèc.
FORM:
VALIDATION_FAIL: <b>Ha fallat la validació:</b>
INVALID_INPUT: Entrada no vàlida a
VALIDATION_FAIL: '<b>Ha fallat la validació:</b>'
INVALID_INPUT: 'Entrada no vàlida a'
MISSING_REQUIRED_FIELD: 'Falta camp obligatori:'
MONTHS_OF_THE_YEAR:
- 'Gener'
+5 -2
View File
@@ -101,9 +101,10 @@ GRAV:
YR_PLURAL: r
DEC_PLURAL: dek
FORM:
VALIDATION_FAIL: <b>Ověření se nezdařilo:</b>
INVALID_INPUT: Neplatný vstup v
VALIDATION_FAIL: '<b>Ověření se nezdařilo:</b>'
INVALID_INPUT: 'Neplatný vstup v'
MISSING_REQUIRED_FIELD: 'Chybí požadované pole:'
XSS_ISSUES: "Byly zjištěny možné problémy XSS v poli '%s'"
MONTHS_OF_THE_YEAR:
- 'leden'
- 'únor'
@@ -125,6 +126,8 @@ GRAV:
- 'pátek'
- 'sobota'
- 'neděle'
YES: "Ano"
NO: "Ne"
CRON:
EVERY: každý
EVERY_HOUR: každou hodinu
+30 -2
View File
@@ -1,11 +1,27 @@
---
GRAV:
FRONTMATTER_ERROR_PAGE: "---\nTitel: %1$s\n---\n\n# Fejl: Ugyldigt frontmatter\n\nSti: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
INFLECTOR_UNCOUNTABLE:
- 'udstyr'
- 'information'
- 'ris'
- 'penge'
- 'arter'
- 'Serier'
- 'fisk'
- 'får'
INFLECTOR_IRREGULAR:
'person': 'personer'
'man': 'mænd'
'child': 'børn'
'sex': 'køn'
'move': 'flyt'
NICETIME:
NO_DATE_PROVIDED: Ingen dato angivet
BAD_DATE: Ugyldig dato
AGO: siden
FROM_NOW: fra nu
JUST_NOW: lige nu
SECOND: sekund
MINUTE: minut
HOUR: time
@@ -15,6 +31,7 @@ GRAV:
YEAR: år
DECADE: årti
SEC: sek
MIN: min.
HR: t
WK: u
MO: md
@@ -36,8 +53,8 @@ GRAV:
YR_PLURAL: år
DEC_PLURAL: årtier
FORM:
VALIDATION_FAIL: <b>Validering mislykkedes:</b>
INVALID_INPUT: Ugyldigt input i
VALIDATION_FAIL: '<b>Validering mislykkedes:</b>'
INVALID_INPUT: 'Ugyldigt input i'
MISSING_REQUIRED_FIELD: 'Mangler obligatorisk felt:'
MONTHS_OF_THE_YEAR:
- 'januar'
@@ -60,3 +77,14 @@ GRAV:
- 'fredag'
- 'lørdag'
- 'søndag'
CRON:
EVERY: hver
EVERY_HOUR: hver time
EVERY_MINUTE: hvert minut
EVERY_DAY_OF_WEEK: alle ugens dage
EVERY_DAY_OF_MONTH: alle dage i måneden
EVERY_MONTH: hver måned
TEXT_PERIOD: Hver <b />
TEXT_MINS: ' ved <b /> minut(ter) over timen'
ERROR1: Tagget %s understøttes ikke!
ERROR2: Ugyldigt antal elementer
+5 -2
View File
@@ -101,9 +101,10 @@ GRAV:
YR_PLURAL: Jahre
DEC_PLURAL: Jahrzehnten
FORM:
VALIDATION_FAIL: <b>Überprüfung fehlgeschlagen:</b>
INVALID_INPUT: Ungültige Eingabe in
VALIDATION_FAIL: '<b>Überprüfung fehlgeschlagen:</b>'
INVALID_INPUT: 'Ungültige Eingabe in'
MISSING_REQUIRED_FIELD: 'Erforderliches Feld fehlt:'
XSS_ISSUES: "Potenzielle XSS-Probleme im Feld '%s' erkannt"
MONTHS_OF_THE_YEAR:
- 'Januar'
- 'Februar'
@@ -125,6 +126,8 @@ GRAV:
- 'Freitag'
- 'Samstag'
- 'Sonntag'
YES: "Ja"
NO: "Nein"
CRON:
EVERY: jede
EVERY_HOUR: jede Stunde
+83 -2
View File
@@ -1,11 +1,75 @@
---
GRAV:
FRONTMATTER_ERROR_PAGE: "---\nΤίτλος: %1$s\n---\n\n# Σφάλμα: Μη έγκυρη διαδρομή Frontmatter\n\nΔιαδρομή: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
INFLECTOR_PLURALS:
'/(quiz)$/i': '\1zes'
'/^(ox)$/i': '\1en'
'/([m|l])ouse$/i': '\1ice'
'/(matr|vert|ind)ix|ex$/i': '\1ices'
'/(x|ch|ss|sh)$/i': '\1es'
'/([^aeiouy]|qu)ies$/i': '\1y'
'/([^aeiouy]|qu)y$/i': '\1ies'
'/(hive)$/i': '\1s'
'/(?:([^f])fe|([lr])f)$/i': '\1\2ves'
'/sis$/i': 'ses'
'/([ti])um$/i': '\1a'
'/(buffal|tomat)o$/i': '\1oes'
'/(bu)s$/i': '\1ses'
'/(alias|status)/i': '\1es'
'/(octop|vir)us$/i': '\1i'
'/(ax|test)is$/i': '\1es'
'/s$/i': 's'
'/$/': 's'
INFLECTOR_SINGULAR:
'/(quiz)zes$/i': '\1'
'/(matr)ices$/i': '\1ix'
'/(vert|ind)ices$/i': '\1ex'
'/^(ox)en/i': '\1'
'/(alias|status)es$/i': '\1'
'/([octop|vir])i$/i': '\1us'
'/(cris|ax|test)es$/i': '\1is'
'/(shoe)s$/i': '\1'
'/(o)es$/i': '\1'
'/(bus)es$/i': '\1'
'/([m|l])ice$/i': '\1ouse'
'/(x|ch|ss|sh)es$/i': '\1'
'/(m)ovies$/i': '\1ovie'
'/(s)eries$/i': '\1eries'
'/([^aeiouy]|qu)ies$/i': '\1y'
'/([lr])ves$/i': '\1f'
'/(tive)s$/i': '\1'
'/(hive)s$/i': '\1'
'/([^f])ves$/i': '\1fe'
'/(^analy)ses$/i': '\1sis'
'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i': '\1\2sis'
'/([ti])a$/i': '\1um'
'/(n)ews$/i': '\1ews'
INFLECTOR_UNCOUNTABLE:
- 'εξοπλισμός'
- 'πληροφοριες'
- 'rice'
- 'χρήματα'
- 'είδη'
- 'σειρές'
- 'ψάρι'
- 'πρόβατο'
INFLECTOR_IRREGULAR:
'person': 'άνθρωποι'
'man': 'άνδρες'
'child': 'παιδιά'
'sex': 'φύλο'
'move': 'κινήσεις'
INFLECTOR_ORDINALS:
'default': 'th'
'first': 'st'
'second': 'nd'
'third': 'rd'
NICETIME:
NO_DATE_PROVIDED: Δεν δόθηκε καμία ημερομηνία
BAD_DATE: Εσφαλμένη ημερομηνία
AGO: πρίν
FROM_NOW: από τώρα
JUST_NOW: μόλις τώρα
SECOND: δευτερόλεπτο
MINUTE: λεπτό
HOUR: ώρα
@@ -37,8 +101,8 @@ GRAV:
YR_PLURAL: έτη
DEC_PLURAL: δεκαετίες
FORM:
VALIDATION_FAIL: <b>Η επικύρωση απέτυχε:</b>
INVALID_INPUT: Μη έγκυρα δεδομένα σε
VALIDATION_FAIL: '<b>Η επικύρωση απέτυχε:</b>'
INVALID_INPUT: 'Μη έγκυρα δεδομένα σε'
MISSING_REQUIRED_FIELD: 'Λείπει το απαιτούμενο πεδίο:'
MONTHS_OF_THE_YEAR:
- 'Ιανουάριος'
@@ -61,3 +125,20 @@ GRAV:
- 'Παρασκευή'
- 'Σάββατο'
- 'Κυριακή'
CRON:
EVERY: κάθε
EVERY_HOUR: κάθε ώρα
EVERY_MINUTE: κάθε λεπτό
EVERY_DAY_OF_WEEK: κάθε μέρα της εβδομάδος
EVERY_DAY_OF_MONTH: κάθε μέρα του μήνα
EVERY_MONTH: κάθε μήνα
TEXT_PERIOD: Κάθε <b />
TEXT_MINS: ' κατά <b /> λεπτό(ά) μετά την ώρα'
TEXT_TIME: ' στο <b />:<b />'
TEXT_DOW: ' στις <b />'
TEXT_MONTH: ' από <b />'
TEXT_DOM: ' στις <b />'
ERROR1: Η ετικέτα %s δεν υποστηρίζεται!
ERROR2: Μη έγκυρος αριθμός στοιχείων
ERROR3: Το jquery_element θα έπρεπε να οριστεί στις ρυθμίσεις του jqCron
ERROR4: Μη αναγνωρισμένη έκφραση
+4 -3
View File
@@ -94,9 +94,10 @@ GRAV:
YR_PLURAL: yrs
DEC_PLURAL: decs
FORM:
VALIDATION_FAIL: <b>Validation failed:</b>
INVALID_INPUT: Invalid input in
MISSING_REQUIRED_FIELD: Missing required field:
VALIDATION_FAIL: '<b>Validation failed:</b>'
INVALID_INPUT: 'Invalid input in'
MISSING_REQUIRED_FIELD: 'Missing required field:'
XSS_ISSUES: "Potential XSS issues detected in '%s' field"
MONTHS_OF_THE_YEAR: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
DAYS_OF_THE_WEEK: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
YES: "Yes"
+16 -4
View File
@@ -1,19 +1,25 @@
---
GRAV:
FRONTMATTER_ERROR_PAGE: "---\ntítulo: %1$s\n---\n\n# Error: Frontmatter no válido\n\nRuta: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
FRONTMATTER_ERROR_PAGE: "---\ntítulo: %1$s\n---\n\n# Error: Prefacio no válido\n\nRuta: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
INFLECTOR_PLURALS:
'/(quiz)$/i': '\1ios'
'/s$/i': 's'
'/$/': 's'
INFLECTOR_UNCOUNTABLE:
- 'equipo'
- 'equipamiento'
- 'información'
- 'rice'
- 'arroz'
- 'dinero'
- 'species'
- 'especies'
- 'series'
- 'pescado'
- 'oveja'
INFLECTOR_IRREGULAR:
'person': 'personas'
'man': 'hombres'
'child': 'niños'
'sex': 'sexos'
'move': 'movido'
INFLECTOR_ORDINALS:
'first': 'ro'
'second': 'do'
@@ -33,10 +39,12 @@ GRAV:
YEAR: año
DECADE: década
SEC: seg
MIN: min
HR: h
WK: sem
MO: mes
YR: año
DEC: dic
SECOND_PLURAL: segundos
MINUTE_PLURAL: minutos
HOUR_PLURAL: horas
@@ -46,6 +54,7 @@ GRAV:
YEAR_PLURAL: años
DECADE_PLURAL: décadas
SEC_PLURAL: segs
MIN_PLURAL: mins
HR_PLURAL: hs
WK_PLURAL: sem
MO_PLURAL: mes
@@ -55,6 +64,7 @@ GRAV:
VALIDATION_FAIL: '<b>Falló la validación: </b>'
INVALID_INPUT: 'Dato inválido en: '
MISSING_REQUIRED_FIELD: 'Falta el campo requerido: '
XSS_ISSUES: "Se detectaron problemas XSS potenciales en el campo '%s'"
MONTHS_OF_THE_YEAR:
- 'Enero'
- 'Febrero'
@@ -76,6 +86,8 @@ GRAV:
- 'Viernes'
- 'Sábado'
- 'Domingo'
YES: "Si"
NO: "No"
CRON:
EVERY: cada
EVERY_HOUR: cada hora
+22 -3
View File
@@ -1,11 +1,22 @@
---
GRAV:
FRONTMATTER_ERROR_PAGE: "---\npealkiri: %1$s\n---\n\n# Viga: vigane Frontmatter'i\n\nasukoht: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
INFLECTOR_PLURALS:
'/(octop|vir)us$/i': '\1i'
INFLECTOR_SINGULAR:
'/^(ox)en/i': '\1'
'/(alias|status)es$/i': '\1'
'/(shoe)s$/i': '\1'
'/(o)es$/i': '\1'
'/(bus)es$/i': '\1'
'/(x|ch|ss|sh)es$/i': '\1'
'/(tive)s$/i': '\1'
'/(hive)s$/i': '\1'
INFLECTOR_UNCOUNTABLE:
- 'equipment'
- 'informatsioon'
- 'rice'
- 'money'
- 'riis'
- 'raha'
- 'species'
- 'series'
- 'kala'
@@ -34,6 +45,7 @@ GRAV:
YEAR: aasta
DECADE: 10 aastat
SEC: sek
MIN: min
HR: t
WK: näd
MO: k.
@@ -55,7 +67,7 @@ GRAV:
YR_PLURAL: aastat
DEC_PLURAL: dek.
FORM:
VALIDATION_FAIL: <b>Kinnitamine nurjus:</b>
VALIDATION_FAIL: '<b>Kinnitamine nurjus:</b>'
INVALID_INPUT: 'Vigane sisend:'
MISSING_REQUIRED_FIELD: 'Nõutud väli puudub:'
MONTHS_OF_THE_YEAR:
@@ -81,5 +93,12 @@ GRAV:
- 'pühapäev'
CRON:
EVERY: iga
EVERY_HOUR: iga tund
EVERY_MINUTE: iga minut
EVERY_DAY_OF_WEEK: iga nädala päev
EVERY_MONTH: iga kuu
TEXT_PERIOD: Iga <b />
ERROR1: Silt %s pole toetatud!
ERROR2: Vale elementide arv
ERROR3: jqCron seadetes peaks olema määratud jquery_element
ERROR4: Tundmatu väljend
+2 -2
View File
@@ -36,8 +36,8 @@ GRAV:
YR_PLURAL: urt
DEC_PLURAL: ham
FORM:
VALIDATION_FAIL: <b>Balidazioak huts egin du</b>
INVALID_INPUT: Baliogabeko sarrera
VALIDATION_FAIL: '<b>Balidazioak huts egin du</b>'
INVALID_INPUT: 'Baliogabeko sarrera'
MISSING_REQUIRED_FIELD: 'Derrigorrezko eremua bete gabe:'
MONTHS_OF_THE_YEAR:
- 'Urtarrila'
+2 -2
View File
@@ -36,8 +36,8 @@ GRAV:
YR_PLURAL: سال
DEC_PLURAL: دهه
FORM:
VALIDATION_FAIL: <b>سنجش اعتبار ناموفق بود</b>
INVALID_INPUT: ورودی نامعتبر در
VALIDATION_FAIL: '<b>سنجش اعتبار ناموفق بود</b>'
INVALID_INPUT: 'ورودی نامعتبر در'
MISSING_REQUIRED_FIELD: 'قسمت ضروری جا افتاده:'
MONTHS_OF_THE_YEAR:
- 'ژانویه'
+2 -2
View File
@@ -100,8 +100,8 @@ GRAV:
YR_PLURAL: v
DEC_PLURAL: vuosikymmentä
FORM:
VALIDATION_FAIL: <b>Vahvistus epäonnistui:</b>
INVALID_INPUT: Syöte ei kelpaa
VALIDATION_FAIL: '<b>Vahvistus epäonnistui:</b>'
INVALID_INPUT: 'Syöte ei kelpaa'
MISSING_REQUIRED_FIELD: 'Puuttuva pakollinen kenttä:'
MONTHS_OF_THE_YEAR:
- 'Tammikuu'
+22 -21
View File
@@ -16,6 +16,7 @@ GRAV:
'/(buffal|tomat)o$/i': '\1es'
'/(bu)s$/i': 'Bus'
'/(alias|status)/i': 'alias|status'
'/(octop|vir)us$/i': 'virus'
'/(ax|test)is$/i': '\1s'
'/s$/i': 's'
'/$/': 's'
@@ -80,30 +81,30 @@ GRAV:
YR_PLURAL: a
DEC_PLURAL: décs
FORM:
VALIDATION_FAIL: <b>La validation a échoué :</b>
INVALID_INPUT: Saisie non valide
VALIDATION_FAIL: '<b>La validation a échoué :</b>'
INVALID_INPUT: 'Saisie non valide'
MISSING_REQUIRED_FIELD: 'Champ obligatoire manquant :'
MONTHS_OF_THE_YEAR:
- 'Janvier'
- 'Février'
- 'Mars'
- 'Avril'
- 'Mai'
- 'Juin'
- 'Juillet'
- 'Août'
- 'Septembre'
- 'Octobre'
- 'Novembre'
- 'Décembre'
- 'janvier'
- 'février'
- 'mars'
- 'avril'
- 'mai'
- 'juin'
- 'juillet'
- 'août'
- 'septembre'
- 'octobre'
- 'novembre'
- 'décembre'
DAYS_OF_THE_WEEK:
- 'Lundi'
- 'Mardi'
- 'Mercredi'
- 'Jeudi'
- 'Vendredi'
- 'Samedi'
- 'Dimanche'
- 'lundi'
- 'mardi'
- 'mercredi'
- 'jeudi'
- 'vendredi'
- 'samedi'
- 'dimanche'
CRON:
EVERY: chaque
EVERY_HOUR: toutes les heures
+144
View File
@@ -0,0 +1,144 @@
---
GRAV:
FRONTMATTER_ERROR_PAGE: "---\ntítulo: %1$s\n---\n\n# Erro: Limiar incorrecto\n\nRuta: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
INFLECTOR_PLURALS:
'/(quiz)$/i': '\1zes'
'/^(ox)$/i': '\1en'
'/([m|l])ouse$/i': '\1ice'
'/(matr|vert|ind)ix|ex$/i': '\1ices'
'/(x|ch|ss|sh)$/i': '\1es'
'/([^aeiouy]|qu)ies$/i': '\1y'
'/([^aeiouy]|qu)y$/i': '\1ies'
'/(hive)$/i': '\1s'
'/(?:([^f])fe|([lr])f)$/i': '\1\2ves'
'/sis$/i': 'ses'
'/([ti])um$/i': '\1a'
'/(buffal|tomat)o$/i': '\1oes'
'/(bu)s$/i': '\1ses'
'/(alias|status)/i': '\1'
'/(octop|vir)us$/i': '\1'
'/(ax|test)is$/i': '\1es'
'/s$/i': 's'
'/$/': 's'
INFLECTOR_SINGULAR:
'/(quiz)zes$/i': '\1ces'
'/(matr)ices$/i': '\1ix'
'/(vert|ind)ices$/i': '\1ex'
'/^(ox)en/i': '\1'
'/(alias|status)es$/i': '\1'
'/([octop|vir])i$/i': '\1'
'/(cris|ax|test)es$/i': '\1es'
'/(shoe)s$/i': '\1'
'/(o)es$/i': '\1'
'/(bus)es$/i': '\1'
'/([m|l])ice$/i': '\1ouse'
'/(x|ch|ss|sh)es$/i': '\1'
'/(m)ovies$/i': '\1ovie'
'/(s)eries$/i': '\1eries'
'/([^aeiouy]|qu)ies$/i': '\1'
'/([lr])ves$/i': '\1f'
'/(tive)s$/i': '\1'
'/(hive)s$/i': '\1'
'/([^f])ves$/i': '\1fe'
'/(^analy)ses$/i': '\1se'
'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i': '\1\2se'
'/([ti])a$/i': '\1um'
'/(n)ews$/i': '\1ews'
INFLECTOR_UNCOUNTABLE:
- 'equipo'
- 'información'
- 'arroz'
- 'diñeiro'
- 'especies'
- 'series'
- 'peixe'
- 'ovella'
INFLECTOR_IRREGULAR:
'person': 'xente'
'man': 'home'
'child': 'neno'
'sex': 'sexos'
'move': 'move'
INFLECTOR_ORDINALS:
'default': 'º'
'first': 'º'
'second': 'º'
'third': 'º'
NICETIME:
NO_DATE_PROVIDED: Non fornece unha data
BAD_DATE: Data errada
AGO: hai
FROM_NOW: dende agora
JUST_NOW: xusto agora
SECOND: segundo
MINUTE: minuto
HOUR: hora
DAY: día
WEEK: semana
MONTH: mes
YEAR: ano
DECADE: década
SEC: seg
MIN: min
HR: hr
WK: Sem
MO: m
YR: a
DEC: dec
SECOND_PLURAL: segundos
MINUTE_PLURAL: minutos
HOUR_PLURAL: horas
DAY_PLURAL: días
WEEK_PLURAL: semanas
MONTH_PLURAL: meses
YEAR_PLURAL: anos
DECADE_PLURAL: décadas
SEC_PLURAL: segs
MIN_PLURAL: mins
HR_PLURAL: hrs
WK_PLURAL: sem
MO_PLURAL: mes
YR_PLURAL: a
DEC_PLURAL: deca
FORM:
VALIDATION_FAIL: '<b>Fallou a validación:</b>'
INVALID_INPUT: 'Entrada incorrecta en'
MISSING_REQUIRED_FIELD: 'Falta un campo requirido:'
MONTHS_OF_THE_YEAR:
- 'xaneiro'
- 'febreiro'
- 'marzo'
- 'abril'
- 'maio'
- 'xuño'
- 'xullo'
- 'agosto'
- 'setembro'
- 'outubro'
- 'novembro'
- 'decembro'
DAYS_OF_THE_WEEK:
- 'luns'
- 'martes'
- 'mércores'
- 'xoves'
- 'venres'
- 'sábado'
- 'domingo'
CRON:
EVERY: cada
EVERY_HOUR: Cada hora
EVERY_MINUTE: Cada minuto
EVERY_DAY_OF_WEEK: cada día da semana
EVERY_DAY_OF_MONTH: cada día do mes
EVERY_MONTH: cada mes
TEXT_PERIOD: Cada <b />
TEXT_MINS: ' dentro de <b /> minuto(s) despois da hora'
TEXT_TIME: ' dentro <b />:<b />'
TEXT_DOW: ' o <b />'
TEXT_MONTH: ' de <b />'
TEXT_DOM: ' o <b />'
ERROR1: A etiqueta %s non é compatíbel!
ERROR2: Mal número de elementos
ERROR3: O jquery_element debería estar determinado na configuración de jqCron
ERROR4: Expresión non recoñecida
+2 -2
View File
@@ -37,8 +37,8 @@ GRAV:
YR_PLURAL: שני'
DEC_PLURAL: עש'
FORM:
VALIDATION_FAIL: <b>האימות נכשל:</b>
INVALID_INPUT: קלט לא חוקי
VALIDATION_FAIL: '<b>האימות נכשל:</b>'
INVALID_INPUT: 'קלט לא חוקי'
MISSING_REQUIRED_FIELD: 'שדות חובה חסרים:'
MONTHS_OF_THE_YEAR:
- 'ינואר'
+2 -2
View File
@@ -50,8 +50,8 @@ GRAV:
YR_PLURAL: g
DEC_PLURAL: des
FORM:
VALIDATION_FAIL: <b>Validacija nije uspjela:</b>
INVALID_INPUT: Pogrešan unos u
VALIDATION_FAIL: '<b>Validacija nije uspjela:</b>'
INVALID_INPUT: 'Pogrešan unos u'
MISSING_REQUIRED_FIELD: 'Nedostaje obavezno polje:'
MONTHS_OF_THE_YEAR:
- 'Siječanj'
+1 -1
View File
@@ -58,7 +58,7 @@ GRAV:
YR_PLURAL: év
DEC_PLURAL: évt
FORM:
VALIDATION_FAIL: <b>Érvényesítés nem sikerült:</b>
VALIDATION_FAIL: '<b>Érvényesítés nem sikerült:</b>'
INVALID_INPUT: 'A megadott érték érvénytelen:'
MISSING_REQUIRED_FIELD: 'Ez a kötelező mező nincs kitöltve:'
MONTHS_OF_THE_YEAR:
+4 -2
View File
@@ -1,6 +1,8 @@
---
GRAV:
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# Error: Frontmatter tidak valid\n\nLokasi: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
INFLECTOR_PLURALS:
'/(quiz)$/i': '\1zes'
INFLECTOR_UNCOUNTABLE:
- 'peralatan'
- 'informasi'
@@ -53,8 +55,8 @@ GRAV:
YR_PLURAL: thn
DEC_PLURAL: dekade
FORM:
VALIDATION_FAIL: <b>Validasi gagal:</b>
INVALID_INPUT: Input tidak valid di
VALIDATION_FAIL: '<b>Validasi gagal:</b>'
INVALID_INPUT: 'Input tidak valid di'
MISSING_REQUIRED_FIELD: 'Data yang diperlukan belum terisi:'
MONTHS_OF_THE_YEAR:
- 'Januari'
+2 -2
View File
@@ -46,8 +46,8 @@ GRAV:
YR_PLURAL: árum
DEC_PLURAL: árat
FORM:
VALIDATION_FAIL: <b>Sannvottun mistókst:</b>
INVALID_INPUT: Ógilt inntak í
VALIDATION_FAIL: '<b>Sannvottun mistókst:</b>'
INVALID_INPUT: 'Ógilt inntak í'
MISSING_REQUIRED_FIELD: 'Vantar nauðsynlegan reit:'
MONTHS_OF_THE_YEAR:
- 'janúar'
+48 -2
View File
@@ -1,6 +1,49 @@
---
GRAV:
FRONTMATTER_ERROR_PAGE: "---Titolo: %1$s---# Errore: Frontmatter non valido: '%2$s' * *%3$s * * ' '%4$s ' '"
INFLECTOR_PLURALS:
'/(quiz)$/i': '\1'
'/^(ox)$/i': '\1en'
'/([m|l])ouse$/i': '\1ice'
'/(matr|vert|ind)ix|ex$/i': '\1ices'
'/(x|ch|ss|sh)$/i': '\1es'
'/([^aeiouy]|qu)ies$/i': '\1y'
'/([^aeiouy]|qu)y$/i': '\1ies'
'/(hive)$/i': '\1s'
'/(?:([^f])fe|([lr])f)$/i': '\1\2ves'
'/sis$/i': 'ses'
'/([ti])um$/i': '\1a'
'/(buffal|tomat)o$/i': '\1oes'
'/(bu)s$/i': '\1ses'
'/(alias|status)/i': '\1es'
'/(octop|vir)us$/i': '\1i'
'/(ax|test)is$/i': '\1es'
'/s$/i': 's'
'/$/': 's'
INFLECTOR_SINGULAR:
'/(quiz)zes$/i': '\1'
'/(matr)ices$/i': '\1ix'
'/(vert|ind)ices$/i': '\1ex'
'/^(ox)en/i': '\1'
'/(alias|status)es$/i': '\1'
'/([octop|vir])i$/i': '\1us'
'/(cris|ax|test)es$/i': '\1is'
'/(shoe)s$/i': '\1'
'/(o)es$/i': '\1'
'/(bus)es$/i': '\1'
'/([m|l])ice$/i': '\1ouse'
'/(x|ch|ss|sh)es$/i': '\1'
'/(m)ovies$/i': '\1ovie'
'/(s)eries$/i': '\1eries'
'/([^aeiouy]|qu)ies$/i': '\1y'
'/([lr])ves$/i': '\1f'
'/(tive)s$/i': '\1'
'/(hive)s$/i': '\1'
'/([^f])ves$/i': '\1fe'
'/(^analy)ses$/i': '\1sis'
'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i': '\1\2sis'
'/([ti])a$/i': '\1um'
'/(n)ews$/i': '\1ews'
INFLECTOR_UNCOUNTABLE:
- 'dotazione'
- 'informazione'
@@ -58,9 +101,10 @@ GRAV:
YR_PLURAL: anni
DEC_PLURAL: decenni
FORM:
VALIDATION_FAIL: <b>Validazione fallita:</b>
INVALID_INPUT: Input non valido in
VALIDATION_FAIL: '<b>Validazione fallita:</b>'
INVALID_INPUT: 'Input non valido in'
MISSING_REQUIRED_FIELD: 'Campo richiesto mancante:'
XSS_ISSUES: "Rilevati potenziali problemi di XSS nel campo '%s'"
MONTHS_OF_THE_YEAR:
- 'Gennaio'
- 'Febbraio'
@@ -82,6 +126,8 @@ GRAV:
- 'Venerdì'
- 'Sabato'
- 'Domenica'
YES: "Sì"
NO: "No"
CRON:
EVERY: ogni
EVERY_HOUR: ogni ora
+17 -2
View File
@@ -1,11 +1,22 @@
---
GRAV:
INFLECTOR_UNCOUNTABLE:
- 'equipment'
- '情報'
- 'rice'
- 'お金'
- 'species'
- 'series'
- '魚'
- 'ヒツジ'
INFLECTOR_IRREGULAR:
'person': 'みんな'
'man': '人'
'child': '子供'
'sex': '性別'
'move': '移動'
INFLECTOR_ORDINALS:
'first': '番目'
NICETIME:
NO_DATE_PROVIDED: 日付が設定されていません
BAD_DATE: 不正な日付
@@ -40,8 +51,8 @@ GRAV:
YR_PLURAL:
DEC_PLURAL: 10年
FORM:
VALIDATION_FAIL: <b>バリデーション失敗 :</b>
INVALID_INPUT: 不正な入力:
VALIDATION_FAIL: '<b>バリデーション失敗 :</b>'
INVALID_INPUT: '不正な入力:'
MISSING_REQUIRED_FIELD: '必須項目が入力されていません:'
MONTHS_OF_THE_YEAR:
- '1月'
@@ -64,3 +75,7 @@ GRAV:
- '金'
- '土'
- '日'
CRON:
EVERY:
EVERY_MONTH: 毎月
ERROR1: 共有タイプ %s はサポートされていません
+2 -2
View File
@@ -37,8 +37,8 @@ GRAV:
YR_PLURAL:
DEC_PLURAL: 년간
FORM:
VALIDATION_FAIL: <b>유효성 검사 실패:</b>
INVALID_INPUT: 잘못된 입력
VALIDATION_FAIL: '<b>유효성 검사 실패:</b>'
INVALID_INPUT: '잘못된 입력'
MISSING_REQUIRED_FIELD: '누락 된 필수 필드:'
MONTHS_OF_THE_YEAR:
- '일월'
+2 -2
View File
@@ -52,8 +52,8 @@ GRAV:
YR_PLURAL: m.
DEC_PLURAL: dešimtmečiai
FORM:
VALIDATION_FAIL: <b>Patvirtinimas nepavyko:</b>
INVALID_INPUT: Neteisingai įvesta į
VALIDATION_FAIL: '<b>Patvirtinimas nepavyko:</b>'
INVALID_INPUT: 'Neteisingai įvesta į'
MISSING_REQUIRED_FIELD: 'Būtina užpildyti laukelį:'
MONTHS_OF_THE_YEAR:
- 'Sausis'
+2 -2
View File
@@ -101,8 +101,8 @@ GRAV:
YR_PLURAL: jaren
DEC_PLURAL: decennia
FORM:
VALIDATION_FAIL: <b>Validatie mislukt:</b>
INVALID_INPUT: Ongeldige invoer in
VALIDATION_FAIL: '<b>Validatie mislukt:</b>'
INVALID_INPUT: 'Ongeldige invoer in'
MISSING_REQUIRED_FIELD: 'Ontbrekend verplicht veld:'
MONTHS_OF_THE_YEAR:
- 'Januari'
+7 -2
View File
@@ -21,6 +21,7 @@ GRAV:
BAD_DATE: Ugyldig dato
AGO: siden
FROM_NOW: fra nå
JUST_NOW: akkurat nå
SECOND: sekund
MINUTE: minutt
HOUR: time
@@ -51,8 +52,8 @@ GRAV:
YR_PLURAL: år
DEC_PLURAL: årtier
FORM:
VALIDATION_FAIL: <b>Godkjenning mislyktes:</b>
INVALID_INPUT: Ugyldig innhold i
VALIDATION_FAIL: '<b>Godkjenning mislyktes:</b>'
INVALID_INPUT: 'Ugyldig innhold i'
MISSING_REQUIRED_FIELD: 'Mangler påkrevd felt:'
MONTHS_OF_THE_YEAR:
- 'januar'
@@ -75,3 +76,7 @@ GRAV:
- 'fredag'
- 'lørdag'
- 'søndag'
CRON:
EVERY: hver
EVERY_HOUR: hver time
EVERY_MINUTE: hvert minutt
+40 -2
View File
@@ -1,11 +1,32 @@
---
GRAV:
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# Error: Nieprawidłowy Frontmatter\n\nPath: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
INFLECTOR_SINGULAR:
'/(alias|status)es$/i': '\1'
INFLECTOR_UNCOUNTABLE:
- 'wyposażenie'
- 'informacja'
- 'rice'
- 'pieniądze'
- 'species'
- 'series'
- 'ryba'
- 'owca'
INFLECTOR_IRREGULAR:
'person': 'człowiek'
'man': 'mężczyźni'
'child': 'dzieci'
'sex': 'płci'
INFLECTOR_ORDINALS:
'first': 'pierwszy'
'second': 'drugi'
'third': 'trzeci'
NICETIME:
NO_DATE_PROVIDED: Nie podano daty
BAD_DATE: Zła data
AGO: temu
FROM_NOW: od teraz
JUST_NOW: właśnie teraz
SECOND: sekunda
MINUTE: minuta
HOUR: godzina
@@ -15,6 +36,7 @@ GRAV:
YEAR: rok
DECADE: dekada
SEC: sek
MIN: minuta
HR: godz
WK: tydz
MO: m-c
@@ -36,9 +58,10 @@ GRAV:
YR_PLURAL: lat
DEC_PLURAL: dekad
FORM:
VALIDATION_FAIL: <b>Weryfikacja nie powiodła się:</b>
INVALID_INPUT: Nieprawidłowe dane wejściowe
VALIDATION_FAIL: '<b>Weryfikacja nie powiodła się:</b>'
INVALID_INPUT: 'Nieprawidłowe dane wejściowe'
MISSING_REQUIRED_FIELD: 'Opuszczono wymagane pole:'
XSS_ISSUES: "Potencjalne problemy XSS wykryte w polu '%s'"
MONTHS_OF_THE_YEAR:
- 'Styczeń'
- 'Luty'
@@ -60,3 +83,18 @@ GRAV:
- 'Piątek'
- 'Sobota'
- 'Niedziela'
YES: "Tak"
NO: "Nie"
CRON:
EVERY: każdy
EVERY_HOUR: każdą godzinę
EVERY_MINUTE: każdą minutę
EVERY_DAY_OF_WEEK: każdego dnia tygodnia
EVERY_DAY_OF_MONTH: każdego dnia miesiące
EVERY_MONTH: każdego miesiąca
TEXT_PERIOD: Każdego <b />
TEXT_MINS: 'o <b /> minut po godzinie'
TEXT_TIME: 'o <b />:<b />'
ERROR1: Znacznik %s nie jest wspierany!
ERROR2: Nieprawidłowa liczba elementów
ERROR4: Wyrażenie nierozpoznane
+112 -14
View File
@@ -1,8 +1,75 @@
---
GRAV:
FRONTMATTER_ERROR_PAGE: "---\ntitle: %1$s\n---\n\n# Erro: Frontmatter Inválido\n\nLocalização: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
INFLECTOR_PLURALS:
'/(quiz)$/i': '\1zes'
'/^(ox)$/i': '\1en'
'/([m|l])ouse$/i': '\1ice'
'/(matr|vert|ind)ix|ex$/i': '\1ices'
'/(x|ch|ss|sh)$/i': '\1es'
'/([^aeiouy]|qu)ies$/i': '\1y'
'/([^aeiouy]|qu)y$/i': '\1ies'
'/(hive)$/i': '\1s'
'/(?:([^f])fe|([lr])f)$/i': '\1\2ves'
'/sis$/i': 'ses'
'/([ti])um$/i': '\1a'
'/(buffal|tomat)o$/i': '\1oes'
'/(bu)s$/i': '\1ses'
'/(alias|status)/i': '\1es'
'/(octop|vir)us$/i': '\1i'
'/(ax|test)is$/i': '\1es'
'/s$/i': 's'
'/$/': 's'
INFLECTOR_SINGULAR:
'/(quiz)zes$/i': '\1'
'/(matr)ices$/i': '\1ix'
'/(vert|ind)ices$/i': '\1ex'
'/^(ox)en/i': '\1'
'/(alias|status)es$/i': '\1'
'/([octop|vir])i$/i': '\1us'
'/(cris|ax|test)es$/i': '\1is'
'/(shoe)s$/i': '\1'
'/(o)es$/i': '\1'
'/(bus)es$/i': '\1'
'/([m|l])ice$/i': '\1ouse'
'/(x|ch|ss|sh)es$/i': '\1'
'/(m)ovies$/i': '\1ovie'
'/(s)eries$/i': '\1eries'
'/([^aeiouy]|qu)ies$/i': '\1y'
'/([lr])ves$/i': '\1f'
'/(tive)s$/i': '\1'
'/(hive)s$/i': '\1'
'/([^f])ves$/i': '\1fe'
'/(^analy)ses$/i': '\1sis'
'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i': '\1\2sis'
'/([ti])a$/i': '\1um'
'/(n)ews$/i': '\1ews'
INFLECTOR_UNCOUNTABLE:
- 'equipamento'
- 'informação'
- 'arroz'
- 'dinheiro'
- 'espécie'
- 'série'
- 'peixe'
- 'ovelha'
INFLECTOR_IRREGULAR:
'person': 'pessoas'
'man': 'homens'
'child': 'crianças'
'sex': 'sexos'
'move': 'movimentos'
INFLECTOR_ORDINALS:
'default': 'º'
'first': 'º'
'second': 'º'
'third': 'º'
NICETIME:
NO_DATE_PROVIDED: Nenhuma data fornecida
BAD_DATE: Data inválida
AGO: atrás
FROM_NOW: a partir de agora
JUST_NOW: mesmo agora
SECOND: segundo
MINUTE: minuto
HOUR: hora
@@ -11,17 +78,32 @@ GRAV:
MONTH: mês
YEAR: ano
DECADE: década
SEC: segundos
MIN: minutos
SEC: seg
MIN: min
HR: hora
WK: semana
MO: mês
YR: ano
DEC: década
SECOND_PLURAL: segundos
MINUTE_PLURAL: minutos
HOUR_PLURAL: horas
DAY_PLURAL: dias
WEEK_PLURAL: semanas
MONTH_PLURAL: meses
YEAR_PLURAL: anos
DECADE_PLURAL: decadas
DECADE_PLURAL: décadas
SEC_PLURAL: segs
MIN_PLURAL: mins
HR_PLURAL: hrs
WK_PLURAL: sems
MO_PLURAL: meses
YR_PLURAL: anos
DEC_PLURAL: décadas
FORM:
VALIDATION_FAIL: <b>Falha na validação!</b>
MISSING_REQUIRED_FIELD: 'Campo obrigatório requerido:'
VALIDATION_FAIL: '<b>Falha na validação:</b>'
INVALID_INPUT: 'Dados inseridos são inválidos em'
MISSING_REQUIRED_FIELD: 'Campo obrigatório em falta:'
MONTHS_OF_THE_YEAR:
- 'Janeiro'
- 'Fevereiro'
@@ -35,12 +117,28 @@ GRAV:
- 'Outubro'
- 'Novembro'
- 'Dezembro'
INFLECTOR_UNCOUNTABLE:
- 'equipment'
- 'information'
- 'arroz'
- 'money'
- 'species'
- 'series'
- 'fish'
- 'sheep'
DAYS_OF_THE_WEEK:
- 'Segunda-feira'
- 'Terça-feira'
- 'Quarta-feira'
- 'Quinta-feira'
- 'Sexta-feira'
- 'Sábado'
- 'Domingo'
CRON:
EVERY: cada
EVERY_HOUR: cada hora
EVERY_MINUTE: cada minuto
EVERY_DAY_OF_WEEK: todos os dias da semana
EVERY_DAY_OF_MONTH: todos os dias do mês
EVERY_MONTH: todos os meses
TEXT_PERIOD: Cada <b />
TEXT_MINS: ' em <b /> minuto(s) após a hora'
TEXT_TIME: ' em <b />:<b />'
TEXT_DOW: ' em <b />'
TEXT_MONTH: ' de <b />'
TEXT_DOM: ' em <b />'
ERROR1: A tag %s não é suportada!
ERROR2: Número de elementos inválido
ERROR3: O jquery_element deve ser definido nas configurações do jqCron
ERROR4: Expressão não reconhecida
+2 -2
View File
@@ -53,8 +53,8 @@ GRAV:
YR_PLURAL: ani
DEC_PLURAL: decenii
FORM:
VALIDATION_FAIL: <b>Validare nereușită</b>
INVALID_INPUT: Date incorecte în
VALIDATION_FAIL: '<b>Validare nereușită</b>'
INVALID_INPUT: 'Date incorecte în'
MISSING_REQUIRED_FIELD: 'Câmp obligatoriu lipsă:'
MONTHS_OF_THE_YEAR:
- 'Ianuarie'
+12 -9
View File
@@ -13,7 +13,7 @@ GRAV:
INFLECTOR_IRREGULAR:
'person': 'люди'
'man': 'человек'
'child': 'ребенок'
'child': 'дети'
'sex': 'пол'
'move': 'движется'
INFLECTOR_ORDINALS:
@@ -58,9 +58,10 @@ GRAV:
YR_PLURAL: г
DEC_PLURAL: дстлт
FORM:
VALIDATION_FAIL: <b>Проверка не удалась:</b>
INVALID_INPUT: Неверный ввод в
VALIDATION_FAIL: '<b>Проверка не удалась:</b>'
INVALID_INPUT: 'Неверный ввод в'
MISSING_REQUIRED_FIELD: 'Отсутствует необходимое поле:'
XSS_ISSUES: "Обнаружены потенциальные XSS проблемы в поле '%s'"
MONTHS_OF_THE_YEAR:
- 'январь'
- 'февраль'
@@ -68,12 +69,12 @@ GRAV:
- 'апрель'
- 'май'
- 'июнь'
- 'Июль'
- 'Август'
- 'Сентябрь'
- 'Октябрь'
- 'Ноябрь'
- 'Декабрь'
- 'июль'
- 'август'
- 'сентябрь'
- 'октябрь'
- 'ноябрь'
- 'декабрь'
DAYS_OF_THE_WEEK:
- 'понедельник'
- 'вторник'
@@ -82,6 +83,8 @@ GRAV:
- 'пятница'
- 'суббота'
- 'воскресенье'
YES: "Да"
NO: "Нет"
CRON:
EVERY: раз в
EVERY_HOUR: раз в час
+2 -2
View File
@@ -101,8 +101,8 @@ GRAV:
YR_PLURAL: rokov
DEC_PLURAL: dekád
FORM:
VALIDATION_FAIL: <b>Overenie zlyhalo:</b>
INVALID_INPUT: Neplatný vstup v
VALIDATION_FAIL: '<b>Overenie zlyhalo:</b>'
INVALID_INPUT: 'Neplatný vstup v'
MISSING_REQUIRED_FIELD: 'Chýba vyžadované pole:'
MONTHS_OF_THE_YEAR:
- 'Január'
+2 -2
View File
@@ -36,8 +36,8 @@ GRAV:
YR_PLURAL: l
DEC_PLURAL: des
FORM:
VALIDATION_FAIL: <b>Preverjanje veljavnosti ni uspelo:</b>
INVALID_INPUT: Neveljaven vnos v
VALIDATION_FAIL: '<b>Preverjanje veljavnosti ni uspelo:</b>'
INVALID_INPUT: 'Neveljaven vnos v'
MISSING_REQUIRED_FIELD: 'Manjka obvezno polje:'
MONTHS_OF_THE_YEAR:
- 'Januar'
+144
View File
@@ -0,0 +1,144 @@
---
GRAV:
FRONTMATTER_ERROR_PAGE: "---\nнаслов: %1$s\n---\n\n# Грешка: неисправан Frontmatter\n\nПутања: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
INFLECTOR_PLURALS:
'/(quiz)$/i': '\1zes'
'/^(ox)$/i': '\1en'
'/([m|l])ouse$/i': '\1ice'
'/(matr|vert|ind)ix|ex$/i': '\1ices'
'/(x|ch|ss|sh)$/i': '\1es'
'/([^aeiouy]|qu)ies$/i': '\1y'
'/([^aeiouy]|qu)y$/i': '\1ies'
'/(hive)$/i': '\1s'
'/(?:([^f])fe|([lr])f)$/i': '\1\2ves'
'/sis$/i': 'ses'
'/([ti])um$/i': '\1a'
'/(buffal|tomat)o$/i': '\1oes'
'/(bu)s$/i': '\1ses'
'/(alias|status)/i': '\1es'
'/(octop|vir)us$/i': '\1i'
'/(ax|test)is$/i': '\1es'
'/s$/i': 's'
'/$/': 's'
INFLECTOR_SINGULAR:
'/(quiz)zes$/i': '\1'
'/(matr)ices$/i': '\1ix'
'/(vert|ind)ices$/i': '\1ex'
'/^(ox)en/i': '\1'
'/(alias|status)es$/i': '\1'
'/([octop|vir])i$/i': '\1us'
'/(cris|ax|test)es$/i': '\1is'
'/(shoe)s$/i': '\1'
'/(o)es$/i': '\1'
'/(bus)es$/i': '\1'
'/([m|l])ice$/i': '\1ouse'
'/(x|ch|ss|sh)es$/i': '\1'
'/(m)ovies$/i': '\1ovie'
'/(s)eries$/i': '\1eries'
'/([^aeiouy]|qu)ies$/i': '\1y'
'/([lr])ves$/i': '\1f'
'/(tive)s$/i': '\1'
'/(hive)s$/i': '\1'
'/([^f])ves$/i': '\1fe'
'/(^analy)ses$/i': '\1sis'
'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i': '\1\2sis'
'/([ti])a$/i': '\1um'
'/(n)ews$/i': '\1ews'
INFLECTOR_UNCOUNTABLE:
- 'опрема'
- 'информација'
- 'пиринач'
- 'новац'
- 'врсте'
- 'серије'
- 'риба'
- 'овца'
INFLECTOR_IRREGULAR:
'person': 'особе'
'man': 'људи'
'child': 'деца'
'sex': 'полови'
'move': 'помери'
INFLECTOR_ORDINALS:
'default': 'ти'
'first': 'први'
'second': 'други'
'third': 'трећи'
NICETIME:
NO_DATE_PROVIDED: Нема датума
BAD_DATE: Погрешан датум
AGO: од пре
FROM_NOW: од сада
JUST_NOW: управо сада
SECOND: секунда
MINUTE: минута
HOUR: сат
DAY: дан
WEEK: недеља
MONTH: месец
YEAR: година
DECADE: декада
SEC: сек
MIN: мин
HR: сат
WK: нед
MO: мес
YR: год
DEC: дек
SECOND_PLURAL: секунди
MINUTE_PLURAL: минута
HOUR_PLURAL: сати
DAY_PLURAL: дана
WEEK_PLURAL: недеља
MONTH_PLURAL: месеци
YEAR_PLURAL: године(а)
DECADE_PLURAL: декаде(а)
SEC_PLURAL: сек
MIN_PLURAL: мин
HR_PLURAL: сати
WK_PLURAL: недеља
MO_PLURAL: месеци
YR_PLURAL: година
DEC_PLURAL: декада
FORM:
VALIDATION_FAIL: '<b>Провера неуспела:</b>'
INVALID_INPUT: 'Неисправан унос у'
MISSING_REQUIRED_FIELD: 'Недостаје обавезн поље:'
MONTHS_OF_THE_YEAR:
- 'Јануар'
- 'Фебруар'
- 'Март'
- 'Април'
- 'Мај'
- 'Јуни'
- 'Јули'
- 'Август'
- 'Септембар'
- 'Октобар'
- 'Новембар'
- 'Децембар'
DAYS_OF_THE_WEEK:
- 'Понедељак'
- 'Уторак'
- 'Среда'
- 'Четвртак'
- 'Петак'
- 'Субота'
- 'Недеља'
CRON:
EVERY: сваки
EVERY_HOUR: сваки сат
EVERY_MINUTE: сваки минут
EVERY_DAY_OF_WEEK: сваки дан у недељи
EVERY_DAY_OF_MONTH: сваки дан у месецу
EVERY_MONTH: сваки месец
TEXT_PERIOD: Сваки <b />
TEXT_MINS: ' у <b /> минути(а) прошлог сата'
TEXT_TIME: ' у <b />:<b />'
TEXT_DOW: ' на <b />'
TEXT_MONTH: ' од <b />'
TEXT_DOM: ' на <b />'
ERROR1: Таг %s није подржан!
ERROR2: Погрешан број елемената
ERROR3: јquery_element би требао да буде постављен у jqCron подешавању
ERROR4: Непрепознат израз
+30 -2
View File
@@ -12,11 +12,21 @@ GRAV:
- 'får'
INFLECTOR_IRREGULAR:
'person': 'personer'
'man': 'män'
'child': 'barn'
'sex': 'kön'
'move': 'flytta'
INFLECTOR_ORDINALS:
'default': ':e'
'first': ':a'
'second': ':a'
'third': ':e'
NICETIME:
NO_DATE_PROVIDED: Inget datum har angivits
BAD_DATE: Ogiltigt datum
AGO: sedan
FROM_NOW: fr.o.m nu
JUST_NOW: just nu
SECOND: sekund
MINUTE: minut
HOUR: timme
@@ -26,10 +36,12 @@ GRAV:
YEAR: år
DECADE: årtionde
SEC: sek
MIN: min
HR: t
WK: v
MO: m
YR: år
DEC: dec
SECOND_PLURAL: sekunder
MINUTE_PLURAL: minuter
HOUR_PLURAL: timmar
@@ -46,8 +58,8 @@ GRAV:
YR_PLURAL: år
DEC_PLURAL: dec
FORM:
VALIDATION_FAIL: <b>Kontrollen misslyckades:</b>
INVALID_INPUT: Ogiltig indata i
VALIDATION_FAIL: '<b>Kontrollen misslyckades:</b>'
INVALID_INPUT: 'Ogiltig indata i'
MISSING_REQUIRED_FIELD: 'Obligatoriskt fält måste fyllas i:'
MONTHS_OF_THE_YEAR:
- 'Januari'
@@ -70,3 +82,19 @@ GRAV:
- 'Fredag'
- 'Lördag'
- 'Söndag'
CRON:
EVERY: varje
EVERY_HOUR: varje timme
EVERY_MINUTE: varje minut
EVERY_DAY_OF_WEEK: varje veckodag
EVERY_DAY_OF_MONTH: alla månadens dagar
EVERY_MONTH: varje månad
TEXT_PERIOD: Varje <b />
TEXT_MINS: ' timmens <b />:e minut'
TEXT_TIME: ' kl <b />:<b />'
TEXT_DOW: ' <b />'
TEXT_MONTH: ' <b />'
TEXT_DOM: ' <b />'
ERROR1: Taggen %s stöds inte!
ERROR2: Ogiltigt antal element
ERROR4: Uttrycket känns inte igen
+1 -1
View File
@@ -31,7 +31,7 @@ GRAV:
YR_PLURAL: ปี
FORM:
VALIDATION_FAIL: '<b>ตรวจสอบล้มเหลว: </b>'
INVALID_INPUT: ป้อนข้อมูลไม่ถูกต้องใน
INVALID_INPUT: 'ป้อนข้อมูลไม่ถูกต้องใน'
MISSING_REQUIRED_FIELD: 'ขาดข้อมูลที่จำเป็น:'
MONTHS_OF_THE_YEAR:
- 'มกราคม'
+37 -2
View File
@@ -1,11 +1,32 @@
---
GRAV:
FRONTMATTER_ERROR_PAGE: "---\nBaşlık: %1$s\n---\n\n# Hata: Geçersiz Önbölüm\n\nYol: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
INFLECTOR_UNCOUNTABLE:
- 'ekipman'
- 'bilgi'
- 'pirinç'
- 'para'
- 'türler'
- 'seriler'
- 'balık'
- 'koyun'
INFLECTOR_IRREGULAR:
'person': 'kişi'
'man': 'erkek'
'child': 'çocuklar'
'sex': 'cinsiyet'
'move': 'taşınmış'
INFLECTOR_ORDINALS:
'default': '#F'
'first': ' 1.'
'second': ' 2.'
'third': ' 3.'
NICETIME:
NO_DATE_PROVIDED: Sağlanan tarih yok
BAD_DATE: Yanlış tarih
AGO: önce
FROM_NOW: şu andan itibaren
JUST_NOW: şimdi
SECOND: saniye
MINUTE: dakika
HOUR: saat
@@ -37,8 +58,8 @@ GRAV:
YR_PLURAL: yıl
DEC_PLURAL: onyl
FORM:
VALIDATION_FAIL: <b>Doğrulama başarısız:</b>
INVALID_INPUT: Geçersiz bilgi girişi
VALIDATION_FAIL: '<b>Doğrulama başarısız:</b>'
INVALID_INPUT: 'Geçersiz bilgi girişi'
MISSING_REQUIRED_FIELD: 'Gerekli alan eksik:'
MONTHS_OF_THE_YEAR:
- 'Ocak'
@@ -61,3 +82,17 @@ GRAV:
- 'Cuma'
- 'Cumartesi'
- 'Pazar'
CRON:
EVERY: her
EVERY_HOUR: saatte bir
EVERY_MINUTE: dakikada bir
EVERY_DAY_OF_WEEK: haftanın her günü
EVERY_DAY_OF_MONTH: ayın her günü
EVERY_MONTH: her ay
TEXT_PERIOD: Her <b />
TEXT_MINS: ' saatin <b /> dakikasında'
TEXT_TIME: ' da'
ERROR1: Etiket %s desteklenmiyor!
ERROR2: Kötü eleman sayısı
ERROR3: jquery_element jqCron ayarları içinde tanımlanmalı
ERROR4: Tanınmayan ifade
+2 -2
View File
@@ -37,8 +37,8 @@ GRAV:
YR_PLURAL: рр.
DEC_PLURAL: рр.
FORM:
VALIDATION_FAIL: <b>Перевірка не вдалася:</b>
INVALID_INPUT: Невірне введення в
VALIDATION_FAIL: '<b>Перевірка не вдалася:</b>'
INVALID_INPUT: 'Невірне введення в'
MISSING_REQUIRED_FIELD: 'Відсутнє обов''язкове поле:'
MONTHS_OF_THE_YEAR:
- 'Січень'
+2 -2
View File
@@ -37,8 +37,8 @@ GRAV:
YR_PLURAL: năm
DEC_PLURAL: thập kỷ
FORM:
VALIDATION_FAIL: <b>Xác nhận thất bại:</b>
INVALID_INPUT: Dữ liệu nhập không hợp lệ cho
VALIDATION_FAIL: '<b>Xác nhận thất bại:</b>'
INVALID_INPUT: 'Dữ liệu nhập không hợp lệ cho'
MISSING_REQUIRED_FIELD: 'Thiếu trường bắt buộc:'
MONTHS_OF_THE_YEAR:
- 'Tháng 1'
+144
View File
@@ -0,0 +1,144 @@
---
GRAV:
FRONTMATTER_ERROR_PAGE: "---\n标题: %1$s\n---\n\n# 错误:无效参数\n\n位置: `%2$s`\n\n**%3$s**\n\n```\n%4$s\n```"
INFLECTOR_PLURALS:
'/(quiz)$/i': '\1zes'
'/^(ox)$/i': '\1en'
'/([m|l])ouse$/i': '\1ice'
'/(matr|vert|ind)ix|ex$/i': '\1ices'
'/(x|ch|ss|sh)$/i': '\1es'
'/([^aeiouy]|qu)ies$/i': '\1y'
'/([^aeiouy]|qu)y$/i': '\1ies'
'/(hive)$/i': '\1s'
'/(?:([^f])fe|([lr])f)$/i': '\1\2ves'
'/sis$/i': 'ses'
'/([ti])um$/i': '\1a'
'/(buffal|tomat)o$/i': '\1oes'
'/(bu)s$/i': '\1ses'
'/(alias|status)/i': '\1es'
'/(octop|vir)us$/i': '\1i'
'/(ax|test)is$/i': '\1es'
'/s$/i': 's'
'/$/': 's'
INFLECTOR_SINGULAR:
'/(quiz)zes$/i': '\1'
'/(matr)ices$/i': '\1ix'
'/(vert|ind)ices$/i': '\1ex'
'/^(ox)en/i': '\1'
'/(alias|status)es$/i': '\1'
'/([octop|vir])i$/i': '\1us'
'/(cris|ax|test)es$/i': '\1is'
'/(shoe)s$/i': '\1'
'/(o)es$/i': '\1'
'/(bus)es$/i': '\1'
'/([m|l])ice$/i': '\1ouse'
'/(x|ch|ss|sh)es$/i': '\1'
'/(m)ovies$/i': '\1ovie'
'/(s)eries$/i': '\1eries'
'/([^aeiouy]|qu)ies$/i': '\1y'
'/([lr])ves$/i': '\1f'
'/(tive)s$/i': '\1'
'/(hive)s$/i': '\1'
'/([^f])ves$/i': '\1fe'
'/(^analy)ses$/i': '\1sis'
'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i': '\1\2sis'
'/([ti])a$/i': '\1um'
'/(n)ews$/i': '\1ews'
INFLECTOR_UNCOUNTABLE:
- '装备'
- '信息'
- '大米'
- '钱'
- '物种'
- '系列'
- '鱼'
- '羊'
INFLECTOR_IRREGULAR:
'person': '人员'
'man': '男人'
'child': '儿童'
'sex': '性别'
'move': '移动'
INFLECTOR_ORDINALS:
'default': 'th'
'first': 'st'
'second': 'md'
'third': 'rd'
NICETIME:
NO_DATE_PROVIDED: 无日期信息
BAD_DATE: 无效日期
AGO:
FROM_NOW: 距今
JUST_NOW: 刚刚
SECOND:
MINUTE: 分钟
HOUR: 小时
DAY:
WEEK:
MONTH:
YEAR:
DECADE: 十年
SEC:
MIN: 分钟
HR: 小时
WK:
MO:
YR:
DEC: 年代
SECOND_PLURAL:
MINUTE_PLURAL:
HOUR_PLURAL: 小时
DAY_PLURAL:
WEEK_PLURAL:
MONTH_PLURAL:
YEAR_PLURAL:
DECADE_PLURAL: 十年
SEC_PLURAL:
MIN_PLURAL:
HR_PLURAL:
WK_PLURAL:
MO_PLURAL:
YR_PLURAL:
DEC_PLURAL: 年代
FORM:
VALIDATION_FAIL: '<b>验证失败:</b>'
INVALID_INPUT: '无效输入'
MISSING_REQUIRED_FIELD: '必填字段缺失:'
MONTHS_OF_THE_YEAR:
- '1月'
- '2月'
- '3月'
- '4月'
- '5月'
- '6月'
- '7月'
- '8月'
- '9月'
- '10月'
- '11月'
- '12月'
DAYS_OF_THE_WEEK:
- '星期一'
- '星期二'
- '星期三'
- '星期四'
- '星期五'
- '星期六'
- '星期日'
CRON:
EVERY: 每隔
EVERY_HOUR: 每小时
EVERY_MINUTE: 每分钟
EVERY_DAY_OF_WEEK: 一周中的每一天
EVERY_DAY_OF_MONTH: 月份中的每一天
EVERY_MONTH: 每月
TEXT_PERIOD: 所有 <b />
TEXT_MINS: ' 在 <b /> 小时过后的分钟'
TEXT_TIME: ' 在 <b />:<b />'
TEXT_DOW: ' on <b />'
TEXT_MONTH: ' of <b />'
TEXT_DOM: ' on <b />'
ERROR1: 不支持分享类型 %s
ERROR2: 无效数字
ERROR3: 请在 jqCron 设置中设定 jquery_element
ERROR4: 无法识别表达式
+3 -3
View File
@@ -101,9 +101,9 @@ GRAV:
YR_PLURAL:
DEC_PLURAL: 年代
FORM:
VALIDATION_FAIL: <b>验证失败:</b>
INVALID_INPUT: 无效输入
MISSING_REQUIRED_FIELD: 必填字段缺失:
VALIDATION_FAIL: '<b>验证失败:</b>'
INVALID_INPUT: '无效输入'
MISSING_REQUIRED_FIELD: '必填字段缺失:'
MONTHS_OF_THE_YEAR:
- '1月'
- '2月'
+1
View File
@@ -2,4 +2,5 @@
title: Not Found
routable: false
notfound: true
expires: 0
---
+2 -1
View File
@@ -3,7 +3,7 @@
/**
* @package Grav\Core
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@@ -20,6 +20,7 @@ if (is_file($_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . $_SERVER['SCRIPT_N
$grav_index = 'index.php';
/* Check the GRAV_BASEDIR environment variable and use if set */
$grav_basedir = getenv('GRAV_BASEDIR') ?: '';
if ($grav_basedir) {
$grav_index = ltrim($grav_basedir, '/') . DIRECTORY_SEPARATOR . $grav_index;
+106 -44
View File
@@ -3,30 +3,41 @@
/**
* @package Grav\Common
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common;
use Closure;
use Grav\Common\Assets\Pipeline;
use Grav\Common\Assets\Traits\LegacyAssetsTrait;
use Grav\Common\Assets\Traits\TestingAssetsTrait;
use Grav\Common\Config\Config;
use Grav\Framework\Object\PropertyObject;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use function call_user_func_array;
use function count;
use function func_get_args;
use function is_array;
/**
* Class Assets
* @package Grav\Common
*/
class Assets extends PropertyObject
{
use TestingAssetsTrait;
use LegacyAssetsTrait;
const CSS = 'css';
const JS = 'js';
const CSS_COLLECTION = 'assets_css';
const JS_COLLECTION = 'assets_js';
const CSS_TYPE = 'Css';
const JS_TYPE = 'Js';
const INLINE_CSS_TYPE = 'InlineCss';
const INLINE_JS_TYPE = 'InlineJs';
const CSS_TYPE = Assets\Css::class;
const JS_TYPE = Assets\Js::class;
const INLINE_CSS_TYPE = Assets\InlineCss::class;
const INLINE_JS_TYPE = Assets\InlineJs::class;
/** @const Regex to match CSS and JavaScript files */
const DEFAULT_REGEX = '/.\.(css|js)$/i';
@@ -37,31 +48,57 @@ class Assets extends PropertyObject
/** @const Regex to match JavaScript files */
const JS_REGEX = '/.\.js$/i';
/** @var string */
protected $assets_dir;
/** @var string */
protected $assets_url;
/** @var array */
protected $assets_css = [];
/** @var array */
protected $assets_js = [];
// Config Options
// Following variables come from the configuration:
/** @var bool */
protected $css_pipeline;
/** @var bool */
protected $css_pipeline_include_externals;
/** @var bool */
protected $css_pipeline_before_excludes;
/** @var bool */
protected $inlinecss_pipeline_include_externals;
/** @var bool */
protected $inlinecss_pipeline_before_excludes;
/** @var bool */
protected $js_pipeline;
/** @var bool */
protected $js_pipeline_include_externals;
/** @var bool */
protected $js_pipeline_before_excludes;
/** @var bool */
protected $inlinejs_pipeline_include_externals;
/** @var bool */
protected $inlinejs_pipeline_before_excludes;
/** @var array */
protected $pipeline_options = [];
/** @var Closure|string */
protected $fetch_command;
/** @var string */
protected $autoload;
/** @var bool */
protected $enable_asset_timestamp;
/** @var array|null */
protected $collections;
/** @var string */
protected $timestamp;
/** @var array Keeping track for order counts (for sorting) */
protected $order = [];
/**
* Initialization called in the Grav lifecycle to initialize the Assets with appropriate configuration
*
* @return void
*/
public function init()
{
@@ -92,7 +129,6 @@ class Assets extends PropertyObject
* assets and/or collections that will be automatically added on startup.
*
* @param array $config Configurable options.
*
* @return $this
*/
public function config(array $config)
@@ -119,35 +155,35 @@ class Assets extends PropertyObject
* It automatically detects the asset type (JavaScript, CSS or collection).
* You may add more than one asset passing an array as argument.
*
* @param array|string $asset
* @param string|string[] $asset
* @return $this
*/
public function add($asset)
{
$args = \func_get_args();
$args = func_get_args();
// More than one asset
if (\is_array($asset)) {
if (is_array($asset)) {
foreach ($asset as $a) {
array_shift($args);
$args = array_merge([$a], $args);
\call_user_func_array([$this, 'add'], $args);
call_user_func_array([$this, 'add'], $args);
}
} elseif (isset($this->collections[$asset])) {
array_shift($args);
$args = array_merge([$this->collections[$asset]], $args);
\call_user_func_array([$this, 'add'], $args);
call_user_func_array([$this, 'add'], $args);
} else {
// Get extension
$extension = pathinfo(parse_url($asset, PHP_URL_PATH), PATHINFO_EXTENSION);
// JavaScript or CSS
if (\strlen($extension) > 0) {
if ($extension !== '') {
$extension = strtolower($extension);
if ($extension === 'css') {
\call_user_func_array([$this, 'addCss'], $args);
call_user_func_array([$this, 'addCss'], $args);
} elseif ($extension === 'js') {
\call_user_func_array([$this, 'addJs'], $args);
call_user_func_array([$this, 'addJs'], $args);
}
}
}
@@ -155,12 +191,20 @@ class Assets extends PropertyObject
return $this;
}
/**
* @param string $collection
* @param string $type
* @param string|string[] $asset
* @param array $options
* @return $this
*/
protected function addType($collection, $type, $asset, $options)
{
if (\is_array($asset)) {
if (is_array($asset)) {
foreach ($asset as $a) {
$this->addType($collection, $type, $a, $options);
}
return $this;
}
@@ -172,7 +216,7 @@ class Assets extends PropertyObject
// If pipeline disabled, set to position if provided, else after
if (isset($options['pipeline'])) {
if ($options['pipeline'] === false) {
$exclude_type = ($type === $this::JS_TYPE || $type === $this::INLINE_JS_TYPE) ? $this::JS_TYPE : $this::CSS_TYPE;
$exclude_type = ($type === $this::JS_TYPE || $type === $this::INLINE_JS_TYPE) ? $this::JS : $this::CSS;
$excludes = strtolower($exclude_type . '_pipeline_before_excludes');
if ($this->{$excludes}) {
$default = 'after';
@@ -190,11 +234,18 @@ class Assets extends PropertyObject
$options['timestamp'] = $this->timestamp;
// Set order
$options['order'] = \count($this->$collection);
$group = $options['group'] ?? 'head';
$position = $options['position'] ?? 'pipeline';
$orderKey = "{$type}|{$group}|{$position}";
if (!isset($this->order[$orderKey])) {
$this->order[$orderKey] = 0;
}
$options['order'] = $this->order[$orderKey]++;
// Create asset of correct type
$asset_class = "\\Grav\\Common\\Assets\\{$type}";
$asset_object = new $asset_class();
$asset_object = new $type();
// If exists
if ($asset_object->init($asset, $options)) {
@@ -202,7 +253,6 @@ class Assets extends PropertyObject
}
return $this;
}
/**
@@ -212,7 +262,7 @@ class Assets extends PropertyObject
*/
public function addCss($asset)
{
return $this->addType(Assets::CSS_COLLECTION,Assets::CSS_TYPE, $asset, $this->unifyLegacyArguments(\func_get_args(), Assets::CSS_TYPE));
return $this->addType($this::CSS_COLLECTION, $this::CSS_TYPE, $asset, $this->unifyLegacyArguments(func_get_args(), $this::CSS_TYPE));
}
/**
@@ -222,7 +272,7 @@ class Assets extends PropertyObject
*/
public function addInlineCss($asset)
{
return $this->addType(Assets::CSS_COLLECTION, Assets::INLINE_CSS_TYPE, $asset, $this->unifyLegacyArguments(\func_get_args(), Assets::INLINE_CSS_TYPE));
return $this->addType($this::CSS_COLLECTION, $this::INLINE_CSS_TYPE, $asset, $this->unifyLegacyArguments(func_get_args(), $this::INLINE_CSS_TYPE));
}
/**
@@ -232,7 +282,7 @@ class Assets extends PropertyObject
*/
public function addJs($asset)
{
return $this->addType(Assets::JS_COLLECTION, Assets::JS_TYPE, $asset, $this->unifyLegacyArguments(\func_get_args(), Assets::JS_TYPE));
return $this->addType($this::JS_COLLECTION, $this::JS_TYPE, $asset, $this->unifyLegacyArguments(func_get_args(), $this::JS_TYPE));
}
/**
@@ -242,20 +292,19 @@ class Assets extends PropertyObject
*/
public function addInlineJs($asset)
{
return $this->addType(Assets::JS_COLLECTION, Assets::INLINE_JS_TYPE, $asset, $this->unifyLegacyArguments(\func_get_args(), Assets::INLINE_JS_TYPE));
return $this->addType($this::JS_COLLECTION, $this::INLINE_JS_TYPE, $asset, $this->unifyLegacyArguments(func_get_args(), $this::INLINE_JS_TYPE));
}
/**
* Add/replace collection.
*
* @param string $collectionName
* @param array $assets
* @param string $collectionName
* @param array $assets
* @param bool $overwrite
*
* @return $this
*/
public function registerCollection($collectionName, Array $assets, $overwrite = false)
public function registerCollection($collectionName, array $assets, $overwrite = false)
{
if ($overwrite || !isset($this->collections[$collectionName])) {
$this->collections[$collectionName] = $assets;
@@ -264,26 +313,33 @@ class Assets extends PropertyObject
return $this;
}
/**
* @param array $assets
* @param string $key
* @param string $value
* @param bool $sort
* @return array|false
*/
protected function filterAssets($assets, $key, $value, $sort = false)
{
$results = array_filter($assets, function($asset) use ($key, $value) {
$results = array_filter($assets, function ($asset) use ($key, $value) {
if ($key === 'position' && $value === 'pipeline') {
$type = $asset->getType();
if ($asset->getRemote() && $this->{$type . '_pipeline_include_externals'} === false && $asset['position'] === 'pipeline' ) {
if ($this->{$type . '_pipeline_before_excludes'}) {
if ($asset->getRemote() && $this->{strtolower($type) . '_pipeline_include_externals'} === false && $asset['position'] === 'pipeline') {
if ($this->{strtolower($type) . '_pipeline_before_excludes'}) {
$asset->setPosition('after');
} else {
$asset->setPosition('before');
}
return false;
}
}
if ($asset[$key] === $value) return true;
if ($asset[$key] === $value) {
return true;
}
return false;
});
@@ -295,17 +351,25 @@ class Assets extends PropertyObject
return $results;
}
/**
* @param array $assets
* @return array
*/
protected function sortAssets($assets)
{
uasort ($assets, function($a, $b) {
if ($a['priority'] == $b['priority']) {
return $a['order'] - $b['order'];
}
return $b['priority'] - $a['priority'];
uasort($assets, static function ($a, $b) {
return $b['priority'] <=> $a['priority'] ?: $a['order'] <=> $b['order'];
});
return $assets;
}
/**
* @param string $type
* @param string $group
* @param array $attributes
* @return string
*/
public function render($type, $group = 'head', $attributes = [])
{
$before_output = '';
@@ -352,7 +416,6 @@ class Assets extends PropertyObject
*
* @param string $group name of the group
* @param array $attributes
*
* @return string
*/
public function css($group = 'head', $attributes = [])
@@ -365,7 +428,6 @@ class Assets extends PropertyObject
*
* @param string $group name of the group
* @param array $attributes
*
* @return string
*/
public function js($group = 'head', $attributes = [])
+74 -14
View File
@@ -3,18 +3,24 @@
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\Assets;
use Grav\Common\Assets\Traits\AssetUtilsTrait;
use Grav\Common\Config\Config;
use Grav\Common\Grav;
use Grav\Common\Uri;
use Grav\Common\Utils;
use Grav\Framework\Object\PropertyObject;
use SplFileInfo;
/**
* Class BaseAsset
* @package Grav\Common\Assets
*/
abstract class BaseAsset extends PropertyObject
{
use AssetUtilsTrait;
@@ -22,32 +28,46 @@ abstract class BaseAsset extends PropertyObject
protected const CSS_ASSET = true;
protected const JS_ASSET = false;
/** @const Regex to match CSS import content */
protected const CSS_IMPORT_REGEX = '{@import(.*?);}';
/** @var string|false */
protected $asset;
/** @var string */
protected $asset_type;
/** @var int */
protected $order;
/** @var string */
protected $group;
/** @var string */
protected $position;
/** @var int */
protected $priority;
/** @var array */
protected $attributes = [];
/** @var string */
protected $timestamp;
/** @var int|false */
protected $modified;
/** @var bool */
protected $remote;
/** @var string */
protected $query = '';
// Private Bits
private $base_url;
private $fetch_command;
/** @var bool */
private $css_rewrite = false;
/** @var bool */
private $css_minify = false;
/**
* @return string
*/
abstract function render();
/**
* BaseAsset constructor.
* @param array $elements
* @param string|null $key
*/
public function __construct(array $elements = [], $key = null)
{
$base_config = [
@@ -64,6 +84,11 @@ abstract class BaseAsset extends PropertyObject
parent::__construct($elements, $key);
}
/**
* @param string|false $asset
* @param array $options
* @return $this|false
*/
public function init($asset, $options)
{
$config = Grav::instance()['config'];
@@ -88,7 +113,6 @@ abstract class BaseAsset extends PropertyObject
// Move this to render?
if (!$this->remote) {
$asset_parts = parse_url($asset);
if (isset($asset_parts['query'])) {
$this->query = $asset_parts['query'];
@@ -101,7 +125,7 @@ abstract class BaseAsset extends PropertyObject
if ($locator->isStream($asset)) {
$path = $locator->findResource($asset, true);
} else {
$path = GRAV_ROOT . $asset;
$path = GRAV_WEBROOT . $asset;
}
// If local file is missing return
@@ -109,7 +133,7 @@ abstract class BaseAsset extends PropertyObject
return false;
}
$file = new \SplFileInfo($path);
$file = new SplFileInfo($path);
$asset = $this->buildLocalLink($file->getPathname());
@@ -122,22 +146,57 @@ abstract class BaseAsset extends PropertyObject
return $this;
}
/**
* @return string|false
*/
public function getAsset()
{
return $this->asset;
}
/**
* @return bool
*/
public function getRemote()
{
return $this->remote;
}
/**
* @param string $position
* @return $this
*/
public function setPosition($position)
{
$this->position = $position;
return $this;
}
/**
* Receive asset location and return the SRI integrity hash
*
* @param string $input
* @return string
*/
public static function integrityHash($input)
{
$grav = Grav::instance();
$assetsConfig = $grav['config']->get('system.assets');
if ( !empty($assetsConfig['enable_asset_sri']) && $assetsConfig['enable_asset_sri'] )
{
$dataToHash = file_get_contents( GRAV_WEBROOT . $input);
$hash = hash('sha256', $dataToHash, true);
$hash_base64 = base64_encode($hash);
return ' integrity="sha256-' . $hash_base64 . '"';
}
return '';
}
/**
*
@@ -149,7 +208,7 @@ abstract class BaseAsset extends PropertyObject
*/
// protected function getLastModificationTime($asset)
// {
// $file = GRAV_ROOT . $asset;
// $file = GRAV_WEBROOT . $asset;
// if (Grav::instance()['locator']->isStream($asset)) {
// $file = $this->buildLocalLink($asset, true);
// }
@@ -163,12 +222,12 @@ abstract class BaseAsset extends PropertyObject
*
* @param string $asset the asset string reference
*
* @return string the final link url to the asset
* @return string|false the final link url to the asset
*/
protected function buildLocalLink($asset)
{
if ($asset) {
return $this->base_url . ltrim(Utils::replaceFirstOccurrence(GRAV_ROOT, '', $asset), '/');
return $this->base_url . ltrim(Utils::replaceFirstOccurrence(GRAV_WEBROOT, '', $asset), '/');
}
return false;
}
@@ -190,6 +249,7 @@ abstract class BaseAsset extends PropertyObject
* @param string $file
* @param string $dir
* @param bool $local
* @return string
*/
protected function cssRewrite($file, $dir, $local)
{
+15 -3
View File
@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@@ -11,8 +11,17 @@ namespace Grav\Common\Assets;
use Grav\Common\Utils;
/**
* Class Css
* @package Grav\Common\Assets
*/
class Css extends BaseAsset
{
/**
* Css constructor.
* @param array $elements
* @param string|null $key
*/
public function __construct(array $elements = [], $key = null)
{
$base_options = [
@@ -28,13 +37,16 @@ class Css extends BaseAsset
parent::__construct($merged_attributes, $key);
}
/**
* @return string
*/
public function render()
{
if (isset($this->attributes['loading']) && $this->attributes['loading'] === 'inline') {
$buffer = $this->gatherLinks( [$this], self::CSS_ASSET);
$buffer = $this->gatherLinks([$this], self::CSS_ASSET);
return "<style>\n" . trim($buffer) . "\n</style>\n";
}
return '<link href="' . trim($this->asset) . $this->renderQueryString() . '"' . $this->renderAttributes() . ">\n";
return '<link href="' . trim($this->asset) . $this->renderQueryString() . '"' . $this->renderAttributes() . $this->integrityHash($this->asset) . ">\n";
}
}
+13 -1
View File
@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@@ -11,8 +11,17 @@ namespace Grav\Common\Assets;
use Grav\Common\Utils;
/**
* Class InlineCss
* @package Grav\Common\Assets
*/
class InlineCss extends BaseAsset
{
/**
* InlineCss constructor.
* @param array $elements
* @param string|null $key
*/
public function __construct(array $elements = [], $key = null)
{
$base_options = [
@@ -25,6 +34,9 @@ class InlineCss extends BaseAsset
parent::__construct($merged_attributes, $key);
}
/**
* @return string
*/
public function render()
{
return '<style' . $this->renderAttributes(). ">\n" . trim($this->asset) . "\n</style>\n";
+13 -1
View File
@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@@ -11,8 +11,17 @@ namespace Grav\Common\Assets;
use Grav\Common\Utils;
/**
* Class InlineJs
* @package Grav\Common\Assets
*/
class InlineJs extends BaseAsset
{
/**
* InlineJs constructor.
* @param array $elements
* @param string|null $key
*/
public function __construct(array $elements = [], $key = null)
{
$base_options = [
@@ -25,6 +34,9 @@ class InlineJs extends BaseAsset
parent::__construct($merged_attributes, $key);
}
/**
* @return string
*/
public function render()
{
return '<script' . $this->renderAttributes(). ">\n" . trim($this->asset) . "\n</script>\n";
+15 -3
View File
@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@@ -11,8 +11,17 @@ namespace Grav\Common\Assets;
use Grav\Common\Utils;
/**
* Class Js
* @package Grav\Common\Assets
*/
class Js extends BaseAsset
{
/**
* Js constructor.
* @param array $elements
* @param string|null $key
*/
public function __construct(array $elements = [], $key = null)
{
$base_options = [
@@ -24,13 +33,16 @@ class Js extends BaseAsset
parent::__construct($merged_attributes, $key);
}
/**
* @return string
*/
public function render()
{
if (isset($this->attributes['loading']) && $this->attributes['loading'] === 'inline') {
$buffer = $this->gatherLinks( [$this], self::JS_ASSET);
$buffer = $this->gatherLinks([$this], self::JS_ASSET);
return '<script' . $this->renderAttributes() . ">\n" . trim($buffer) . "\n</script>\n";
}
return '<script src="' . trim($this->asset) . $this->renderQueryString() . '"' . $this->renderAttributes() . "></script>\n";
return '<script src="' . trim($this->asset) . $this->renderQueryString() . '"' . $this->renderAttributes() . $this->integrityHash($this->asset) . "></script>\n";
}
}
+44 -37
View File
@@ -3,20 +3,28 @@
/**
* @package Grav\Common\Assets
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\Assets;
use Grav\Common\Assets\BaseAsset;
use Grav\Common\Assets\Traits\AssetUtilsTrait;
use Grav\Common\Config\Config;
use Grav\Common\Grav;
use Grav\Common\Uri;
use Grav\Common\Utils;
use Grav\Framework\Object\PropertyObject;
use MatthiasMullie\Minify\CSS;
use MatthiasMullie\Minify\JS;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use function array_key_exists;
/**
* Class Pipeline
* @package Grav\Common\Assets
*/
class Pipeline extends PropertyObject
{
use AssetUtilsTrait;
@@ -30,40 +38,42 @@ class Pipeline extends PropertyObject
/** @const Regex to match CSS sourcemap comments */
protected const CSS_SOURCEMAP_REGEX = '{\/\*# (.*?) \*\/}';
/** @const Regex to match CSS import content */
protected const CSS_IMPORT_REGEX = '{@import(.*?);}';
protected const FIRST_FORWARDSLASH_REGEX = '{^\/{1}\w}';
protected $css_minify;
protected $css_minify_windows;
protected $css_rewrite;
// Following variables come from the configuration:
/** @var bool */
protected $css_minify = false;
/** @var bool */
protected $css_minify_windows = false;
/** @var bool */
protected $css_rewrite = false;
/** @var bool */
protected $css_pipeline_include_externals = true;
/** @var bool */
protected $js_minify = false;
/** @var bool */
protected $js_minify_windows = false;
/** @var bool */
protected $js_pipeline_include_externals = true;
protected $js_minify;
protected $js_minify_windows;
protected $base_url;
/** @var string */
protected $assets_dir;
/** @var string */
protected $assets_url;
/** @var string */
protected $timestamp;
/** @var array */
protected $attributes;
protected $query;
/** @var string */
protected $query = '';
/** @var string */
protected $asset;
/**
* Closure used by the pipeline to fetch assets.
*
* Useful when file_get_contents() function is not available in your PHP
* installation or when you want to apply any kind of preprocessing to
* your assets before they get pipelined.
*
* The closure will receive as the only parameter a string with the path/URL of the asset and
* it should return the content of the asset file as a string.
*
* @var \Closure
* Pipeline constructor.
* @param array $elements
* @param string|null $key
*/
protected $fetch_command;
public function __construct(array $elements = [], ?string $key = null)
{
parent::__construct($elements, $key);
@@ -88,7 +98,6 @@ class Pipeline extends PropertyObject
* @param array $assets
* @param string $group
* @param array $attributes
*
* @return bool|string URL or generated content if available, else false
*/
public function renderCss($assets, $group, $attributes = [])
@@ -125,7 +134,7 @@ class Pipeline extends PropertyObject
// Minify if required
if ($this->shouldMinify('css')) {
$minifier = new \MatthiasMullie\Minify\CSS();
$minifier = new CSS();
$minifier->add($buffer);
$buffer = $minifier->minify();
}
@@ -140,7 +149,7 @@ class Pipeline extends PropertyObject
$output = "<style>\n" . $buffer . "\n</style>\n";
} else {
$this->asset = $relative_path;
$output = '<link href="' . $relative_path . $this->renderQueryString() . '"' . $this->renderAttributes() . ">\n";
$output = '<link href="' . $relative_path . $this->renderQueryString() . '"' . $this->renderAttributes() . BaseAsset::integrityHash($this->asset) . ">\n";
}
return $output;
@@ -152,7 +161,6 @@ class Pipeline extends PropertyObject
* @param array $assets
* @param string $group
* @param array $attributes
*
* @return bool|string URL or generated content if available, else false
*/
public function renderJs($assets, $group, $attributes = [])
@@ -189,7 +197,7 @@ class Pipeline extends PropertyObject
// Minify if required
if ($this->shouldMinify('js')) {
$minifier = new \MatthiasMullie\Minify\JS();
$minifier = new JS();
$minifier->add($buffer);
$buffer = $minifier->minify();
}
@@ -204,7 +212,7 @@ class Pipeline extends PropertyObject
$output = '<script' . $this->renderAttributes(). ">\n" . $buffer . "\n</script>\n";
} else {
$this->asset = $relative_path;
$output = '<script src="' . $relative_path . $this->renderQueryString() . '"' . $this->renderAttributes() . "></script>\n";
$output = '<script src="' . $relative_path . $this->renderQueryString() . '"' . $this->renderAttributes() . BaseAsset::integrityHash($this->asset) . "></script>\n";
}
return $output;
@@ -217,8 +225,7 @@ class Pipeline extends PropertyObject
* @param string $file the css source file
* @param string $dir , $local relative path to the css file
* @param bool $local is this a local or remote asset
*
* @return mixed
* @return string
*/
protected function cssRewrite($file, $dir, $local)
{
@@ -244,16 +251,16 @@ class Pipeline extends PropertyObject
$new_url = ($local ? $this->base_url: '') . $old_url;
$fixed = str_replace($matches[2], $new_url, $matches[0]);
return $fixed;
return str_replace($matches[2], $new_url, $matches[0]);
}, $file);
return $file;
}
/**
* @param string $type
* @return bool
*/
private function shouldMinify($type = 'css')
{
$check = $type . '_minify';
@@ -3,17 +3,42 @@
/**
* @package Grav\Common\Assets\Traits
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\Assets\Traits;
use Closure;
use Grav\Common\Grav;
use Grav\Common\Utils;
use function dirname;
use function in_array;
use function is_array;
/**
* Trait AssetUtilsTrait
* @package Grav\Common\Assets\Traits
*/
trait AssetUtilsTrait
{
/**
* @var Closure|null
*
* Closure used by the pipeline to fetch assets.
*
* Useful when file_get_contents() function is not available in your PHP
* installation or when you want to apply any kind of preprocessing to
* your assets before they get pipelined.
*
* The closure will receive as the only parameter a string with the path/URL of the asset and
* it should return the content of the asset file as a string.
*/
protected $fetch_command;
/** @var string */
protected $base_url;
/**
* Determine whether a link is local or remote.
* Understands both "http://" and "https://" as well as protocol agnostic links "//"
@@ -38,7 +63,6 @@ trait AssetUtilsTrait
*
* @param array $assets
* @param bool $css
*
* @return string
*/
protected function gatherLinks(array $assets, $css = true)
@@ -57,7 +81,7 @@ trait AssetUtilsTrait
if (0 === strpos($link, '//')) {
$link = 'http:' . $link;
}
$relative_dir = \dirname($relative_path);
$relative_dir = dirname($relative_path);
} else {
// Fix to remove relative dir if grav is in one
if (($this->base_url !== '/') && Utils::startsWith($relative_path, $this->base_url)) {
@@ -65,11 +89,12 @@ trait AssetUtilsTrait
$relative_path = ltrim(preg_replace($base_url, '/', $link, 1), '/');
}
$relative_dir = \dirname($relative_path);
$link = ROOT_DIR . $relative_path;
$relative_dir = dirname($relative_path);
$link = GRAV_ROOT . '/' . $relative_path;
}
$file = ($this->fetch_command instanceof \Closure) ? @$this->fetch_command->__invoke($link) : @file_get_contents($link);
// TODO: looks like this is not being used.
$file = $this->fetch_command instanceof Closure ? @$this->fetch_command->__invoke($link) : @file_get_contents($link);
// No file found, skip it...
if ($file === false) {
@@ -102,14 +127,15 @@ trait AssetUtilsTrait
* Moves @import statements to the top of the file per the CSS specification
*
* @param string $file the file containing the combined CSS files
*
* @return string the modified file with any @imports at the top of the file
*/
protected function moveImports($file)
{
$regex = '{@import.*?["\']([^"\']+)["\'].*?;}';
$imports = [];
$file = (string)preg_replace_callback(self::CSS_IMPORT_REGEX, function ($matches) use (&$imports) {
$file = (string)preg_replace_callback($regex, function ($matches) use (&$imports) {
$imports[] = $matches[0];
return '';
@@ -133,11 +159,11 @@ trait AssetUtilsTrait
if (is_numeric($key)) {
$key = $value;
}
if (\is_array($value)) {
if (is_array($value)) {
$value = implode(' ', $value);
}
if (\in_array($key, $no_key, true)) {
if (in_array($key, $no_key, true)) {
$element = htmlentities($value, ENT_QUOTES, 'UTF-8', false);
} else {
$element = $key . '="' . htmlentities($value, ENT_QUOTES, 'UTF-8', false) . '"';
@@ -152,7 +178,7 @@ trait AssetUtilsTrait
/**
* Render Querystring
*
* @param string $asset
* @param string|null $asset
* @return string
*/
protected function renderQueryString($asset = null)
@@ -3,17 +3,23 @@
/**
* @package Grav\Common\Assets\Traits
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\Assets\Traits;
use Grav\Common\Assets;
use function count;
use function is_array;
use function is_int;
/**
* Trait LegacyAssetsTrait
* @package Grav\Common\Assets\Traits
*/
trait LegacyAssetsTrait
{
/**
* @param array $args
* @param string $type
@@ -39,12 +45,12 @@ trait LegacyAssetsTrait
}
switch ($type) {
case(Assets::JS_TYPE):
case (Assets::JS_TYPE):
$defaults = ['priority' => null, 'pipeline' => true, 'loading' => null, 'group' => null];
$arguments = $this->createArgumentsFromLegacy($args, $defaults);
break;
case(Assets::INLINE_JS_TYPE):
case (Assets::INLINE_JS_TYPE):
$defaults = ['priority' => null, 'group' => null, 'attributes' => null];
$arguments = $this->createArgumentsFromLegacy($args, $defaults);
@@ -61,13 +67,13 @@ trait LegacyAssetsTrait
break;
case(Assets::INLINE_CSS_TYPE):
case (Assets::INLINE_CSS_TYPE):
$defaults = ['priority' => null, 'group' => null];
$arguments = $this->createArgumentsFromLegacy($args, $defaults);
break;
default:
case(Assets::CSS_TYPE):
case (Assets::CSS_TYPE):
$defaults = ['priority' => null, 'pipeline' => true, 'group' => null, 'loading' => null];
$arguments = $this->createArgumentsFromLegacy($args, $defaults);
}
@@ -75,6 +81,11 @@ trait LegacyAssetsTrait
return $arguments;
}
/**
* @param array $args
* @param array $defaults
* @return array
*/
protected function createArgumentsFromLegacy(array $args, array $defaults)
{
// Remove arguments with old default values.
@@ -97,8 +108,7 @@ trait LegacyAssetsTrait
* @param int $priority
* @param bool $pipeline
* @param string $group name of the group
*
* @return \Grav\Common\Assets
* @return Assets
* @deprecated Please use dynamic method with ['loading' => 'async'].
*/
public function addAsyncJs($asset, $priority = 10, $pipeline = true, $group = 'head')
@@ -115,8 +125,7 @@ trait LegacyAssetsTrait
* @param int $priority
* @param bool $pipeline
* @param string $group name of the group
*
* @return \Grav\Common\Assets
* @return Assets
* @deprecated Please use dynamic method with ['loading' => 'defer'].
*/
public function addDeferJs($asset, $priority = 10, $pipeline = true, $group = 'head')
@@ -125,5 +134,4 @@ trait LegacyAssetsTrait
return $this->addJs($asset, $priority, $pipeline, 'defer', $group);
}
}
@@ -3,21 +3,29 @@
/**
* @package Grav\Common\Assets\Traits
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\Assets\Traits;
use FilesystemIterator;
use Grav\Common\Grav;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use RegexIterator;
use function strlen;
/**
* Trait TestingAssetsTrait
* @package Grav\Common\Assets\Traits
*/
trait TestingAssetsTrait
{
/**
* Determines if an asset exists as a collection, CSS or JS reference
*
* @param string $asset
*
* @return bool
*/
public function exists($asset)
@@ -39,7 +47,6 @@ trait TestingAssetsTrait
* Set the array of collections explicitly
*
* @param array $collections
*
* @return $this
*/
public function setCollection($collections)
@@ -54,7 +61,7 @@ trait TestingAssetsTrait
* If a $key is provided, it will try to return only that asset
* else it will return null
*
* @param null|string $key the asset key
* @param string|null $key the asset key
* @return array
*/
public function getCss($key = null)
@@ -73,7 +80,7 @@ trait TestingAssetsTrait
* If a $key is provided, it will try to return only that asset
* else it will return null
*
* @param null|string $key the asset key
* @param string|null $key the asset key
* @return array
*/
public function getJs($key = null)
@@ -91,7 +98,6 @@ trait TestingAssetsTrait
* Set the whole array of CSS assets
*
* @param array $css
*
* @return $this
*/
public function setCss($css)
@@ -105,7 +111,6 @@ trait TestingAssetsTrait
* Set the whole array of JS assets
*
* @param array $js
*
* @return $this
*/
public function setJs($js)
@@ -119,7 +124,6 @@ trait TestingAssetsTrait
* Removes an item from the CSS array if set
*
* @param string $key The asset key
*
* @return $this
*/
public function removeCss($key)
@@ -136,7 +140,6 @@ trait TestingAssetsTrait
* Removes an item from the JS array if set
*
* @param string $key The asset key
*
* @return $this
*/
public function removeJs($key)
@@ -153,7 +156,6 @@ trait TestingAssetsTrait
* Sets the state of CSS Pipeline
*
* @param bool $value
*
* @return $this
*/
public function setCssPipeline($value)
@@ -167,7 +169,6 @@ trait TestingAssetsTrait
* Sets the state of JS Pipeline
*
* @param bool $value
*
* @return $this
*/
public function setJsPipeline($value)
@@ -188,6 +189,7 @@ trait TestingAssetsTrait
$this->resetJs();
$this->setCssPipeline(false);
$this->setJsPipeline(false);
$this->order = [];
return $this;
}
@@ -230,7 +232,7 @@ trait TestingAssetsTrait
* Get the timestamp for assets
*
* @param bool $include_join
* @return string
* @return string|null
*/
public function getTimestamp($include_join = true)
{
@@ -246,12 +248,11 @@ trait TestingAssetsTrait
*
* @param string $directory Relative to the Grav root path, or a stream identifier
* @param string $pattern (regex)
*
* @return $this
*/
public function addDir($directory, $pattern = self::DEFAULT_REGEX)
{
$root_dir = rtrim(ROOT_DIR, '/');
$root_dir = GRAV_ROOT;
// Check if $directory is a stream.
if (strpos($directory, '://')) {
@@ -296,7 +297,6 @@ trait TestingAssetsTrait
* Add all JavaScript assets within $directory
*
* @param string $directory Relative to the Grav root path, or a stream identifier
*
* @return $this
*/
public function addDirJs($directory)
@@ -308,7 +308,6 @@ trait TestingAssetsTrait
* Add all CSS assets within $directory
*
* @param string $directory Relative to the Grav root path, or a stream identifier
*
* @return $this
*/
public function addDirCss($directory)
@@ -321,15 +320,16 @@ trait TestingAssetsTrait
*
* @param string $directory
* @param string $pattern (regex)
* @param string $ltrim Will be trimmed from the left of the file path
*
* @param string|null $ltrim Will be trimmed from the left of the file path
* @return array
*/
protected function rglob($directory, $pattern, $ltrim = null)
{
$iterator = new \RegexIterator(new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory,
\FilesystemIterator::SKIP_DOTS)), $pattern);
$offset = \strlen($ltrim);
$iterator = new RegexIterator(new RecursiveIteratorIterator(new RecursiveDirectoryIterator(
$directory,
FilesystemIterator::SKIP_DOTS
)), $pattern);
$offset = strlen($ltrim);
$files = [];
foreach ($iterator as $file) {
@@ -338,6 +338,4 @@ trait TestingAssetsTrait
return $files;
}
}
+96 -34
View File
@@ -3,12 +3,16 @@
/**
* @package Grav\Common\Backup
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\Backup;
use DateTime;
use Exception;
use FilesystemIterator;
use GlobIterator;
use Grav\Common\Filesystem\Archiver;
use Grav\Common\Filesystem\Folder;
use Grav\Common\Inflector;
@@ -17,81 +21,126 @@ use Grav\Common\Scheduler\Scheduler;
use Grav\Common\Utils;
use Grav\Common\Grav;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\Event\EventDispatcher;
use RocketTheme\Toolbox\File\JsonFile;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use RuntimeException;
use SplFileInfo;
use stdClass;
use Symfony\Component\EventDispatcher\EventDispatcher;
use function count;
/**
* Class Backups
* @package Grav\Common\Backup
*/
class Backups
{
protected const BACKUP_FILENAME_REGEXZ = "#(.*)--(\d*).zip#";
protected const BACKUP_DATE_FORMAT = 'YmdHis';
/** @var string */
protected static $backup_dir;
protected static $backups = null;
/** @var array|null */
protected static $backups;
/**
* @return void
*/
public function init()
{
$grav = Grav::instance();
/** @var EventDispatcher $dispatcher */
$dispatcher = Grav::instance()['events'];
$dispatcher = $grav['events'];
$dispatcher->addListener('onSchedulerInitialized', [$this, 'onSchedulerInitialized']);
Grav::instance()->fireEvent('onBackupsInitialized', new Event(['backups' => $this]));
$grav->fireEvent('onBackupsInitialized', new Event(['backups' => $this]));
}
/**
* @return void
*/
public function setup()
{
if (null === static::$backup_dir) {
static::$backup_dir = Grav::instance()['locator']->findResource('backup://', true, true);
$grav = Grav::instance();
static::$backup_dir = $grav['locator']->findResource('backup://', true, true);
Folder::create(static::$backup_dir);
}
}
/**
* @param Event $event
* @return void
*/
public function onSchedulerInitialized(Event $event)
{
$grav = Grav::instance();
/** @var Scheduler $scheduler */
$scheduler = $event['scheduler'];
/** @var Inflector $inflector */
$inflector = Grav::instance()['inflector'];
$inflector = $grav['inflector'];
foreach (static::getBackupProfiles() as $id => $profile) {
$at = $profile['schedule_at'];
$name = $inflector::hyphenize($profile['name']);
$logs = 'logs/backup-' . $name . '.out';
/** @var Job $job */
$job = $scheduler->addFunction('Grav\Common\Backup\Backups::backup', [$id], $name );
$job = $scheduler->addFunction('Grav\Common\Backup\Backups::backup', [$id], $name);
$job->at($at);
$job->output($logs);
$job->backlink('/tools/backups');
}
}
/**
* @param string $backup
* @param string $base_url
* @return string
*/
public function getBackupDownloadUrl($backup, $base_url)
{
$param_sep = $param_sep = Grav::instance()['config']->get('system.param_sep', ':');
$download = urlencode(base64_encode($backup));
$url = rtrim(Grav::instance()['uri']->rootUrl(true), '/') . '/' . trim($base_url,
'/') . '/task' . $param_sep . 'backup/download' . $param_sep . $download . '/admin-nonce' . $param_sep . Utils::getNonce('admin-form');
$download = urlencode(base64_encode(basename($backup)));
$url = rtrim(Grav::instance()['uri']->rootUrl(true), '/') . '/' . trim(
$base_url,
'/'
) . '/task' . $param_sep . 'backup/download' . $param_sep . $download . '/admin-nonce' . $param_sep . Utils::getNonce('admin-form');
return $url;
}
/**
* @return array
*/
public static function getBackupProfiles()
{
return Grav::instance()['config']->get('backups.profiles');
}
/**
* @return array
*/
public static function getPurgeConfig()
{
return Grav::instance()['config']->get('backups.purge');
}
/**
* @return array
*/
public function getBackupNames()
{
return array_column(static::getBackupProfiles(), 'name');
}
/**
* @return float|int
*/
public static function getTotalBackupsSize()
{
$backups = static::getAvailableBackups();
@@ -100,24 +149,29 @@ class Backups
return $size ?? 0;
}
/**
* @param bool $force
* @return array
*/
public static function getAvailableBackups($force = false)
{
if ($force || null === static::$backups) {
static::$backups = [];
$backups_itr = new \GlobIterator(static::$backup_dir . '/*.zip', \FilesystemIterator::KEY_AS_FILENAME);
$inflector = Grav::instance()['inflector'];
$grav = Grav::instance();
$backups_itr = new GlobIterator(static::$backup_dir . '/*.zip', FilesystemIterator::KEY_AS_FILENAME);
$inflector = $grav['inflector'];
$long_date_format = DATE_RFC2822;
/**
* @var string $name
* @var \SplFileInfo $file
* @var SplFileInfo $file
*/
foreach ($backups_itr as $name => $file) {
if (preg_match(static::BACKUP_FILENAME_REGEXZ, $name, $matches)) {
$date = \DateTime::createFromFormat(static::BACKUP_DATE_FORMAT, $matches[2]);
$date = DateTime::createFromFormat(static::BACKUP_DATE_FORMAT, $matches[2]);
$timestamp = $date->getTimestamp();
$backup = new \stdClass();
$backup = new stdClass();
$backup->title = $inflector->titleize($matches[1]);
$backup->time = $date;
$backup->date = $date->format($long_date_format);
@@ -137,28 +191,29 @@ class Backups
/**
* Backup
*
* @param int $id
* @param int $id
* @param callable|null $status
*
* @return null|string
* @return string|null
*/
public static function backup($id = 0, callable $status = null)
{
$grav = Grav::instance();
$profiles = static::getBackupProfiles();
/** @var UniformResourceLocator $locator */
$locator = Grav::instance()['locator'];
$locator = $grav['locator'];
if (isset($profiles[$id])) {
$backup = (object) $profiles[$id];
} else {
throw new \RuntimeException('No backups defined...');
throw new RuntimeException('No backups defined...');
}
$name = Grav::instance()['inflector']->underscorize($backup->name);
$name = $grav['inflector']->underscorize($backup->name);
$date = date(static::BACKUP_DATE_FORMAT, time());
$filename = trim($name, '_') . '--' . $date . '.zip';
$destination = static::$backup_dir . DS . $filename;
$max_execution_time = ini_set('max_execution_time', 600);
$max_execution_time = ini_set('max_execution_time', '600');
$backup_root = $backup->root;
if ($locator->isStream($backup_root)) {
@@ -168,7 +223,7 @@ class Backups
}
if (!file_exists($backup_root)) {
throw new \RuntimeException("Backup location: {$backup_root} does not exist...");
throw new RuntimeException("Backup location: {$backup_root} does not exist...");
}
$options = [
@@ -176,7 +231,6 @@ class Backups
'exclude_paths' => static::convertExclude($backup->exclude_paths ?? ''),
];
/** @var Archiver $archiver */
$archiver = Archiver::create('zip');
$archiver->setArchive($destination)->setOptions($options)->compress($backup_root, $status)->addEmptyFolders($options['exclude_paths'], $status);
@@ -195,16 +249,16 @@ class Backups
}
// Log the backup
Grav::instance()['log']->notice('Backup Created: ' . $destination);
$grav['log']->notice('Backup Created: ' . $destination);
// Fire Finished event
Grav::instance()->fireEvent('onBackupFinished', new Event(['backup' => $destination]));
$grav->fireEvent('onBackupFinished', new Event(['backup' => $destination]));
// Purge anything required
static::purge();
// Log
$log = JsonFile::instance(Grav::instance()['locator']->findResource("log://backup.log", true, true));
$log = JsonFile::instance($locator->findResource("log://backup.log", true, true));
$log->content([
'time' => time(),
'location' => $destination
@@ -214,26 +268,29 @@ class Backups
return $destination;
}
/**
* @return void
* @throws Exception
*/
public static function purge()
{
$purge_config = static::getPurgeConfig();
$trigger = $purge_config['trigger'];
$backups = static::getAvailableBackups(true);
switch ($trigger)
{
switch ($trigger) {
case 'number':
$backups_count = count($backups);
if ($backups_count > $purge_config['max_backups_count']) {
$last = end($backups);
unlink ($last->path);
unlink($last->path);
static::purge();
}
break;
case 'time':
$last = end($backups);
$now = new \DateTime();
$now = new DateTime();
$interval = $now->diff($last->time);
if ($interval->days > $purge_config['max_backups_time']) {
unlink($last->path);
@@ -253,9 +310,14 @@ class Backups
}
}
/**
* @param string $exclude
* @return array
*/
protected static function convertExclude($exclude)
{
$lines = preg_split("/[\s,]+/", $exclude);
return array_map('trim', $lines, array_fill(0, \count($lines), '/'));
return array_map('trim', $lines, array_fill(0, count($lines), '/'));
}
}
+6 -4
View File
@@ -3,12 +3,13 @@
/**
* @package Grav\Common
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common;
use InvalidArgumentException;
use function donatj\UserAgent\parse_user_agent;
/**
@@ -16,6 +17,7 @@ use function donatj\UserAgent\parse_user_agent;
*/
class Browser
{
/** @var string[] */
protected $useragent = [];
/**
@@ -25,7 +27,7 @@ class Browser
{
try {
$this->useragent = parse_user_agent();
} catch (\InvalidArgumentException $e) {
} catch (InvalidArgumentException $e) {
$this->useragent = parse_user_agent("Mozilla/5.0 (compatible; Unknown;)");
}
}
@@ -110,7 +112,7 @@ class Browser
/**
* Get the current major version identifier
*
* @return string the browser major version identifier
* @return int the browser major version identifier
*/
public function getVersion()
{
@@ -137,7 +139,7 @@ class Browser
return true;
}
/**
* Determine if “Do Not Track” is set by browser
* @see https://www.w3.org/TR/tracking-dnt/
+85 -47
View File
@@ -3,19 +3,27 @@
/**
* @package Grav\Common
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common;
use DirectoryIterator;
use \Doctrine\Common\Cache as DoctrineCache;
use Exception;
use Grav\Common\Config\Config;
use Grav\Common\Filesystem\Folder;
use Grav\Common\Scheduler\Scheduler;
use LogicException;
use Psr\SimpleCache\CacheInterface;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\Event\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcher;
use function dirname;
use function extension_loaded;
use function function_exists;
use function in_array;
use function is_array;
/**
* The GravCache object is used throughout Grav to store and retrieve cached data.
@@ -29,42 +37,41 @@ use RocketTheme\Toolbox\Event\EventDispatcher;
*/
class Cache extends Getters
{
/**
* @var string Cache key.
*/
/** @var string Cache key. */
protected $key;
/** @var int */
protected $lifetime;
/** @var int */
protected $now;
/** @var Config $config */
protected $config;
/**
* @var DoctrineCache\CacheProvider
*/
/** @var DoctrineCache\CacheProvider */
protected $driver;
/**
* @var CacheInterface
*/
/** @var CacheInterface */
protected $simpleCache;
/** @var string */
protected $driver_name;
/** @var string */
protected $driver_setting;
/**
* @var bool
*/
/** @var bool */
protected $enabled;
/** @var string */
protected $cache_dir;
protected static $standard_remove = [
'cache://twig/',
'cache://doctrine/',
'cache://compiled/',
'cache://clockwork/',
'cache://validated-',
'cache://images',
'asset://',
@@ -74,6 +81,7 @@ class Cache extends Getters
'cache://twig/',
'cache://doctrine/',
'cache://compiled/',
'cache://clockwork/',
'cache://validated-',
'asset://',
];
@@ -115,12 +123,10 @@ class Cache extends Getters
* Initialization that sets a base key and the driver based on configuration settings
*
* @param Grav $grav
*
* @return void
*/
public function init(Grav $grav)
{
/** @var Config $config */
$this->config = $grav['config'];
$this->now = time();
@@ -174,7 +180,7 @@ class Cache extends Getters
$current = basename($this->cache_dir);
$count = 0;
foreach (new \DirectoryIterator($cache_dir) as $file) {
foreach (new DirectoryIterator($cache_dir) as $file) {
$dir = $file->getBasename();
if ($dir === $current || $file->isDot() || $file->isFile()) {
continue;
@@ -191,6 +197,7 @@ class Cache extends Getters
* Public accessor to set the enabled state of the cache
*
* @param bool|int $enabled
* @return void
*/
public function setEnabled($enabled)
{
@@ -260,24 +267,28 @@ class Cache extends Getters
case 'memcache':
if (extension_loaded('memcache')) {
$memcache = new \Memcache();
$memcache->connect($this->config->get('system.cache.memcache.server', 'localhost'),
$this->config->get('system.cache.memcache.port', 11211));
$memcache->connect(
$this->config->get('system.cache.memcache.server', 'localhost'),
$this->config->get('system.cache.memcache.port', 11211)
);
$driver = new DoctrineCache\MemcacheCache();
$driver->setMemcache($memcache);
} else {
throw new \LogicException('Memcache PHP extension has not been installed');
throw new LogicException('Memcache PHP extension has not been installed');
}
break;
case 'memcached':
if (extension_loaded('memcached')) {
$memcached = new \Memcached();
$memcached->addServer($this->config->get('system.cache.memcached.server', 'localhost'),
$this->config->get('system.cache.memcached.port', 11211));
$memcached->addServer(
$this->config->get('system.cache.memcached.server', 'localhost'),
$this->config->get('system.cache.memcached.port', 11211)
);
$driver = new DoctrineCache\MemcachedCache();
$driver->setMemcached($memcached);
} else {
throw new \LogicException('Memcached PHP extension has not been installed');
throw new LogicException('Memcached PHP extension has not been installed');
}
break;
@@ -286,12 +297,15 @@ class Cache extends Getters
$redis = new \Redis();
$socket = $this->config->get('system.cache.redis.socket', false);
$password = $this->config->get('system.cache.redis.password', false);
$databaseId = $this->config->get('system.cache.redis.database', 0);
if ($socket) {
$redis->connect($socket);
} else {
$redis->connect($this->config->get('system.cache.redis.server', 'localhost'),
$this->config->get('system.cache.redis.port', 6379));
$redis->connect(
$this->config->get('system.cache.redis.server', 'localhost'),
$this->config->get('system.cache.redis.port', 6379)
);
}
// Authenticate with password if set
@@ -299,10 +313,15 @@ class Cache extends Getters
throw new \RedisException('Redis authentication failed');
}
// Select alternate ( !=0 ) database ID if set
if ($databaseId && !$redis->select($databaseId)) {
throw new \RedisException('Could not select alternate Redis database ID');
}
$driver = new DoctrineCache\RedisCache();
$driver->setRedis($redis);
} else {
throw new \LogicException('Redis PHP extension has not been installed');
throw new LogicException('Redis PHP extension has not been installed');
}
break;
@@ -318,8 +337,7 @@ class Cache extends Getters
* Gets a cached entry if it exists based on an id. If it does not exist, it returns false
*
* @param string $id the id of the cached entry
*
* @return object|bool returns the cached entry, can be any type, or false if doesn't exist
* @return mixed|bool returns the cached entry, can be any type, or false if doesn't exist
*/
public function fetch($id)
{
@@ -334,8 +352,8 @@ class Cache extends Getters
* Stores a new cached entry.
*
* @param string $id the id of the cached entry
* @param array|object $data the data for the cached entry to store
* @param int $lifetime the lifetime to store the entry in seconds
* @param array|object|int $data the data for the cached entry to store
* @param int|null $lifetime the lifetime to store the entry in seconds
*/
public function save($id, $data, $lifetime = null)
{
@@ -393,6 +411,8 @@ class Cache extends Getters
/**
* Getter method to get the cache key
*
* @return string
*/
public function getKey()
{
@@ -401,6 +421,9 @@ class Cache extends Getters
/**
* Setter method to set key (Advanced)
*
* @param string $key
* @return void
*/
public function setKey($key)
{
@@ -412,7 +435,6 @@ class Cache extends Getters
* Helper method to clear all Grav caches
*
* @param string $remove standard|all|assets-only|images-only|cache-only
*
* @return array
*/
public static function clearCache($remove = 'standard')
@@ -446,7 +468,6 @@ class Cache extends Getters
} else {
$remove_paths = self::$standard_remove_no_images;
}
}
// Delete entries in the doctrine cache if required
@@ -459,11 +480,12 @@ class Cache extends Getters
Grav::instance()->fireEvent('onBeforeCacheClear', new Event(['remove' => $remove, 'paths' => &$remove_paths]));
foreach ($remove_paths as $stream) {
// Convert stream to a real path
try {
$path = $locator->findResource($stream, true, true);
if($path === false) continue;
if ($path === false) {
continue;
}
$anything = false;
$files = glob($path . '/*');
@@ -477,7 +499,7 @@ class Cache extends Getters
$anything = true;
}
} elseif (is_dir($file)) {
if (Folder::delete($file)) {
if (Folder::delete($file, false)) {
$anything = true;
}
}
@@ -487,7 +509,7 @@ class Cache extends Getters
if ($anything) {
$output[] = '<red>Cleared: </red>' . $path . '/*';
}
} catch (\Exception $e) {
} catch (Exception $e) {
// stream not found or another error while deleting files.
$output[] = '<red>ERROR: </red>' . $e->getMessage();
}
@@ -510,9 +532,14 @@ class Cache extends Getters
@opcache_reset();
}
Grav::instance()->fireEvent('onAfterCacheClear', new Event(['remove' => $remove, 'output' => &$output]));
return $output;
}
/**
* @return void
*/
public static function invalidateCache()
{
$user_config = USER_DIR . 'config/system.yaml';
@@ -528,13 +555,13 @@ class Cache extends Getters
if (function_exists('opcache_reset')) {
@opcache_reset();
}
}
/**
* Set the cache lifetime programmatically
*
* @param int $future timestamp
* @return void
*/
public function setLifetime($future)
{
@@ -552,7 +579,7 @@ class Cache extends Getters
/**
* Retrieve the cache lifetime (in seconds)
*
* @return mixed
* @return int
*/
public function getLifetime()
{
@@ -566,7 +593,7 @@ class Cache extends Getters
/**
* Returns the current driver name
*
* @return mixed
* @return string
*/
public function getDriverName()
{
@@ -576,7 +603,7 @@ class Cache extends Getters
/**
* Returns the current driver setting
*
* @return mixed
* @return string
*/
public function getDriverSetting()
{
@@ -600,20 +627,30 @@ class Cache extends Getters
/**
* Static function to call as a scheduled Job to purge old Doctrine files
*
* @param bool $echo
*
* @return string|void
*/
public static function purgeJob()
public static function purgeJob($echo = false)
{
/** @var Cache $cache */
$cache = Grav::instance()['cache'];
$deleted_folders = $cache->purgeOldCache();
$msg = 'Purged ' . $deleted_folders . ' old cache folders...';
echo 'Purged ' . $deleted_folders . ' old cache folders...';
if ($echo) {
echo $msg;
} else {
return $msg;
}
}
/**
* Static function to call as a scheduled Job to clear Grav cache
*
* @param string $type
* @return void
*/
public static function clearJob($type)
{
@@ -623,6 +660,10 @@ class Cache extends Getters
echo strip_tags(implode("\n", $result));
}
/**
* @param Event $event
* @return void
*/
public function onSchedulerInitialized(Event $event)
{
/** @var Scheduler $scheduler */
@@ -634,7 +675,7 @@ class Cache extends Getters
$name = 'cache-purge';
$logs = 'logs/' . $name . '.out';
$job = $scheduler->addFunction('Grav\Common\Cache::purgeJob', [], $name );
$job = $scheduler->addFunction('Grav\Common\Cache::purgeJob', [true], $name);
$job->at($at);
$job->output($logs);
$job->backlink('/config/system#caching');
@@ -645,12 +686,9 @@ class Cache extends Getters
$name = 'cache-clear';
$logs = 'logs/' . $name . '.out';
$job = $scheduler->addFunction('Grav\Common\Cache::clearJob', [$clear_type], $name );
$job = $scheduler->addFunction('Grav\Common\Cache::clearJob', [$clear_type], $name);
$job->at($at);
$job->output($logs);
$job->backlink('/config/system#caching');
}
}
+9 -3
View File
@@ -3,12 +3,18 @@
/**
* @package Grav\Common
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common;
use function function_exists;
/**
* Class Composer
* @package Grav\Common
*/
class Composer
{
/** @const Default composer location */
@@ -21,12 +27,12 @@ class Composer
*/
public static function getComposerLocation()
{
if (!\function_exists('shell_exec') || stripos(PHP_OS, 'win') === 0) {
if (!function_exists('shell_exec') || stripos(PHP_OS, 'win') === 0) {
return self::DEFAULT_PATH;
}
// check for global composer install
$path = trim(shell_exec('command -v composer'));
$path = trim((string)shell_exec('command -v composer'));
// fall back to grav bundled composer
if (!$path || !preg_match('/(composer|composer\.phar)$/', $path)) {
+44 -38
View File
@@ -3,78 +3,70 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\Config;
use BadMethodCallException;
use Exception;
use RocketTheme\Toolbox\File\PhpFile;
use RuntimeException;
use function get_class;
use function is_array;
/**
* Class CompiledBase
* @package Grav\Common\Config
*/
abstract class CompiledBase
{
/**
* @var int Version number for the compiled file.
*/
/** @var int Version number for the compiled file. */
public $version = 1;
/**
* @var string Filename (base name) of the compiled configuration.
*/
/** @var string Filename (base name) of the compiled configuration. */
public $name;
/**
* @var string|bool Configuration checksum.
*/
/** @var string|bool Configuration checksum. */
public $checksum;
/**
* @var string Timestamp of compiled configuration
*/
public $timestamp;
/** @var int Timestamp of compiled configuration */
public $timestamp = 0;
/**
* @var string Cache folder to be used.
*/
/** @var string Cache folder to be used. */
protected $cacheFolder;
/**
* @var array List of files to load.
*/
/** @var array List of files to load. */
protected $files;
/**
* @var string
*/
/** @var string */
protected $path;
/**
* @var mixed Configuration object.
*/
/** @var mixed Configuration object. */
protected $object;
/**
* @param string $cacheFolder Cache folder to be used.
* @param array $files List of files as returned from ConfigFileFinder class.
* @param string $path Base path for the file list.
* @throws \BadMethodCallException
* @throws BadMethodCallException
*/
public function __construct($cacheFolder, array $files, $path)
{
if (!$cacheFolder) {
throw new \BadMethodCallException('Cache folder not defined.');
throw new BadMethodCallException('Cache folder not defined.');
}
$this->path = $path ? rtrim($path, '\\/') . '/' : '';
$this->cacheFolder = $cacheFolder;
$this->files = $files;
$this->timestamp = 0;
}
/**
* Get filename for the compiled PHP file.
*
* @param string $name
* @param string|null $name
* @return $this
*/
public function name($name = null)
@@ -88,8 +80,12 @@ abstract class CompiledBase
/**
* Function gets called when cached configuration is saved.
*
* @return void
*/
public function modified() {}
public function modified()
{
}
/**
* Get timestamp of compiled configuration
@@ -136,6 +132,9 @@ abstract class CompiledBase
return $this->checksum;
}
/**
* @return string
*/
protected function createFilename()
{
return "{$this->cacheFolder}/{$this->name()->name}.php";
@@ -145,11 +144,14 @@ abstract class CompiledBase
* Create configuration object.
*
* @param array $data
* @return void
*/
abstract protected function createObject(array $data = []);
/**
* Finalize configuration object.
*
* @return void
*/
abstract protected function finalizeObject();
@@ -157,7 +159,8 @@ abstract class CompiledBase
* Load single configuration file and append it to the correct position.
*
* @param string $name Name of the position.
* @param string $filename File to be loaded.
* @param string|string[] $filename File(s) to be loaded.
* @return void
*/
abstract protected function loadFile($name, $filename);
@@ -197,10 +200,9 @@ abstract class CompiledBase
}
$cache = include $filename;
if (
!\is_array($cache)
if (!is_array($cache)
|| !isset($cache['checksum'], $cache['data'], $cache['@class'])
|| $cache['@class'] !== \get_class($this)
|| $cache['@class'] !== get_class($this)
) {
return false;
}
@@ -222,7 +224,8 @@ abstract class CompiledBase
* Save compiled file.
*
* @param string $filename
* @throws \RuntimeException
* @return void
* @throws RuntimeException
* @internal
*/
protected function saveCompiledFile($filename)
@@ -232,7 +235,7 @@ abstract class CompiledBase
// Attempt to lock the file for writing.
try {
$file->lock(false);
} catch (\Exception $e) {
} catch (Exception $e) {
// Another process has locked the file; we will check this in a bit.
}
@@ -242,7 +245,7 @@ abstract class CompiledBase
}
$cache = [
'@class' => \get_class($this),
'@class' => get_class($this),
'timestamp' => time(),
'checksum' => $this->checksum(),
'files' => $this->files,
@@ -256,6 +259,9 @@ abstract class CompiledBase
$this->modified();
}
/**
* @return array
*/
protected function getState()
{
return $this->object->toArray();
@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@@ -19,6 +19,12 @@ use Grav\Common\Grav;
*/
class CompiledBlueprints extends CompiledBase
{
/**
* CompiledBlueprints constructor.
* @param string $cacheFolder
* @param array $files
* @param string $path
*/
public function __construct($cacheFolder, array $files, $path)
{
parent::__construct($cacheFolder, $files, $path);
@@ -45,7 +51,7 @@ class CompiledBlueprints extends CompiledBase
/**
* Create configuration object.
*
* @param array $data
* @param array $data
*/
protected function createObject(array $data = [])
{
@@ -64,6 +70,8 @@ class CompiledBlueprints extends CompiledBase
/**
* Finalize configuration object.
*
* @return void
*/
protected function finalizeObject()
{
@@ -74,6 +82,7 @@ class CompiledBlueprints extends CompiledBase
*
* @param string $name Name of the position.
* @param array $files Files to be loaded.
* @return void
*/
protected function loadFile($name, $files)
{
@@ -112,6 +121,9 @@ class CompiledBlueprints extends CompiledBase
return true;
}
/**
* @return array
*/
protected function getState()
{
return $this->object->getState();
@@ -3,26 +3,33 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\Config;
use Grav\Common\File\CompiledYamlFile;
use function is_callable;
/**
* Class CompiledConfig
* @package Grav\Common\Config
*/
class CompiledConfig extends CompiledBase
{
/**
* @var callable Blueprints loader.
*/
/** @var callable Blueprints loader. */
protected $callable;
/**
* @var bool
*/
protected $withDefaults;
/** @var bool */
protected $withDefaults = false;
/**
* CompiledConfig constructor.
* @param string $cacheFolder
* @param array $files
* @param string $path
*/
public function __construct($cacheFolder, array $files, $path)
{
parent::__construct($cacheFolder, $files, $path);
@@ -58,10 +65,11 @@ class CompiledConfig extends CompiledBase
* Create configuration object.
*
* @param array $data
* @return void
*/
protected function createObject(array $data = [])
{
if ($this->withDefaults && empty($data) && \is_callable($this->callable)) {
if ($this->withDefaults && empty($data) && is_callable($this->callable)) {
$blueprints = $this->callable;
$data = $blueprints()->getDefaults();
}
@@ -71,6 +79,8 @@ class CompiledConfig extends CompiledBase
/**
* Finalize configuration object.
*
* @return void
*/
protected function finalizeObject()
{
@@ -80,6 +90,8 @@ class CompiledConfig extends CompiledBase
/**
* Function gets called when cached configuration is saved.
*
* @return void
*/
public function modified()
{
@@ -91,6 +103,7 @@ class CompiledConfig extends CompiledBase
*
* @param string $name Name of the position.
* @param string $filename File to be loaded.
* @return void
*/
protected function loadFile($name, $filename)
{
@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@@ -11,8 +11,18 @@ namespace Grav\Common\Config;
use Grav\Common\File\CompiledYamlFile;
/**
* Class CompiledLanguages
* @package Grav\Common\Config
*/
class CompiledLanguages extends CompiledBase
{
/**
* CompiledLanguages constructor.
* @param string $cacheFolder
* @param array $files
* @param string $path
*/
public function __construct($cacheFolder, array $files, $path)
{
parent::__construct($cacheFolder, $files, $path);
@@ -24,6 +34,7 @@ class CompiledLanguages extends CompiledBase
* Create configuration object.
*
* @param array $data
* @return void
*/
protected function createObject(array $data = [])
{
@@ -32,6 +43,8 @@ class CompiledLanguages extends CompiledBase
/**
* Finalize configuration object.
*
* @return void
*/
protected function finalizeObject()
{
@@ -42,6 +55,8 @@ class CompiledLanguages extends CompiledBase
/**
* Function gets called when cached configuration is saved.
*
* @return void
*/
public function modified()
{
@@ -53,6 +68,7 @@ class CompiledLanguages extends CompiledBase
*
* @param string $name Name of the position.
* @param string $filename File to be loaded.
* @return void
*/
protected function loadFile($name, $filename)
{
+32 -2
View File
@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@@ -14,9 +14,15 @@ use Grav\Common\Grav;
use Grav\Common\Data\Data;
use Grav\Common\Service\ConfigServiceProvider;
use Grav\Common\Utils;
use function is_array;
/**
* Class Config
* @package Grav\Common\Config
*/
class Config extends Data
{
/** @var string */
public $environment;
/** @var string */
@@ -28,6 +34,9 @@ class Config extends Data
/** @var bool */
protected $modified = false;
/**
* @return string
*/
public function key()
{
if (null === $this->key) {
@@ -37,6 +46,10 @@ class Config extends Data
return $this->key;
}
/**
* @param string|null $checksum
* @return string|null
*/
public function checksum($checksum = null)
{
if ($checksum !== null) {
@@ -46,6 +59,10 @@ class Config extends Data
return $this->checksum;
}
/**
* @param bool|null $modified
* @return bool
*/
public function modified($modified = null)
{
if ($modified !== null) {
@@ -55,6 +72,10 @@ class Config extends Data
return $this->modified;
}
/**
* @param int|null $timestamp
* @return int
*/
public function timestamp($timestamp = null)
{
if ($timestamp !== null) {
@@ -64,6 +85,9 @@ class Config extends Data
return $this->timestamp;
}
/**
* @return $this
*/
public function reload()
{
$grav = Grav::instance();
@@ -86,6 +110,9 @@ class Config extends Data
return $this;
}
/**
* @return void
*/
public function debug()
{
/** @var Debugger $debugger */
@@ -97,11 +124,14 @@ class Config extends Data
}
}
/**
* @return void
*/
public function init()
{
$setup = Grav::instance()['setup']->toArray();
foreach ($setup as $key => $value) {
if ($key === 'streams' || !\is_array($value)) {
if ($key === 'streams' || !is_array($value)) {
// Optimized as streams and simple values are fully defined in setup.
$this->items[$key] = $value;
} else {
@@ -3,16 +3,23 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\Config;
use DirectoryIterator;
use Grav\Common\Filesystem\Folder;
use RecursiveDirectoryIterator;
/**
* Class ConfigFileFinder
* @package Grav\Common\Config
*/
class ConfigFileFinder
{
/** @var string */
protected $base = '';
/**
@@ -40,6 +47,7 @@ class ConfigFileFinder
foreach ($paths as $folder) {
$list += $this->detectRecursive($folder, $pattern, $levels);
}
return $list;
}
@@ -61,6 +69,7 @@ class ConfigFileFinder
$list += $files[trim($path, '/')];
}
return $list;
}
@@ -78,6 +87,7 @@ class ConfigFileFinder
foreach ($paths as $folder) {
$list = array_merge_recursive($list, $this->detectAll($folder, $pattern, $levels));
}
return $list;
}
@@ -96,6 +106,7 @@ class ConfigFileFinder
foreach ($folders as $folder) {
$list += $this->detectInFolder($folder, $filename);
}
return $list;
}
@@ -103,7 +114,7 @@ class ConfigFileFinder
* Find filename from a list of folders.
*
* @param array $folders
* @param string $filename
* @param string|null $filename
* @return array
*/
public function locateInFolders(array $folders, $filename = null)
@@ -113,6 +124,7 @@ class ConfigFileFinder
$path = trim(Folder::getRelativePath($folder), '/');
$list[$path] = $this->detectInFolder($folder, $filename);
}
return $list;
}
@@ -166,7 +178,7 @@ class ConfigFileFinder
'filters' => [
'pre-key' => $this->base,
'key' => $pattern,
'value' => function (\RecursiveDirectoryIterator $file) use ($path) {
'value' => function (RecursiveDirectoryIterator $file) use ($path) {
return ['file' => "{$path}/{$file->getSubPathname()}", 'modified' => $file->getMTime()];
}
],
@@ -187,7 +199,7 @@ class ConfigFileFinder
* Detects all directories with the lookup file and returns them with last modification time.
*
* @param string $folder Location to look up from.
* @param string $lookup Filename to be located (defaults to directory name).
* @param string|null $lookup Filename to be located (defaults to directory name).
* @return array
* @internal
*/
@@ -200,9 +212,7 @@ class ConfigFileFinder
$list = [];
if (is_dir($folder)) {
$iterator = new \DirectoryIterator($folder);
/** @var \DirectoryIterator $directory */
$iterator = new DirectoryIterator($folder);
foreach ($iterator as $directory) {
if (!$directory->isDir() || $directory->isDot()) {
continue;
@@ -244,7 +254,7 @@ class ConfigFileFinder
'filters' => [
'pre-key' => $this->base,
'key' => $pattern,
'value' => function (\RecursiveDirectoryIterator $file) use ($path) {
'value' => function (RecursiveDirectoryIterator $file) use ($path) {
return ["{$path}/{$file->getSubPathname()}" => $file->getMTime()];
}
],
+36 -12
View File
@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@@ -12,24 +12,25 @@ namespace Grav\Common\Config;
use Grav\Common\Data\Data;
use Grav\Common\Utils;
/**
* Class Languages
* @package Grav\Common\Config
*/
class Languages extends Data
{
/**
* @var string|null
*/
/** @var string|null */
protected $checksum;
/**
* @var string|null
*/
protected $modified;
/** @var bool */
protected $modified = false;
/** @var int */
protected $timestamp = 0;
/**
* @var string|null
* @param string|null $checksum
* @return string|null
*/
protected $timestamp;
public function checksum($checksum = null)
{
if ($checksum !== null) {
@@ -39,6 +40,10 @@ class Languages extends Data
return $this->checksum;
}
/**
* @param bool|null $modified
* @return bool
*/
public function modified($modified = null)
{
if ($modified !== null) {
@@ -48,6 +53,10 @@ class Languages extends Data
return $this->modified;
}
/**
* @param int|null $timestamp
* @return int
*/
public function timestamp($timestamp = null)
{
if ($timestamp !== null) {
@@ -57,6 +66,9 @@ class Languages extends Data
return $this->timestamp;
}
/**
* @return void
*/
public function reformat()
{
if (isset($this->items['plugins'])) {
@@ -65,17 +77,29 @@ class Languages extends Data
}
}
/**
* @param array $data
* @return void
*/
public function mergeRecursive(array $data)
{
$this->items = Utils::arrayMergeRecursiveUnique($this->items, $data);
}
/**
* @param string $lang
* @return array
*/
public function flattenByLang($lang)
{
$language = $this->items[$lang];
return Utils::arrayFlattenDotNotation($language);
}
/**
* @param array $array
* @return array
*/
public function unflatten($array)
{
return Utils::arrayUnflattenDotNotation($array);
+155 -64
View File
@@ -3,19 +3,28 @@
/**
* @package Grav\Common\Config
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\Config;
use BadMethodCallException;
use Grav\Common\File\CompiledYamlFile;
use Grav\Common\Data\Data;
use Grav\Common\Utils;
use InvalidArgumentException;
use Pimple\Container;
use Psr\Http\Message\ServerRequestInterface;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use RuntimeException;
use function defined;
use function is_array;
/**
* Class Setup
* @package Grav\Common\Config
*/
class Setup extends Data
{
/**
@@ -28,28 +37,58 @@ class Setup extends Data
];
/**
* @var string Current environment normalized to lower case.
* @var string|null Current environment normalized to lower case.
*/
public static $environment;
/** @var array */
protected $streams = [
'system' => [
'type' => 'ReadOnlyStream',
'prefixes' => [
'' => ['system'],
]
],
'user' => [
'type' => 'ReadOnlyStream',
'force' => true,
'prefixes' => [
'' => ['user'],
'' => [] // Set in constructor
]
],
'cache' => [
'type' => 'Stream',
'force' => true,
'prefixes' => [
'' => [], // Set in constructor
'images' => ['images']
]
],
'log' => [
'type' => 'Stream',
'force' => true,
'prefixes' => [
'' => [] // Set in constructor
]
],
'tmp' => [
'type' => 'Stream',
'force' => true,
'prefixes' => [
'' => [] // Set in constructor
]
],
'backup' => [
'type' => 'Stream',
'force' => true,
'prefixes' => [
'' => [] // Set in constructor
]
],
'environment' => [
'type' => 'ReadOnlyStream'
// If not defined, environment will be set up in the constructor.
],
'system' => [
'type' => 'ReadOnlyStream',
'prefixes' => [
'' => ['system'],
]
],
'asset' => [
'type' => 'Stream',
'prefixes' => [
@@ -59,13 +98,13 @@ class Setup extends Data
'blueprints' => [
'type' => 'ReadOnlyStream',
'prefixes' => [
'' => ['environment://blueprints', 'user://blueprints', 'system/blueprints'],
'' => ['environment://blueprints', 'user://blueprints', 'system://blueprints'],
]
],
'config' => [
'type' => 'ReadOnlyStream',
'prefixes' => [
'' => ['environment://config', 'user://config', 'system/config'],
'' => ['environment://config', 'user://config', 'system://config'],
]
],
'plugins' => [
@@ -89,36 +128,7 @@ class Setup extends Data
'languages' => [
'type' => 'ReadOnlyStream',
'prefixes' => [
'' => ['environment://languages', 'user://languages', 'system/languages'],
]
],
'cache' => [
'type' => 'Stream',
'force' => true,
'prefixes' => [
'' => ['cache'],
'images' => ['images']
]
],
'log' => [
'type' => 'Stream',
'force' => true,
'prefixes' => [
'' => ['logs']
]
],
'backup' => [
'type' => 'Stream',
'force' => true,
'prefixes' => [
'' => ['backup']
]
],
'tmp' => [
'type' => 'Stream',
'force' => true,
'prefixes' => [
'' => ['tmp']
'' => ['environment://languages', 'user://languages', 'system://languages'],
]
],
'image' => [
@@ -153,27 +163,58 @@ class Setup extends Data
*/
public function __construct($container)
{
// Configure main streams.
$abs = str_starts_with(GRAV_SYSTEM_PATH, '/');
$this->streams['system']['prefixes'][''] = $abs ? ['system', GRAV_SYSTEM_PATH] : ['system'];
$this->streams['user']['prefixes'][''] = [GRAV_USER_PATH];
$this->streams['cache']['prefixes'][''] = [GRAV_CACHE_PATH];
$this->streams['log']['prefixes'][''] = [GRAV_LOG_PATH];
$this->streams['tmp']['prefixes'][''] = [GRAV_TMP_PATH];
$this->streams['backup']['prefixes'][''] = [GRAV_BACKUP_PATH];
// If environment is not set, look for the environment variable and then the constant.
$environment = static::$environment ??
(defined('GRAV_ENVIRONMENT') ? GRAV_ENVIRONMENT : (getenv('GRAV_ENVIRONMENT') ?: null));
// If no environment is set, make sure we get one (CLI or hostname).
if (!static::$environment) {
if (\defined('GRAV_CLI')) {
static::$environment = 'cli';
if (null === $environment) {
if (defined('GRAV_CLI')) {
$environment = 'cli';
} else {
/** @var ServerRequestInterface $request */
$request = $container['request'];
$host = $request->getUri()->getHost();
static::$environment = Utils::substrToString($host, ':');
$environment = Utils::substrToString($host, ':');
}
}
// Resolve server aliases to the proper environment.
$environment = $this->environments[static::$environment] ?? static::$environment;
static::$environment = static::$environments[$environment] ?? $environment;
// Pre-load setup.php which contains our initial configuration.
// Configuration may contain dynamic parts, which is why we need to always load it.
// If "GRAV_SETUP_PATH" has been defined, use it, otherwise use defaults.
$file = \defined('GRAV_SETUP_PATH') ? GRAV_SETUP_PATH : GRAV_ROOT . '/setup.php';
$setup = is_file($file) ? (array) include $file : [];
// If GRAV_SETUP_PATH has been defined, use it, otherwise use defaults.
$setupFile = defined('GRAV_SETUP_PATH') ? GRAV_SETUP_PATH : (getenv('GRAV_SETUP_PATH') ?: null);
if (null !== $setupFile) {
// Make sure that the custom setup file exists. Terminates the script if not.
if (!str_starts_with($setupFile, '/')) {
$setupFile = GRAV_WEBROOT . '/' . $setupFile;
}
if (!is_file($setupFile)) {
echo 'GRAV_SETUP_PATH is defined but does not point to existing setup file.';
exit(1);
}
} else {
$setupFile = GRAV_WEBROOT . '/setup.php';
if (!is_file($setupFile)) {
$setupFile = GRAV_WEBROOT . '/' . GRAV_USER_PATH . '/setup.php';
}
if (!is_file($setupFile)) {
$setupFile = null;
}
}
$setup = $setupFile ? (array) include $setupFile : [];
// Add default streams defined in beginning of the class.
if (!isset($setup['streams']['schemes'])) {
@@ -184,19 +225,41 @@ class Setup extends Data
// Initialize class.
parent::__construct($setup);
$this->def('environment', static::$environment);
// Figure out path for the current environment.
$envPath = defined('GRAV_ENVIRONMENT_PATH') ? GRAV_ENVIRONMENT_PATH : (getenv('GRAV_ENVIRONMENT_PATH') ?: null);
if (null === $envPath) {
// Find common path for all environments and append current environment into it.
$envPath = defined('GRAV_ENVIRONMENTS_PATH') ? GRAV_ENVIRONMENTS_PATH : (getenv('GRAV_ENVIRONMENTS_PATH') ?: null);
if (null !== $envPath) {
$envPath .= '/';
} else {
// Use default location. Start with Grav 1.7 default.
$envPath = GRAV_WEBROOT. '/' . GRAV_USER_PATH . '/env';
if (is_dir($envPath)) {
$envPath = 'user://env/';
} else {
// Fallback to Grav 1.6 default.
$envPath = 'user://';
}
}
$envPath .= $this->get('environment');
}
// Set up environment.
$this->def('environment', $environment);
$this->def('streams.schemes.environment.prefixes', ['' => ["user://{$this->get('environment')}"]]);
$this->def('environment', static::$environment);
$this->def('streams.schemes.environment.prefixes', ['' => [$envPath]]);
}
/**
* @return $this
* @throws \RuntimeException
* @throws \InvalidArgumentException
* @throws RuntimeException
* @throws InvalidArgumentException
*/
public function init()
{
$locator = new UniformResourceLocator(GRAV_ROOT);
$locator = new UniformResourceLocator(GRAV_WEBROOT);
$files = [];
$guard = 5;
@@ -220,7 +283,7 @@ class Setup extends Data
} while (--$guard);
if (!$guard) {
throw new \RuntimeException('Setup: Configuration reload loop detected!');
throw new RuntimeException('Setup: Configuration reload loop detected!');
}
// Make sure we have valid setup.
@@ -233,7 +296,8 @@ class Setup extends Data
* Initialize resource locator by using the configuration.
*
* @param UniformResourceLocator $locator
* @throws \BadMethodCallException
* @return void
* @throws BadMethodCallException
*/
public function initializeLocator(UniformResourceLocator $locator)
{
@@ -279,24 +343,51 @@ class Setup extends Data
/**
* @param UniformResourceLocator $locator
* @throws \InvalidArgumentException
* @throws \BadMethodCallException
* @throws \RuntimeException
* @return void
* @throws InvalidArgumentException
* @throws BadMethodCallException
* @throws RuntimeException
*/
protected function check(UniformResourceLocator $locator)
{
$streams = $this->items['streams']['schemes'] ?? null;
if (!\is_array($streams)) {
throw new \InvalidArgumentException('Configuration is missing streams.schemes!');
if (!is_array($streams)) {
throw new InvalidArgumentException('Configuration is missing streams.schemes!');
}
$diff = array_keys(array_diff_key($this->streams, $streams));
if ($diff) {
throw new \InvalidArgumentException(
throw new InvalidArgumentException(
sprintf('Configuration is missing keys %s from streams.schemes!', implode(', ', $diff))
);
}
try {
// If environment is found, remove all missing override locations (B/C compatibility).
if ($locator->findResource('environment://', true)) {
$force = $this->get('streams.schemes.environment.force', false);
if (!$force) {
$prefixes = $this->get('streams.schemes.environment.prefixes.');
$update = false;
foreach ($prefixes as $i => $prefix) {
if ($locator->isStream($prefix)) {
if ($locator->findResource($prefix, true)) {
break;
}
} elseif (file_exists($prefix)) {
break;
}
unset($prefixes[$i]);
$update = true;
}
if ($update) {
$this->set('streams.schemes.environment.prefixes', ['' => array_values($prefixes)]);
$this->initializeLocator($locator);
}
}
}
if (!$locator->findResource('environment://config', true)) {
// If environment does not have its own directory, remove it from the lookup.
$this->set('streams.schemes.environment.prefixes', ['config' => []]);
@@ -314,8 +405,8 @@ class Setup extends Data
$security_file->save();
$security_file->free();
}
} catch (\RuntimeException $e) {
throw new \RuntimeException(sprintf('Grav failed to initialize: %s', $e->getMessage()), 500, $e);
} catch (RuntimeException $e) {
throw new RuntimeException(sprintf('Grav failed to initialize: %s', $e->getMessage()), 500, $e);
}
}
}
+196 -31
View File
@@ -3,7 +3,7 @@
/**
* @package Grav\Common\Data
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
@@ -14,22 +14,44 @@ use Grav\Common\Grav;
use Grav\Common\User\Interfaces\UserInterface;
use RocketTheme\Toolbox\Blueprints\BlueprintForm;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use RuntimeException;
use function call_user_func_array;
use function count;
use function function_exists;
use function in_array;
use function is_array;
use function is_int;
use function is_object;
use function is_string;
use function strlen;
/**
* Class Blueprint
* @package Grav\Common\Data
*/
class Blueprint extends BlueprintForm
{
/** @var string */
protected $context = 'blueprints://';
/** @var string|null */
protected $scope;
/** @var BlueprintSchema */
protected $blueprintSchema;
/** @var array */
/** @var object|null */
protected $object;
/** @var array|null */
protected $defaults;
/** @var array */
protected $handlers = [];
/**
* Clone blueprint.
*/
public function __clone()
{
if ($this->blueprintSchema) {
@@ -37,11 +59,24 @@ class Blueprint extends BlueprintForm
}
}
/**
* @param string $scope
* @return void
*/
public function setScope($scope)
{
$this->scope = $scope;
}
/**
* @param object $object
* @return void
*/
public function setObject($object)
{
$this->object = $object;
}
/**
* Set default values for field types.
*
@@ -57,6 +92,29 @@ class Blueprint extends BlueprintForm
return $this;
}
/**
* @param string $name
* @return array|mixed|null
* @since 1.7
*/
public function getDefaultValue(string $name)
{
$path = explode('.', $name) ?: [];
$current = $this->getDefaults();
foreach ($path as $field) {
if (is_object($current) && isset($current->{$field})) {
$current = $current->{$field};
} elseif (is_array($current) && isset($current[$field])) {
$current = $current[$field];
} else {
return null;
}
}
return $current;
}
/**
* Get nested structure containing default values defined in the blueprints.
*
@@ -88,7 +146,7 @@ class Blueprint extends BlueprintForm
$current = &$this->items;
foreach ($path as $field) {
if (\is_object($current)) {
if (is_object($current)) {
// Handle objects.
if (!isset($current->{$field})) {
$current->{$field} = [];
@@ -97,7 +155,7 @@ class Blueprint extends BlueprintForm
$current = &$current->{$field};
} else {
// Handle arrays and scalars.
if (!\is_array($current)) {
if (!is_array($current)) {
$current = [$field => []];
} elseif (!isset($current[$field])) {
$current[$field] = [];
@@ -111,6 +169,7 @@ class Blueprint extends BlueprintForm
foreach ($data as $property => $call) {
$action = $call['action'];
$method = 'dynamic' . ucfirst($action);
$call['object'] = $this->object;
if (isset($this->handlers[$action])) {
$callable = $this->handlers[$action];
@@ -124,12 +183,44 @@ class Blueprint extends BlueprintForm
return $this;
}
/**
* Extend blueprint with another blueprint.
*
* @param BlueprintForm|array $extends
* @param bool $append
* @return $this
*/
public function extend($extends, $append = false)
{
parent::extend($extends, $append);
$this->deepInit($this->items);
return $this;
}
/**
* @param string $name
* @param mixed $value
* @param string $separator
* @param bool $append
* @return $this
*/
public function embed($name, $value, $separator = '/', $append = false)
{
parent::embed($name, $value, $separator, $append);
$this->deepInit($this->items);
return $this;
}
/**
* Merge two arrays by using blueprints.
*
* @param array $data1
* @param array $data2
* @param string $name Optional
* @param string|null $name Optional
* @param string $separator Optional
* @return array
*/
@@ -172,13 +263,15 @@ class Blueprint extends BlueprintForm
* Validate data against blueprints.
*
* @param array $data
* @throws \RuntimeException
* @param array $options
* @return void
* @throws RuntimeException
*/
public function validate(array $data)
public function validate(array $data, array $options = [])
{
$this->initInternals();
$this->blueprintSchema->validate($data);
$this->blueprintSchema->validate($data, $options);
}
/**
@@ -193,7 +286,7 @@ class Blueprint extends BlueprintForm
{
$this->initInternals();
return $this->blueprintSchema->filter($data, $missingValuesAsNull, $keepEmptyValues);
return $this->blueprintSchema->filter($data, $missingValuesAsNull, $keepEmptyValues) ?? [];
}
@@ -201,13 +294,14 @@ class Blueprint extends BlueprintForm
* Flatten data by using blueprints.
*
* @param array $data
* @param bool $includeAll
* @return array
*/
public function flattenData(array $data)
public function flattenData(array $data, bool $includeAll = false)
{
$this->initInternals();
return $this->blueprintSchema->flattenData($data);
return $this->blueprintSchema->flattenData($data, $includeAll);
}
@@ -223,6 +317,11 @@ class Blueprint extends BlueprintForm
return $this->blueprintSchema;
}
/**
* @param string $name
* @param callable $callable
* @return void
*/
public function addDynamicHandler(string $name, callable $callable): void
{
$this->handlers[$name] = $callable;
@@ -230,6 +329,8 @@ class Blueprint extends BlueprintForm
/**
* Initialize validator.
*
* @return void
*/
protected function initInternals()
{
@@ -250,12 +351,12 @@ class Blueprint extends BlueprintForm
/**
* @param string $filename
* @return string
* @return array
*/
protected function loadFile($filename)
{
$file = CompiledYamlFile::instance($filename);
$content = $file->content();
$content = (array)$file->content();
$file->free();
return $content;
@@ -263,7 +364,7 @@ class Blueprint extends BlueprintForm
/**
* @param string|array $path
* @param string $context
* @param string|null $context
* @return array
*/
protected function getFiles($path, $context = null)
@@ -271,16 +372,24 @@ class Blueprint extends BlueprintForm
/** @var UniformResourceLocator $locator */
$locator = Grav::instance()['locator'];
if (\is_string($path) && !$locator->isStream($path)) {
if (is_string($path) && !$locator->isStream($path)) {
if (is_file($path)) {
return [$path];
}
// Find path overrides.
$paths = (array) ($this->overrides[$path] ?? null);
if (null === $context) {
$paths = (array) ($this->overrides[$path] ?? null);
} else {
$paths = [];
}
// Add path pointing to default context.
if ($context === null) {
$context = $this->context;
}
if ($context && $context[\strlen($context)-1] !== '/') {
if ($context && $context[strlen($context)-1] !== '/') {
$context .= '/';
}
@@ -297,7 +406,7 @@ class Blueprint extends BlueprintForm
$files = [];
foreach ($paths as $lookup) {
if (\is_string($lookup) && strpos($lookup, '://')) {
if (is_string($lookup) && strpos($lookup, '://')) {
$files = array_merge($files, $locator->findResources($lookup));
} else {
$files[] = $lookup;
@@ -311,12 +420,13 @@ class Blueprint extends BlueprintForm
* @param array $field
* @param string $property
* @param array $call
* @return void
*/
protected function dynamicData(array &$field, $property, array &$call)
{
$params = $call['params'];
if (\is_array($params)) {
if (is_array($params)) {
$function = array_shift($params);
} else {
$function = $params;
@@ -327,18 +437,18 @@ class Blueprint extends BlueprintForm
$data = null;
if (!$f) {
if (\function_exists($o)) {
$data = \call_user_func_array($o, $params);
if (function_exists($o)) {
$data = call_user_func_array($o, $params);
}
} else {
if (method_exists($o, $f)) {
$data = \call_user_func_array([$o, $f], $params);
$data = call_user_func_array([$o, $f], $params);
}
}
// If function returns a value,
if (null !== $data) {
if (\is_array($data) && isset($field[$property]) && \is_array($field[$property])) {
if (is_array($data) && isset($field[$property]) && is_array($field[$property])) {
// Combine field and @data-field together.
$field[$property] += $data;
} else {
@@ -352,15 +462,33 @@ class Blueprint extends BlueprintForm
* @param array $field
* @param string $property
* @param array $call
* @return void
*/
protected function dynamicConfig(array &$field, $property, array &$call)
{
$value = $call['params'];
$params = $call['params'];
if (is_array($params)) {
$value = array_shift($params);
$params = array_shift($params);
} else {
$value = $params;
$params = [];
}
$default = $field[$property] ?? null;
$config = Grav::instance()['config']->get($value, $default);
if (!empty($field['value_only'])) {
$config = array_combine($config, $config);
}
if (null !== $config) {
$field[$property] = $config;
if (!empty($params['append']) && is_array($config) && isset($field[$property]) && is_array($field[$property])) {
// Combine field and @config-field together.
$field[$property] += $config;
} else {
// Or create/replace field with @config-field.
$field[$property] = $config;
}
}
}
@@ -368,6 +496,7 @@ class Blueprint extends BlueprintForm
* @param array $field
* @param string $property
* @param array $call
* @return void
*/
protected function dynamicSecurity(array &$field, $property, array &$call)
{
@@ -380,18 +509,48 @@ class Blueprint extends BlueprintForm
/** @var UserInterface|null $user */
$user = $grav['user'] ?? null;
foreach ($actions as $action) {
if (!$user || !$user->authorize($action)) {
$this->addPropertyRecursive($field, 'validate', ['ignore' => true]);
return;
$success = null !== $user;
if ($success) {
$success = $this->resolveActions($user, $actions);
}
if (!$success) {
$this->addPropertyRecursive($field, 'validate', ['ignore' => true]);
}
}
/**
* @param UserInterface|null $user
* @param array $actions
* @param string $op
* @return bool
*/
protected function resolveActions(?UserInterface $user, array $actions, string $op = 'and')
{
if (null === $user) {
return false;
}
$c = $i = count($actions);
foreach ($actions as $key => $action) {
if (!is_int($key) && is_array($actions)) {
$i -= $this->resolveActions($user, $action, $key);
} elseif ($user->authorize($action)) {
$i--;
}
}
if ($op === 'and') {
return $i === 0;
}
return $c !== $i;
}
/**
* @param array $field
* @param string $property
* @param array $call
* @return void
*/
protected function dynamicScope(array &$field, $property, array &$call)
{
@@ -400,7 +559,7 @@ class Blueprint extends BlueprintForm
}
$scopes = (array)$call['params'];
$matches = \in_array($this->scope, $scopes, true);
$matches = in_array($this->scope, $scopes, true);
if ($this->scope && $property !== 'ignore') {
$matches = !$matches;
}
@@ -411,9 +570,15 @@ class Blueprint extends BlueprintForm
}
}
/**
* @param array $field
* @param string $property
* @param mixed $value
* @return void
*/
protected function addPropertyRecursive(array &$field, $property, $value)
{
if (\is_array($value) && isset($field[$property]) && \is_array($field[$property])) {
if (is_array($value) && isset($field[$property]) && is_array($field[$property])) {
$field[$property] = array_merge_recursive($field[$property], $value);
} else {
$field[$property] = $value;
+141 -51
View File
@@ -3,21 +3,33 @@
/**
* @package Grav\Common\Data
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\Data;
use Grav\Common\Config\Config;
use Grav\Common\Grav;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
use RocketTheme\Toolbox\Blueprints\BlueprintSchema as BlueprintSchemaBase;
use RuntimeException;
use function is_array;
use function is_string;
/**
* Class BlueprintSchema
* @package Grav\Common\Data
*/
class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
{
use Export;
/** @var array */
protected $filter = ['validation' => true, 'xss_check' => true];
/** @var array */
protected $ignoreFormKeys = [
'title' => true,
'help' => true,
@@ -48,14 +60,16 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
* Validate data against blueprints.
*
* @param array $data
* @throws \RuntimeException
* @param array $options
* @return void
* @throws RuntimeException
*/
public function validate(array $data)
public function validate(array $data, array $options = [])
{
try {
$messages = $this->validateArray($data, $this->nested);
} catch (\RuntimeException $e) {
$validation = $this->items['']['form']['validation'] ?? 'loose';
$messages = $this->validateArray($data, $this->nested, $validation === 'strict', $options['xss_check'] ?? true);
} catch (RuntimeException $e) {
throw (new ValidationException($e->getMessage(), $e->getCode(), $e))->setMessages();
}
@@ -71,7 +85,7 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
*/
public function processForm(array $data, array $toggles = [])
{
return $this->processFormRecursive($data, $toggles, $this->nested);
return $this->processFormRecursive($data, $toggles, $this->nested) ?? [];
}
/**
@@ -84,18 +98,31 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
*/
public function filter(array $data, $missingValuesAsNull = false, $keepEmptyValues = false)
{
return $this->filterArray($data, $this->nested, $missingValuesAsNull, $keepEmptyValues);
$this->buildIgnoreNested($this->nested);
return $this->filterArray($data, $this->nested, '', $missingValuesAsNull, $keepEmptyValues) ?? [];
}
/**
* Flatten data by using blueprints.
*
* @param array $data Data to be flattened.
* @param bool $includeAll
* @return array
*/
public function flattenData(array $data)
public function flattenData(array $data, bool $includeAll = false)
{
return $this->flattenArray($data, $this->nested, '');
$list = [];
if ($includeAll) {
foreach ($this->items as $key => $rules) {
$type = $rules['type'] ?? '';
if (!str_starts_with($type, '_') && !str_contains($key, '*')) {
$list[$key] = null;
}
}
}
return array_replace($list, $this->flattenArray($data, $this->nested, ''));
}
/**
@@ -123,22 +150,26 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
$array[$prefix.$key] = $field;
}
}
return $array;
}
/**
* @param array $data
* @param array $rules
* @param bool $strict
* @param bool $xss
* @return array
* @throws \RuntimeException
* @throws RuntimeException
*/
protected function validateArray(array $data, array $rules)
protected function validateArray(array $data, array $rules, bool $strict, bool $xss = true)
{
$messages = $this->checkRequired($data, $rules);
foreach ($data as $key => $child) {
$val = $rules[$key] ?? $rules['*'] ?? null;
$rule = \is_string($val) ? $this->items[$val] : null;
$rule = is_string($val) ? $this->items[$val] : null;
$checkXss = $xss;
if ($rule) {
// Item has been defined in blueprints.
@@ -148,12 +179,25 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
}
$messages += Validation::validate($child, $rule);
} elseif (\is_array($child) && \is_array($val)) {
} elseif (is_array($child) && is_array($val)) {
// Array has been defined in blueprints.
$messages += $this->validateArray($child, $val);
} elseif (isset($rules['validation']) && $rules['validation'] === 'strict') {
// Undefined/extra item.
throw new \RuntimeException(sprintf('%s is not defined in blueprints', $key));
$messages += $this->validateArray($child, $val, $strict);
$checkXss = false;
} elseif ($strict) {
// Undefined/extra item in strict mode.
/** @var Config $config */
$config = Grav::instance()['config'];
if (!$config->get('system.strict_mode.blueprint_strict_compat', true)) {
throw new RuntimeException(sprintf('%s is not defined in blueprints', $key));
}
user_error(sprintf('Having extra key %s in your data is deprecated with blueprint having \'validation: strict\'', $key), E_USER_DEPRECATED);
}
if ($checkXss) {
$messages += Validation::checkSafety($child, $rule ?: ['name' => $key]);
}
}
@@ -163,51 +207,55 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
/**
* @param array $data
* @param array $rules
* @param string $parent
* @param bool $missingValuesAsNull
* @param bool $keepEmptyValues
* @return array
* @return array|null
*/
protected function filterArray(array $data, array $rules, $missingValuesAsNull, $keepEmptyValues)
protected function filterArray(array $data, array $rules, string $parent, bool $missingValuesAsNull, bool $keepEmptyValues)
{
$results = [];
if ($missingValuesAsNull) {
// First pass is to fill up all the fields with null. This is done to lock the ordering of the fields.
foreach ($rules as $key => $rule) {
if ($key && !isset($results[$key])) {
$val = $rules[$key] ?? $rules['*'] ?? null;
$rule = \is_string($val) ? $this->items[$val] : null;
if (empty($rule['disabled']) && empty($rule['validate']['ignore'])) {
continue;
}
}
}
}
foreach ($data as $key => $field) {
$val = $rules[$key] ?? $rules['*'] ?? null;
$rule = \is_string($val) ? $this->items[$val] : null;
$rule = is_string($val) ? $this->items[$val] : $this->items[$parent . $key] ?? null;
if ($rule) {
// Item has been defined in blueprints.
if (!empty($rule['disabled']) || !empty($rule['validate']['ignore'])) {
// Skip any data in the ignored field.
if (!empty($rule['disabled']) || !empty($rule['validate']['ignore'])) {
// Skip any data in the ignored field.
unset($results[$key]);
continue;
}
if (null === $field) {
if ($missingValuesAsNull) {
$results[$key] = null;
} else {
unset($results[$key]);
}
continue;
}
$isParent = isset($val['*']);
$type = $rule['type'] ?? null;
if (!$isParent && $type && $type !== '_parent') {
$field = Validation::filter($field, $rule);
} elseif (is_array($field) && is_array($val)) {
// Array has been defined in blueprints.
$k = $isParent ? '*' : $key;
$field = $this->filterArray($field, $val, $parent . $k . '.', $missingValuesAsNull, $keepEmptyValues);
if (null === $field) {
// Nested parent has no values.
unset($results[$key]);
continue;
}
$field = Validation::filter($field, $rule);
} elseif (\is_array($field) && \is_array($val)) {
// Array has been defined in blueprints.
$field = $this->filterArray($field, $val, $missingValuesAsNull, $keepEmptyValues);
} elseif (isset($rules['validation']) && $rules['validation'] === 'strict') {
// Skip any extra data.
continue;
}
if ($keepEmptyValues || (null !== $field && (!\is_array($field) || !empty($field)))) {
if ($keepEmptyValues || (null !== $field && (!is_array($field) || !empty($field)))) {
$results[$key] = $field;
}
}
@@ -215,6 +263,31 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
return $results ?: null;
}
/**
* @param array $nested
* @param string $parent
* @return bool
*/
protected function buildIgnoreNested(array $nested, $parent = '')
{
$ignore = true;
foreach ($nested as $key => $val) {
$key = $parent . $key;
if (is_array($val)) {
$ignore = $this->buildIgnoreNested($val, $key . '.') && $ignore; // Keep the order!
} else {
$child = $this->items[$key] ?? null;
$ignore = $ignore && (!$child || !empty($child['disabled']) || !empty($child['validate']['ignore']));
}
}
if ($ignore) {
$key = trim($parent, '.');
$this->items[$key]['validate']['ignore'] = true;
}
return $ignore;
}
/**
* @param array|null $data
* @param array $toggles
@@ -232,8 +305,19 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
continue;
}
if (is_array($value)) {
// Special toggle handling for all the nested data.
$toggle = $toggles[$key] ?? [];
if (!is_array($toggle)) {
if (!$toggle) {
$data[$key] = null;
continue;
}
$toggle = [];
}
// Recursively fetch the items.
$data[$key] = $this->processFormRecursive($data[$key] ?? null, $toggles[$key] ?? [], $value);
$data[$key] = $this->processFormRecursive($data[$key] ?? null, $toggle, $value);
} else {
$field = $this->get($value);
// Do not add the field if:
@@ -244,8 +328,8 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
|| !empty($field['disabled'])
// Field validation is set to be ignored
|| !empty($field['validate']['ignore'])
// Field is toggleable and the toggle is turned off
|| (!empty($field['toggleable']) && empty($toggles[$key]))
// Field is overridable and the toggle is turned off
|| (!empty($field['overridable']) && empty($toggles[$key]))
) {
continue;
}
@@ -268,7 +352,7 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
$messages = [];
foreach ($fields as $name => $field) {
if (!\is_string($field)) {
if (!is_string($field)) {
continue;
}
@@ -279,10 +363,15 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
continue;
}
// Skip overridable fields without value.
// TODO: We need better overridable support, which is not just ignoring required values but also looking if defaults are good.
if (!empty($field['overridable']) && !isset($data[$name])) {
continue;
}
// Check if required.
if (isset($field['validate']['required'])
&& $field['validate']['required'] === true) {
if (isset($data[$name])) {
continue;
}
@@ -304,6 +393,7 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface
* @param array $field
* @param string $property
* @param array $call
* @return void
*/
protected function dynamicConfig(array &$field, $property, array &$call)
{
+13 -6
View File
@@ -3,15 +3,23 @@
/**
* @package Grav\Common\Data
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\Data;
use DirectoryIterator;
use Grav\Common\Grav;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use RuntimeException;
use function is_array;
use function is_object;
/**
* Class Blueprints
* @package Grav\Common\Data
*/
class Blueprints
{
/** @var array|string */
@@ -34,7 +42,7 @@ class Blueprints
*
* @param string $type Blueprint type.
* @return Blueprint
* @throws \RuntimeException
* @throws RuntimeException
*/
public function get($type)
{
@@ -65,10 +73,9 @@ class Blueprints
if ($locator->isStream($this->search)) {
$iterator = $locator->getIterator($this->search);
} else {
$iterator = new \DirectoryIterator($this->search);
$iterator = new DirectoryIterator($this->search);
}
/** @var \DirectoryIterator $file */
foreach ($iterator as $file) {
if (!$file->isFile() || '.' . $file->getExtension() !== YAML_EXT) {
continue;
@@ -92,7 +99,7 @@ class Blueprints
{
$blueprint = new Blueprint($name);
if (\is_array($this->search) || \is_object($this->search)) {
if (is_array($this->search) || is_object($this->search)) {
// Page types.
$blueprint->setOverrides($this->search);
$blueprint->setContext('blueprints://pages');
@@ -102,7 +109,7 @@ class Blueprints
try {
$blueprint->load()->init();
} catch (\RuntimeException $e) {
} catch (RuntimeException $e) {
$log = Grav::instance()['log'];
$log->error(sprintf('Blueprint %s cannot be loaded: %s', $name, $e->getMessage()));
+46 -31
View File
@@ -3,49 +3,58 @@
/**
* @package Grav\Common\Data
*
* @copyright Copyright (C) 2015 - 2019 Trilby Media, LLC. All rights reserved.
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
* @license MIT License; see LICENSE file for details.
*/
namespace Grav\Common\Data;
use ArrayAccess;
use Exception;
use JsonSerializable;
use RocketTheme\Toolbox\ArrayTraits\Countable;
use RocketTheme\Toolbox\ArrayTraits\Export;
use RocketTheme\Toolbox\ArrayTraits\ExportInterface;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccessWithGetters;
use RocketTheme\Toolbox\File\File;
use RocketTheme\Toolbox\File\FileInterface;
use RuntimeException;
use function func_get_args;
use function is_array;
use function is_callable;
use function is_object;
class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable, ExportInterface
/**
* Class Data
* @package Grav\Common\Data
*/
class Data implements DataInterface, ArrayAccess, \Countable, JsonSerializable, ExportInterface
{
use NestedArrayAccessWithGetters, Countable, Export;
/** @var string */
protected $gettersVariable = 'items';
/** @var array */
protected $items;
/** @var Blueprint */
/** @var Blueprint|callable|null */
protected $blueprints;
/** @var File */
/** @var FileInterface|null */
protected $storage;
/** @var bool */
private $missingValuesAsNull = false;
/** @var bool */
private $keepEmptyValues = true;
/**
* @param array $items
* @param Blueprint|callable $blueprints
* @param Blueprint|callable|null $blueprints
*/
public function __construct(array $items = [], $blueprints = null)
{
$this->items = $items;
$this->blueprints = $blueprints;
if (null !== $blueprints) {
$this->blueprints = $blueprints;
}
}
/**
@@ -92,20 +101,20 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
* @param mixed $value Value to be joined.
* @param string $separator Separator, defaults to '.'
* @return $this
* @throws \RuntimeException
* @throws RuntimeException
*/
public function join($name, $value, $separator = '.')
{
$old = $this->get($name, null, $separator);
if ($old !== null) {
if (!\is_array($old)) {
throw new \RuntimeException('Value ' . $old);
if (!is_array($old)) {
throw new RuntimeException('Value ' . $old);
}
if (\is_object($value)) {
if (is_object($value)) {
$value = (array) $value;
} elseif (!\is_array($value)) {
throw new \RuntimeException('Value ' . $value);
} elseif (!is_array($value)) {
throw new RuntimeException('Value ' . $value);
}
$value = $this->blueprints()->mergeData($old, $value, $name, $separator);
@@ -138,7 +147,7 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
*/
public function joinDefaults($name, $value, $separator = '.')
{
if (\is_object($value)) {
if (is_object($value)) {
$value = (array) $value;
}
@@ -159,14 +168,14 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
* @param array|object $value Value to be joined.
* @param string $separator Separator, defaults to '.'
* @return array
* @throws \RuntimeException
* @throws RuntimeException
*/
public function getJoined($name, $value, $separator = '.')
{
if (\is_object($value)) {
if (is_object($value)) {
$value = (array) $value;
} elseif (!\is_array($value)) {
throw new \RuntimeException('Value ' . $value);
} elseif (!is_array($value)) {
throw new RuntimeException('Value ' . $value);
}
$old = $this->get($name, null, $separator);
@@ -176,8 +185,8 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
return $value;
}
if (!\is_array($old)) {
throw new \RuntimeException('Value ' . $old);
if (!is_array($old)) {
throw new RuntimeException('Value ' . $old);
}
// Return joined data.
@@ -215,7 +224,7 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
* Validate by blueprints.
*
* @return $this
* @throws \Exception
* @throws Exception
*/
public function validate()
{
@@ -255,19 +264,22 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
*/
public function blueprints()
{
if (!$this->blueprints){
$this->blueprints = new Blueprint;
} elseif (\is_callable($this->blueprints)) {
if (!$this->blueprints) {
$this->blueprints = new Blueprint();
} elseif (is_callable($this->blueprints)) {
// Lazy load blueprints.
$blueprints = $this->blueprints;
$this->blueprints = $blueprints();
}
return $this->blueprints;
}
/**
* Save data if storage has been defined.
* @throws \RuntimeException
*
* @return void
* @throws RuntimeException
*/
public function save()
{
@@ -308,8 +320,8 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
/**
* Set or get the data storage.
*
* @param FileInterface $storage Optionally enter a new storage.
* @return FileInterface
* @param FileInterface|null $storage Optionally enter a new storage.
* @return FileInterface|null
*/
public function file(FileInterface $storage = null)
{
@@ -320,6 +332,9 @@ class Data implements DataInterface, \ArrayAccess, \Countable, \JsonSerializable
return $this->storage;
}
/**
* @return array
*/
public function jsonSerialize()
{
return $this->items;

Some files were not shown because too many files have changed in this diff Show More