Merge branch '1.1' of https://github.com/merdiano/orient-site into 1.1
This commit is contained in:
commit
d6fe725919
|
|
@ -42,7 +42,7 @@ return [
|
|||
*/
|
||||
|
||||
'url' => env('APP_URL', 'http://localhost'),
|
||||
|
||||
'cdn' => env('CDN_URL','http://cdn.orient.tm'),
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Application Timezone
|
||||
|
|
@ -81,7 +81,7 @@ return [
|
|||
|
|
||||
*/
|
||||
|
||||
'locale' => 'en',
|
||||
'locale' => 'ru',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ return [
|
|||
|
|
||||
*/
|
||||
|
||||
'backendTimezone' => 'UTC',
|
||||
'backendTimezone' => 'Asia/Ashgabat',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
@ -396,7 +396,7 @@ return [
|
|||
|
|
||||
*/
|
||||
|
||||
'enableCsrfProtection' => env('ENABLE_CSRF', false),
|
||||
'enableCsrfProtection' => env('ENABLE_CSRF', true),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ return [
|
|||
|
|
||||
*/
|
||||
|
||||
'default' => env('LOG_CHANNEL', 'single'),
|
||||
'default' => env('LOG_CHANNEL', 'daily'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ return [
|
|||
|
|
||||
*/
|
||||
|
||||
'cookie' => 'october_session',
|
||||
'cookie' => 'orient_session',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -234,8 +234,8 @@ class UpdateManager
|
|||
|
||||
$params = [
|
||||
'core' => $this->getHash(),
|
||||
'plugins' => serialize($versions),
|
||||
'themes' => serialize($themes),
|
||||
'plugins' => base64_encode(json_encode($versions)),
|
||||
'themes' => base64_encode(json_encode($themes)),
|
||||
'build' => $build,
|
||||
'force' => $force
|
||||
];
|
||||
|
|
@ -590,8 +590,9 @@ class UpdateManager
|
|||
{
|
||||
$fileCode = $name . $hash;
|
||||
$filePath = $this->getFilePath($fileCode);
|
||||
$innerPath = str_replace('.', '/', strtolower($name));
|
||||
|
||||
if (!Zip::extract($filePath, plugins_path())) {
|
||||
if (!Zip::extract($filePath, plugins_path($innerPath))) {
|
||||
throw new ApplicationException(Lang::get('system::lang.zip.extract_failed', ['file' => $filePath]));
|
||||
}
|
||||
|
||||
|
|
@ -632,8 +633,9 @@ class UpdateManager
|
|||
{
|
||||
$fileCode = $name . $hash;
|
||||
$filePath = $this->getFilePath($fileCode);
|
||||
$innerPath = str_replace('.', '-', strtolower($name));
|
||||
|
||||
if (!Zip::extract($filePath, themes_path())) {
|
||||
if (!Zip::extract($filePath, themes_path($innerPath))) {
|
||||
throw new ApplicationException(Lang::get('system::lang.zip.extract_failed', ['file' => $filePath]));
|
||||
}
|
||||
|
||||
|
|
@ -903,13 +905,16 @@ class UpdateManager
|
|||
$http->toFile($filePath);
|
||||
});
|
||||
|
||||
if ($result->code != 200) {
|
||||
throw new ApplicationException(File::get($filePath));
|
||||
if (in_array($result->code, [301, 302])) {
|
||||
if ($redirectUrl = array_get($result->info, 'redirect_url')) {
|
||||
$result = Http::get($redirectUrl, function ($http) use ($postData, $filePath) {
|
||||
$http->toFile($filePath);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (md5_file($filePath) != $expectedHash) {
|
||||
@unlink($filePath);
|
||||
throw new ApplicationException(Lang::get('system::lang.server.file_corrupt'));
|
||||
if ($result->code != 200) {
|
||||
throw new ApplicationException(File::get($filePath));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -942,7 +947,7 @@ class UpdateManager
|
|||
*/
|
||||
protected function createServerUrl($uri)
|
||||
{
|
||||
$gateway = Config::get('cms.updateServer', 'http://gateway.octobercms.com/api');
|
||||
$gateway = Config::get('cms.updateServer', 'https://gateway.octobercms.com/api');
|
||||
if (substr($gateway, -1) != '/') {
|
||||
$gateway .= '/';
|
||||
}
|
||||
|
|
@ -958,10 +963,10 @@ class UpdateManager
|
|||
*/
|
||||
protected function applyHttpAttributes($http, $postData)
|
||||
{
|
||||
$postData['protocol_version'] = '1.1';
|
||||
$postData['protocol_version'] = '1.2';
|
||||
$postData['client'] = 'october';
|
||||
|
||||
$postData['server'] = base64_encode(serialize([
|
||||
$postData['server'] = base64_encode(json_encode([
|
||||
'php' => PHP_VERSION,
|
||||
'url' => Url::to('/'),
|
||||
'since' => PluginVersion::orderBy('created_at')->value('created_at')
|
||||
|
|
|
|||
|
|
@ -126,6 +126,8 @@ class VersionManager
|
|||
*/
|
||||
protected function applyPluginUpdate($code, $version, $details)
|
||||
{
|
||||
$version = $this->normalizeVersion($version);
|
||||
|
||||
list($comments, $scripts) = $this->extractScriptsAndComments($details);
|
||||
|
||||
/*
|
||||
|
|
@ -291,13 +293,18 @@ class VersionManager
|
|||
$versionInfo = [];
|
||||
}
|
||||
|
||||
if ($versionInfo) {
|
||||
uksort($versionInfo, function ($a, $b) {
|
||||
return version_compare($a, $b);
|
||||
});
|
||||
// Sort result
|
||||
uksort($versionInfo, function ($a, $b) {
|
||||
return version_compare($a, $b);
|
||||
});
|
||||
|
||||
$result = [];
|
||||
|
||||
foreach ($versionInfo as $version => $info) {
|
||||
$result[$this->normalizeVersion($version)] = $info;
|
||||
}
|
||||
|
||||
return $this->fileVersions[$code] = $versionInfo;
|
||||
return $this->fileVersions[$code] = $result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -549,6 +556,11 @@ class VersionManager
|
|||
return $this;
|
||||
}
|
||||
|
||||
protected function normalizeVersion($version)
|
||||
{
|
||||
return ltrim((string) $version, 'v');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $details
|
||||
*
|
||||
|
|
|
|||
|
|
@ -18,10 +18,18 @@ final class SecurityPolicy implements SecurityPolicyInterface
|
|||
* @var array List of forbidden methods.
|
||||
*/
|
||||
protected $blockedMethods = [
|
||||
// \October\Rain\Extension\ExtendableTrait
|
||||
'addDynamicMethod',
|
||||
'addDynamicProperty',
|
||||
|
||||
// \October\Rain\Support\Traits\Emitter
|
||||
'bindEvent',
|
||||
'bindEventOnce',
|
||||
|
||||
// Eloquent & Halcyon data modification
|
||||
'insert',
|
||||
'update',
|
||||
'delete',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -65,13 +65,21 @@ class Plugin extends PluginBase
|
|||
|
||||
// extend the post model
|
||||
PostModel::extend(function($model) {
|
||||
$model->belongsToMany['tags'] = [
|
||||
$model->belongsToMany['tags_ru'] = [
|
||||
'Bedard\BlogTags\Models\Tag',
|
||||
'table' => 'bedard_blogtags_post_tag',
|
||||
'order' => 'name'
|
||||
'order' => 'name',
|
||||
'conditions' => 'bedard_blogtags_tags.locale = "ru"'
|
||||
];
|
||||
$model->belongsToMany['tags_en'] = [
|
||||
'Bedard\BlogTags\Models\Tag',
|
||||
'table' => 'bedard_blogtags_post_tag',
|
||||
'order' => 'name',
|
||||
'conditions' => 'bedard_blogtags_tags.locale = "en"'
|
||||
];
|
||||
});
|
||||
|
||||
|
||||
// extend the post form
|
||||
PostsController::extendFormFields(function($form, $model, $context) {
|
||||
if (!$model instanceof PostModel) {
|
||||
|
|
@ -79,9 +87,17 @@ class Plugin extends PluginBase
|
|||
}
|
||||
|
||||
$form->addSecondaryTabFields([
|
||||
'tags' => [
|
||||
'label' => 'bedard.blogtags::lang.form.label',
|
||||
'tags_ru' => [
|
||||
'label' => 'Tags ru',
|
||||
'mode' => 'relation',
|
||||
'dependsOn' => 'locale',
|
||||
'tab' => 'rainlab.blog::lang.post.tab_categories',
|
||||
'type' => 'taglist'
|
||||
],
|
||||
'tags_en' => [
|
||||
'label' => 'Tags en',
|
||||
'mode' => 'relation',
|
||||
'dependsOn' => 'locale',
|
||||
'tab' => 'rainlab.blog::lang.post.tab_categories',
|
||||
'type' => 'taglist'
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<?php namespace Bedard\BlogTags\Models;
|
||||
|
||||
use Config;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Model;
|
||||
use RainLab\Blog\Models\Post;
|
||||
|
||||
|
|
@ -66,6 +67,7 @@ class Tag extends Model
|
|||
protected function setInitialSlug()
|
||||
{
|
||||
$this->slug = str_slug($this->name);
|
||||
$this->locale = input('Post')['locale'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -91,4 +93,6 @@ class Tag extends Model
|
|||
|
||||
return $this->url = $controller->pageUrl($pageName, $params);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
# MIT license
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
<?php namespace Indikator\DevTools;
|
||||
|
||||
use System\Classes\PluginBase;
|
||||
use System\Classes\SettingsManager;
|
||||
use Event;
|
||||
use Backend;
|
||||
use BackendAuth;
|
||||
use BackendMenu;
|
||||
use Indikator\DevTools\Models\Settings as Tools;
|
||||
use Db;
|
||||
|
||||
class Plugin extends PluginBase
|
||||
{
|
||||
public $elevated = true;
|
||||
|
||||
public function pluginDetails()
|
||||
{
|
||||
return [
|
||||
'name' => 'indikator.devtools::lang.plugin.name',
|
||||
'description' => 'indikator.devtools::lang.plugin.description',
|
||||
'author' => 'indikator.devtools::lang.plugin.author',
|
||||
'icon' => 'icon-wrench',
|
||||
'homepage' => 'https://github.com/gergo85/oc-devtools'
|
||||
];
|
||||
}
|
||||
|
||||
public function registerSettings()
|
||||
{
|
||||
return [
|
||||
'devtool' => [
|
||||
'label' => 'indikator.devtools::lang.help.menu_label',
|
||||
'description' => 'indikator.devtools::lang.help.menu_description',
|
||||
'icon' => 'icon-wrench',
|
||||
'class' => 'Indikator\DevTools\Models\Settings',
|
||||
'category' => SettingsManager::CATEGORY_SYSTEM,
|
||||
'permissions' => ['indikator.devtools.settings']
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function registerFormWidgets()
|
||||
{
|
||||
return [
|
||||
'Indikator\DevTools\FormWidgets\Help' => [
|
||||
'label' => 'Help',
|
||||
'code' => 'help'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function registerPermissions()
|
||||
{
|
||||
return [
|
||||
'indikator.devtools.editor' => [
|
||||
'tab' => 'indikator.devtools::lang.plugin.name',
|
||||
'label' => 'indikator.devtools::lang.editor.permission',
|
||||
'order' => 100,
|
||||
'roles' => ['developer']
|
||||
],
|
||||
'indikator.devtools.settings' => [
|
||||
'tab' => 'indikator.devtools::lang.plugin.name',
|
||||
'label' => 'indikator.devtools::lang.help.permission',
|
||||
'order' => 200,
|
||||
'roles' => ['developer']
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function boot()
|
||||
{
|
||||
// Add new menu
|
||||
BackendMenu::registerCallback(function($manager) {
|
||||
$manager->registerMenuItems('Indikator.DevTools', [
|
||||
'editor' => [
|
||||
'label' => 'indikator.devtools::lang.editor.menu_label',
|
||||
'url' => Backend::url('indikator/devtools/editor'),
|
||||
'icon' => 'icon-file-code-o',
|
||||
'iconSvg' => 'plugins/indikator/devtools/assets/images/devtools-icon.svg',
|
||||
'permissions' => ['indikator.devtools.editor'],
|
||||
'order' => 390,
|
||||
|
||||
'sideMenu' => [
|
||||
'assets' => [
|
||||
'label' => 'indikator.devtools::lang.editor.plugins',
|
||||
'icon' => 'icon-cubes',
|
||||
'url' => 'javascript:;',
|
||||
'attributes' => ['data-menu-item' => 'assets'],
|
||||
'counterLabel' => 'cms::lang.asset.unsaved_label',
|
||||
'order' => 100
|
||||
]
|
||||
]
|
||||
]
|
||||
]);
|
||||
});
|
||||
|
||||
// Add new features
|
||||
Event::listen('backend.form.extendFields', function($form)
|
||||
{
|
||||
// Security check
|
||||
if (!BackendAuth::check()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Help docs
|
||||
if ($this->tools_enabled('help') && (get_class($form->config->model) == 'Cms\Classes\Page' || get_class($form->config->model) == 'Cms\Classes\Partial' || get_class($form->config->model) == 'Cms\Classes\Layout') || get_class($form->config->model) == 'Indikator\DevTools\Classes\Asset') {
|
||||
if (get_class($form->config->model) == 'Indikator\DevTools\Classes\Asset') {
|
||||
$content = 'php';
|
||||
}
|
||||
else {
|
||||
$content = 'cms';
|
||||
}
|
||||
|
||||
$form->addSecondaryTabFields([
|
||||
'help' => [
|
||||
'label' => '',
|
||||
'tab' => 'indikator.devtools::lang.help.tab',
|
||||
'type' => 'help',
|
||||
'content' => $content
|
||||
]
|
||||
]);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Wysiwyg editor
|
||||
if ($this->tools_enabled('wysiwyg') && get_class($form->config->model) == 'Cms\Classes\Content') {
|
||||
foreach ($form->getFields() as $field) {
|
||||
if (!empty($field->config['type']) && $field->config['type'] == 'codeeditor') {
|
||||
$field->config['type'] = $field->config['widget'] = 'richeditor';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public function tools_enabled($name)
|
||||
{
|
||||
// Security check
|
||||
if ($name != 'help' && $name != 'wysiwyg') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is enabled
|
||||
if (!Tools::get($name.'_enabled', false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// My account
|
||||
$admin = BackendAuth::getUser();
|
||||
|
||||
// Is superuser
|
||||
if (Tools::get($name.'_superuser', false) && $admin->is_superuser == 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Is admin group
|
||||
if (Tools::get($name.'_admingroup', false) > 0 && Db::table('backend_users_groups')->where(['user_id' => $admin->id, 'user_group_id' => Tools::get($name.'_admingroup', false)])->count() == 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Is current user
|
||||
if (Tools::get($name.'_adminid', false) > 0 && $admin->id == Tools::get($name.'_adminid', false)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Finish
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
# Developer Tools Plugin
|
||||
It is a must-have plugin for you, if you use the October's Docs a lot or you want to use the build-in wysiwyg editor on the Content page.
|
||||
|
||||
## Main features
|
||||
* __Edit directly plugins with the online code editor!__
|
||||
* Add a Help tab to the Pages, Partials or Layouts pages.
|
||||
* Replace the code editor to wysiwyg editor on the Content page.
|
||||
* Show the PHP's configuration if you logged as Superuser.
|
||||
* Set a different permissions for the above features.
|
||||
|
||||
## Available languages
|
||||
* en - English
|
||||
* hu - Magyar
|
||||
|
||||
## Location of plugin
|
||||
You can find it in the back-end: __Settings > System > Developer Tools__
|
||||
|
||||
## PHP's configuration
|
||||
If you like to get the PHP's configuration of your server, use the following URL: www.yourwebsite.com/phpinfo
|
||||
|
||||
## Installation
|
||||
1. Go to the __Settings > Updates & Plugins__ page in the Backend.
|
||||
1. Click on the __Install plugins__ button.
|
||||
1. Type the __Developer Tools__ text in the search field.
|
||||
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 512 512" height="512px" id="Слой_1" version="1.1" viewBox="0 0 512 512" width="512px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g id="wrench"><g><path d="M512,256c0,141.391-114.609,255.992-256,255.992C114.609,511.984,0,397.383,0,256.008 c0-141.391,114.609-255.992,256-256C397.391,0.008,512,114.617,512,256z" fill="#FA6E51"/><path d="M302.195,209.852L112.727,399.32l110.516,110.516c10.734,1.367,21.648,2.156,32.758,2.156 c139.391-0.008,252.656-111.398,255.852-250.016L356.742,106.875l-78.773,78.766L302.195,209.852z" fill="#E8573F"/></g><g><path d="M405.531,156.766l-41.086,41.094c-6.703,6.703-23.188,1.086-36.813-12.523 C314,171.703,308.391,155.227,315.109,148.5l0,0l41.633-41.625c-5.984-1.688-12.297-2.57-18.82-2.578 c-38.555,0.008-69.789,31.234-69.789,69.773c0,8.43,1.5,16.523,4.242,23.984L112.914,357.5c-5.32,5.313-8.594,12.672-8.617,20.797 c0,16.234,13.172,29.398,29.406,29.406c8.133,0,15.484-3.281,20.797-8.617l0,0l159.461-159.438 c7.461,2.727,15.539,4.227,23.961,4.219c38.539,0,69.781-31.258,69.781-69.797C407.703,168.109,406.945,162.297,405.531,156.766z" fill="#F4F6F9" id="wrench_3_"/><path d="M122.078,378.328c0,6.422,5.219,11.625,11.625,11.625c6.43,0,11.633-5.203,11.633-11.625 s-5.203-11.633-11.633-11.633C127.297,366.695,122.078,371.906,122.078,378.328z" fill="#CBD0D8"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
|
@ -0,0 +1,706 @@
|
|||
/*
|
||||
* Scripts for the CMS page.
|
||||
*/
|
||||
+function ($) { "use strict";
|
||||
|
||||
var Base = $.oc.foundation.base,
|
||||
BaseProto = Base.prototype
|
||||
|
||||
var CmsPage = function() {
|
||||
|
||||
Base.call(this)
|
||||
|
||||
//
|
||||
// Initialization
|
||||
//
|
||||
|
||||
this.init()
|
||||
}
|
||||
|
||||
CmsPage.prototype = Object.create(BaseProto)
|
||||
CmsPage.prototype.constructor = CmsPage
|
||||
|
||||
CmsPage.prototype.init = function() {
|
||||
$(document).ready(this.proxy(this.registerHandlers))
|
||||
}
|
||||
|
||||
CmsPage.prototype.updateTemplateList = function(type) {
|
||||
var $form = $('#cms-side-panel form[data-template-type='+type+']'),
|
||||
templateList = type + 'List'
|
||||
|
||||
$form.request(templateList + '::onUpdate', {
|
||||
complete: function() {
|
||||
$('button[data-control=delete-template]', $form).trigger('oc.triggerOn.update')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
CmsPage.prototype.registerHandlers = function() {
|
||||
var $document = $(document),
|
||||
$masterTabs = $('#cms-master-tabs')
|
||||
|
||||
$masterTabs.on('closed.oc.tab', this.proxy(this.onTabClosed))
|
||||
$masterTabs.on('beforeClose.oc.tab', this.proxy(this.onBeforeTabClose))
|
||||
$masterTabs.on('oc.beforeRequest', this.proxy(this.onBeforeRequest))
|
||||
$masterTabs.on('shown.bs.tab', this.proxy(this.onTabShown))
|
||||
$masterTabs.on('initTab.oc.tab', this.proxy(this.onInitTab))
|
||||
$masterTabs.on('afterAllClosed.oc.tab', this.proxy(this.onAfterAllTabsClosed))
|
||||
|
||||
$(window).on('ajaxInvalidField', this.proxy(this.ajaxInvalidField))
|
||||
$document.on('open.oc.list', '#cms-side-panel', this.proxy(this.onOpenDocument))
|
||||
$document.on('ajaxUpdate', '[data-control=filelist], [data-control=assetlist]', this.proxy(this.onAjaxUpdate))
|
||||
$document.on('ajaxError', '#cms-master-tabs form', this.proxy(this.onAjaxError))
|
||||
$document.on('ajaxSuccess', '#cms-master-tabs form', this.proxy(this.onAjaxSuccess))
|
||||
$document.on('click', '#cms-side-panel form button[data-control=create-template], #cms-side-panel form li a[data-control=create-template]', this.proxy(this.onCreateTemplateClick))
|
||||
$document.on('click', '#cms-side-panel form button[data-control=delete-template]', this.proxy(this.onDeleteTemplateClick))
|
||||
$document.on('showing.oc.inspector', '[data-inspectable]', this.proxy(this.onInspectorShowing))
|
||||
$document.on('hidden.oc.inspector', '[data-inspectable]', this.proxy(this.onInspectorHidden))
|
||||
$document.on('hiding.oc.inspector', '[data-inspectable]', this.proxy(this.onInspectorHiding))
|
||||
$document.on('click', '#cms-master-tabs > div.tab-content > .tab-pane.active .control-componentlist a.remove', this.proxy(this.onComponentRemove))
|
||||
$document.on('click', '#cms-component-list [data-component]', this.proxy(this.onComponentClick))
|
||||
}
|
||||
|
||||
// EVENT HANDLERS
|
||||
// ============================
|
||||
|
||||
CmsPage.prototype.onOpenDocument = function(event) {
|
||||
/*
|
||||
* Open a document when it's clicked in the sidebar
|
||||
*/
|
||||
|
||||
var $item = $(event.relatedTarget),
|
||||
$form = $item.closest('[data-template-type]'),
|
||||
data = {
|
||||
type: $form.data('template-type'),
|
||||
theme: $item.data('item-theme'),
|
||||
path: $item.data('item-path')
|
||||
},
|
||||
tabId = data.type + '-' + data.theme + '-' + data.path
|
||||
|
||||
if (data.type == 'asset' && $item.data('editable') === undefined)
|
||||
return true
|
||||
|
||||
if ($form.length == 0)
|
||||
return false
|
||||
|
||||
/*
|
||||
* Find if the tab is already opened
|
||||
*/
|
||||
if ($('#cms-master-tabs').data('oc.tab').goTo(tabId))
|
||||
return false
|
||||
|
||||
/*
|
||||
* Open a new tab
|
||||
*/
|
||||
$.oc.stripeLoadIndicator.show()
|
||||
|
||||
$form.request('onOpenTemplate', {
|
||||
data: data
|
||||
}).done(function(data) {
|
||||
$.oc.stripeLoadIndicator.hide()
|
||||
var fileType = data.tabTitle.split('.').pop()
|
||||
if (fileType == 'php' || fileType == 'js' || fileType == 'css' || fileType == 'html' || fileType == 'htm') fileType = 'icon-file-code-o'
|
||||
else fileType = 'icon-file-text-o'
|
||||
$('#cms-master-tabs').ocTab('addTab', data.tabTitle, data.tab, tabId, fileType)
|
||||
}).always(function() {
|
||||
$.oc.stripeLoadIndicator.hide()
|
||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||
alert(jqXHR.responseText.length ? jqXHR.responseText : jqXHR.statusText)
|
||||
$.oc.stripeLoadIndicator.hide()
|
||||
})
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
CmsPage.prototype.ajaxInvalidField = function(ev, element, name, messages, isFirst) {
|
||||
/*
|
||||
* Detect invalid fields, uncollapse the panel
|
||||
*/
|
||||
if (!isFirst)
|
||||
return
|
||||
|
||||
ev.preventDefault()
|
||||
|
||||
var $el = $(element),
|
||||
$panel = $el.closest('.form-tabless-fields.collapsed'),
|
||||
$primaryPanel = $el.closest('.control-tabs.primary-tabs.collapsed')
|
||||
|
||||
if ($panel.length > 0)
|
||||
$panel.removeClass('collapsed')
|
||||
|
||||
if ($primaryPanel.length > 0) {
|
||||
$primaryPanel.removeClass('collapsed')
|
||||
|
||||
var pane = $primaryPanel.closest('.tab-pane'),
|
||||
$secondaryPanel = $('.control-tabs.secondary-tabs', pane)
|
||||
|
||||
$secondaryPanel.removeClass('primary-collapsed')
|
||||
}
|
||||
|
||||
$el.focus()
|
||||
}
|
||||
|
||||
CmsPage.prototype.onTabClosed = function(ev) {
|
||||
this.updateModifiedCounter()
|
||||
|
||||
if ($('> div.tab-content > div.tab-pane', '#cms-master-tabs').length == 0)
|
||||
this.setPageTitle('')
|
||||
}
|
||||
|
||||
CmsPage.prototype.onBeforeTabClose = function(ev) {
|
||||
if ($.fn.table !== undefined)
|
||||
$('[data-control=table]', ev.relatedTarget).table('dispose')
|
||||
|
||||
$.oc.foundation.controlUtils.disposeControls(ev.relatedTarget.get(0))
|
||||
}
|
||||
|
||||
CmsPage.prototype.onBeforeRequest = function(ev) {
|
||||
var $form = $(ev.target)
|
||||
|
||||
if ($('.components .layout-cell.error-component', $form).length > 0) {
|
||||
if (!confirm('The form contains unknown components. Their properties will be lost on save. Do you want to save the form?'))
|
||||
ev.preventDefault()
|
||||
}
|
||||
}
|
||||
|
||||
CmsPage.prototype.onTabShown = function(ev) {
|
||||
/*
|
||||
* Listen for the tabs "shown" event to track the current template in the list
|
||||
*/
|
||||
|
||||
var $target = $(ev.target)
|
||||
|
||||
if ($target.closest('[data-control=tab]').attr('id') != 'cms-master-tabs')
|
||||
return
|
||||
|
||||
var dataId = $target.closest('li').attr('data-tab-id'),
|
||||
title = $target.attr('title'),
|
||||
$sidePanel = $('#cms-side-panel')
|
||||
|
||||
if (title)
|
||||
this.setPageTitle(title)
|
||||
|
||||
$sidePanel.find('[data-control=filelist]').fileList('markActive', dataId)
|
||||
$sidePanel.find('form').trigger('oc.list.setActiveItem', [dataId])
|
||||
}
|
||||
|
||||
CmsPage.prototype.onInitTab = function(ev, data) {
|
||||
/*
|
||||
* Listen for the tabs "initTab" event to inject extra controls to the tab
|
||||
*/
|
||||
|
||||
if ($(ev.target).attr('id') != 'cms-master-tabs')
|
||||
return
|
||||
|
||||
var $collapseIcon = $('<a href="javascript:;" class="tab-collapse-icon tabless"><i class="icon-chevron-up"></i></a>'),
|
||||
$panel = $('.form-tabless-fields', data.pane)
|
||||
|
||||
$panel.append($collapseIcon);
|
||||
|
||||
$collapseIcon.click(function(){
|
||||
$panel.toggleClass('collapsed')
|
||||
|
||||
if (typeof(localStorage) !== 'undefined')
|
||||
localStorage.ocCmsTablessCollapsed = $panel.hasClass('collapsed') ? 1 : 0
|
||||
|
||||
window.setTimeout(function(){
|
||||
$(window).trigger('oc.updateUi')
|
||||
}, 500)
|
||||
|
||||
return false
|
||||
})
|
||||
|
||||
var $primaryCollapseIcon = $('<a href="javascript:;" class="tab-collapse-icon primary"><i class="icon-chevron-down"></i></a>'),
|
||||
$primaryPanel = $('.control-tabs.primary-tabs', data.pane),
|
||||
$secondaryPanel = $('.control-tabs.secondary-tabs', data.pane)
|
||||
|
||||
if ($primaryPanel.length > 0) {
|
||||
$secondaryPanel.append($primaryCollapseIcon);
|
||||
|
||||
$primaryCollapseIcon.click(function(){
|
||||
$primaryPanel.toggleClass('collapsed')
|
||||
$secondaryPanel.toggleClass('primary-collapsed')
|
||||
$(window).trigger('oc.updateUi')
|
||||
if (typeof(localStorage) !== 'undefined')
|
||||
localStorage.ocCmsPrimaryCollapsed = $primaryPanel.hasClass('collapsed') ? 1 : 0
|
||||
return false
|
||||
})
|
||||
}
|
||||
|
||||
if (typeof(localStorage) !== 'undefined') {
|
||||
if (!$('a', data.tab).hasClass('new-template') && localStorage.ocCmsTablessCollapsed == 1)
|
||||
$panel.addClass('collapsed')
|
||||
|
||||
if (localStorage.ocCmsPrimaryCollapsed == 1) {
|
||||
$primaryPanel.addClass('collapsed')
|
||||
$secondaryPanel.addClass('primary-collapsed')
|
||||
}
|
||||
}
|
||||
|
||||
var $componentListFormGroup = $('.control-componentlist', data.pane).closest('.form-group')
|
||||
if ($primaryPanel.length > 0)
|
||||
$primaryPanel.before($componentListFormGroup)
|
||||
else
|
||||
$secondaryPanel.parent().before($componentListFormGroup)
|
||||
|
||||
$componentListFormGroup.removeClass()
|
||||
$componentListFormGroup.addClass('layout-row min-size')
|
||||
this.updateComponentListClass(data.pane)
|
||||
this.updateFormEditorMode(data.pane, true)
|
||||
|
||||
var $form = $('form', data.pane),
|
||||
self = this
|
||||
|
||||
$form.on('changed.oc.changeMonitor', function() {
|
||||
$panel.trigger('modified.oc.tab')
|
||||
self.updateModifiedCounter()
|
||||
})
|
||||
|
||||
$form.on('unchanged.oc.changeMonitor', function() {
|
||||
$panel.trigger('unmodified.oc.tab')
|
||||
self.updateModifiedCounter()
|
||||
})
|
||||
|
||||
this.addTokenExpanderToEditor(data.pane, $form)
|
||||
}
|
||||
|
||||
CmsPage.prototype.onAfterAllTabsClosed = function(ev) {
|
||||
var $sidePanel = $('#cms-side-panel')
|
||||
|
||||
$sidePanel.find('[data-control=filelist]').fileList('markActive', null)
|
||||
$sidePanel.find('form').trigger('oc.list.setActiveItem', [null])
|
||||
}
|
||||
|
||||
CmsPage.prototype.onAjaxUpdate = function(ev) {
|
||||
var dataId = $('#cms-master-tabs .nav-tabs li.active').attr('data-tab-id'),
|
||||
$sidePanel = $('#cms-side-panel')
|
||||
|
||||
$sidePanel.find('[data-control=filelist]').fileList('markActive', dataId)
|
||||
$sidePanel.find('form').trigger('oc.list.setActiveItem', [dataId])
|
||||
}
|
||||
|
||||
CmsPage.prototype.onAjaxSuccess = function(ev, context, data) {
|
||||
var element = ev.target
|
||||
|
||||
if (data.templatePath !== undefined) {
|
||||
$('input[name=templatePath]', element).val(data.templatePath)
|
||||
$('input[name=templateMtime]', element).val(data.templateMtime)
|
||||
$('[data-control=delete-button]', element).removeClass('hide')
|
||||
$('[data-control=preview-button]', element).removeClass('hide')
|
||||
|
||||
if (data.pageUrl !== undefined)
|
||||
$('[data-control=preview-button]', element).attr('href', data.pageUrl)
|
||||
}
|
||||
|
||||
if (data.tabTitle !== undefined) {
|
||||
$('#cms-master-tabs').ocTab('updateTitle', $(element).closest('.tab-pane'), data.tabTitle)
|
||||
this.setPageTitle(data.tabTitle)
|
||||
}
|
||||
|
||||
var tabId = $('input[name=templateType]', element).val() + '-'
|
||||
+ $('input[name=theme]', element).val() + '-'
|
||||
+ $('input[name=templatePath]', element).val();
|
||||
|
||||
$('#cms-master-tabs').ocTab('updateIdentifier', $(element).closest('.tab-pane'), tabId)
|
||||
|
||||
var templateType = $('input[name=templateType]', element).val()
|
||||
if (templateType.length > 0) {
|
||||
$.oc.cmsPage.updateTemplateList(templateType)
|
||||
|
||||
if (templateType == 'layout')
|
||||
this.updateLayouts(element)
|
||||
}
|
||||
|
||||
this.updateFormEditorMode($(element).closest('.tab-pane'), false)
|
||||
|
||||
if (context.handler == 'onSave' && (!data['X_OCTOBER_ERROR_FIELDS'] && !data['X_OCTOBER_ERROR_MESSAGE'])) {
|
||||
$(element).trigger('unchange.oc.changeMonitor')
|
||||
}
|
||||
}
|
||||
|
||||
CmsPage.prototype.onAjaxError = function(ev, context, message, data, jqXHR) {
|
||||
if (context.handler == 'onSave') {
|
||||
if (jqXHR.responseText == 'mtime-mismatch') {
|
||||
ev.preventDefault()
|
||||
this.handleMtimeMismatch(ev.target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CmsPage.prototype.onCreateTemplateClick = function(ev) {
|
||||
var $form = $(ev.target).closest('[data-template-type]'),
|
||||
type = $form.data('template-type'),
|
||||
tabId = type + Math.random(),
|
||||
self = this
|
||||
|
||||
$.oc.stripeLoadIndicator.show()
|
||||
|
||||
$form.request('onCreateTemplate', {
|
||||
data: {type: type}
|
||||
}).done(function(data) {
|
||||
$('#cms-master-tabs').ocTab('addTab', data.tabTitle, data.tab, tabId, $form.data('type-icon') + ' new-template')
|
||||
$('#layout-side-panel').trigger('close.oc.sidePanel')
|
||||
self.setPageTitle(data.tabTitle)
|
||||
}).always(function(){
|
||||
$.oc.stripeLoadIndicator.hide()
|
||||
})
|
||||
}
|
||||
|
||||
CmsPage.prototype.onDeleteTemplateClick = function(ev) {
|
||||
var $el = $(ev.currentTarget),
|
||||
$form = $el.closest('form'),
|
||||
templateType = $form.data('template-type'),
|
||||
self = this
|
||||
|
||||
if (!confirm($el.data('confirmation')))
|
||||
return
|
||||
|
||||
$.oc.stripeLoadIndicator.show()
|
||||
|
||||
$form.request('onDeleteTemplates', {
|
||||
data: {type: templateType}
|
||||
}).done(function(data) {
|
||||
var tabs = $('#cms-master-tabs').data('oc.tab');
|
||||
$.each(data.deleted, function(index, path){
|
||||
var
|
||||
tabId = templateType + '-' + data.theme + '-' + path,
|
||||
tab = tabs.findByIdentifier(tabId)
|
||||
|
||||
$('#cms-master-tabs').ocTab('closeTab', tab, true)
|
||||
})
|
||||
|
||||
if (data.error !== undefined && $.type(data.error) === 'string' && data.error.length)
|
||||
$.oc.flashMsg({text: data.error, 'class': 'error'})
|
||||
}).always(function(){
|
||||
self.updateTemplateList(templateType)
|
||||
$.oc.stripeLoadIndicator.hide()
|
||||
})
|
||||
}
|
||||
|
||||
CmsPage.prototype.onInspectorShowing = function(ev, data) {
|
||||
$(ev.currentTarget).closest('[data-control="toolbar"]').data('oc.dragScroll').goToElement(ev.currentTarget, data.callback)
|
||||
|
||||
ev.stopPropagation()
|
||||
}
|
||||
|
||||
CmsPage.prototype.onInspectorHidden = function(ev) {
|
||||
var element = ev.target,
|
||||
values = $.parseJSON($('[data-inspector-values]', element).val())
|
||||
|
||||
$('[name="component_aliases[]"]', element).val(values['oc.alias'])
|
||||
$('span.alias', element).text(values['oc.alias'])
|
||||
}
|
||||
|
||||
CmsPage.prototype.onInspectorHiding = function(ev, values) {
|
||||
var element = ev.target,
|
||||
values = $.parseJSON($('[data-inspector-values]', element).val()),
|
||||
alias = values['oc.alias'],
|
||||
$componentList = $('#cms-master-tabs > div.tab-content > .tab-pane.active .control-componentlist .layout'),
|
||||
$cell = $(ev.target).parent()
|
||||
|
||||
$('div.layout-cell', $componentList).each(function(){
|
||||
if ($cell.get(0) == this)
|
||||
return true
|
||||
|
||||
var $input = $('input[name="component_aliases[]"]', this)
|
||||
|
||||
if ($input.val() == alias) {
|
||||
ev.preventDefault()
|
||||
alert('The component alias "'+alias+'" is already used.')
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
CmsPage.prototype.onComponentRemove = function(ev) {
|
||||
var element = ev.currentTarget
|
||||
|
||||
$(element).trigger('change')
|
||||
var pane = $(element).closest('.tab-pane'),
|
||||
component = $(element).closest('div.layout-cell')
|
||||
|
||||
/*
|
||||
* Remove any {% component %} tags in the editor for this component
|
||||
*/
|
||||
var editor = $('[data-control=codeeditor]', pane)
|
||||
if (editor.length) {
|
||||
var alias = $('input[name="component_aliases[]"]', component).val(),
|
||||
codeEditor = editor.codeEditor('getEditorObject')
|
||||
|
||||
codeEditor.replace('', {
|
||||
needle: "{% component '" + alias + "' %}"
|
||||
})
|
||||
}
|
||||
|
||||
component.remove()
|
||||
$(window).trigger('oc.updateUi')
|
||||
|
||||
this.updateComponentListClass(pane)
|
||||
return false
|
||||
}
|
||||
|
||||
CmsPage.prototype.onComponentClick = function(ev) {
|
||||
/*
|
||||
* Determine if a page or layout is open in the master tabs
|
||||
*/
|
||||
|
||||
var $componentList = $('#cms-master-tabs > div.tab-content > .tab-pane.active .control-componentlist .layout')
|
||||
if ($componentList.length == 0) {
|
||||
alert('Components can be added only to pages, partials and layouts.')
|
||||
return;
|
||||
}
|
||||
|
||||
var $component = $(ev.currentTarget).clone(),
|
||||
$iconInput = $component.find('[data-component-icon]'),
|
||||
$componentContainer = $('.layout-relative', $component),
|
||||
$configInput = $component.find('[data-inspector-config]'),
|
||||
$aliasInput = $component.find('[data-component-default-alias]'),
|
||||
$valuesInput = $component.find('[data-inspector-values]'),
|
||||
$nameInput = $component.find('[data-component-name]'),
|
||||
$classInput = $component.find('[data-inspector-class]'),
|
||||
alias = $aliasInput.val(),
|
||||
originalAlias = alias,
|
||||
counter = 2,
|
||||
existingAliases = []
|
||||
|
||||
$('div.layout-cell input[name="component_aliases[]"]', $componentList).each(function(){
|
||||
existingAliases.push($(this).val())
|
||||
})
|
||||
|
||||
while($.inArray(alias, existingAliases) !== -1) {
|
||||
alias = originalAlias + counter
|
||||
counter++
|
||||
}
|
||||
|
||||
// Set the last alias used so dragComponents can use it
|
||||
$('input[name="component_aliases[]"]', $(ev.currentTarget)).val(alias)
|
||||
|
||||
$component.attr('data-component-attached', true)
|
||||
$componentContainer.addClass($iconInput.val())
|
||||
$iconInput.remove()
|
||||
|
||||
$componentContainer.attr({
|
||||
'data-inspectable': '',
|
||||
'data-inspector-title': $component.find('span.name').text(),
|
||||
'data-inspector-description': $component.find('span.description').text(),
|
||||
'data-inspector-config': $configInput.val(),
|
||||
'data-inspector-class': $classInput.val()
|
||||
})
|
||||
|
||||
$configInput.remove()
|
||||
$('input[name="component_names[]"]', $component).val($nameInput.val())
|
||||
$nameInput.remove()
|
||||
$('input[name="component_aliases[]"]', $component).val(alias)
|
||||
$component.find('span.alias').text(alias)
|
||||
$valuesInput.val($valuesInput.val().replace('--alias--', alias))
|
||||
$aliasInput.remove()
|
||||
|
||||
$component.addClass('adding')
|
||||
$componentList.append($component)
|
||||
$componentList.closest('[data-control="toolbar"]').data('oc.dragScroll').goToElement($component)
|
||||
$component.removeClass('adding')
|
||||
$component.trigger('change')
|
||||
|
||||
this.updateComponentListClass($component.closest('.tab-pane'))
|
||||
|
||||
$(window).trigger('oc.updateUi')
|
||||
}
|
||||
|
||||
// INTERNAL METHODS
|
||||
// ============================
|
||||
|
||||
CmsPage.prototype.updateComponentListClass = function(pane) {
|
||||
var $componentList = $('.control-componentlist', pane),
|
||||
$primaryPanel = $('.control-tabs.primary-tabs', pane),
|
||||
$primaryTabContainer = $('.nav-tabs', $primaryPanel),
|
||||
hasComponents = $('.layout', $componentList).children(':not(.hidden)').length > 0
|
||||
|
||||
$primaryTabContainer.toggleClass('component-area', hasComponents)
|
||||
$componentList.toggleClass('has-components', hasComponents)
|
||||
}
|
||||
|
||||
CmsPage.prototype.updateFormEditorMode = function(pane, initialization) {
|
||||
var $contentTypeElement = $('[data-toolbar-type]', pane)
|
||||
if ($contentTypeElement.length == 0)
|
||||
return
|
||||
|
||||
if ($contentTypeElement.data('toolbar-type') != 'content')
|
||||
return
|
||||
|
||||
var fileName = $('input[name=fileName]', pane).val(),
|
||||
parts = fileName.split('.'),
|
||||
extension = 'txt',
|
||||
mode = 'plain_text',
|
||||
modes = { css: "css", htm: "html", html: "html", js: "javascript", less: "less", md: "markdown", sass: "sass", scss: "scss", txt: "plain_text", yaml: "yaml", php: "php" },
|
||||
editor = $('[data-control=codeeditor]', pane)
|
||||
|
||||
if (parts.length >= 2)
|
||||
extension = parts.pop().toLowerCase()
|
||||
|
||||
if (modes[extension] !== undefined)
|
||||
mode = modes[extension];
|
||||
|
||||
var setEditorMode = function() {
|
||||
window.setTimeout(function(){
|
||||
editor.data('oc.codeEditor').editor.getSession().setMode({path: 'ace/mode/'+mode})
|
||||
}, 200)
|
||||
}
|
||||
|
||||
if (initialization)
|
||||
editor.on('oc.codeEditorReady', setEditorMode)
|
||||
else
|
||||
setEditorMode()
|
||||
}
|
||||
|
||||
CmsPage.prototype.updateModifiedCounter = function() {
|
||||
var counters = {
|
||||
page: { menu: 'pages', count: 0 },
|
||||
partial: { menu: 'partials', count: 0 },
|
||||
layout: { menu: 'layouts', count: 0 },
|
||||
content: { menu: 'content', count: 0 },
|
||||
asset: { menu: 'assets', count: 0}
|
||||
}
|
||||
|
||||
$('> div.tab-content > div.tab-pane[data-modified]', '#cms-master-tabs').each(function(){
|
||||
var inputType = $('> form > input[name=templateType]', this).val()
|
||||
counters[inputType].count++
|
||||
})
|
||||
|
||||
$.each(counters, function(type, data){
|
||||
$.oc.sideNav.setCounter('cms/' + data.menu, data.count);
|
||||
})
|
||||
}
|
||||
|
||||
CmsPage.prototype.addTokenExpanderToEditor = function(pane, $form) {
|
||||
var group = $('[data-field-name=markup]', pane),
|
||||
editor = $('[data-control=codeeditor]', group),
|
||||
canExpand = false,
|
||||
self = this
|
||||
|
||||
if (!editor.length || editor.data('oc.tokenexpander'))
|
||||
return
|
||||
|
||||
var toolbar = editor.codeEditor('getToolbar')
|
||||
|
||||
editor.tokenExpander()
|
||||
|
||||
var breakButton = $('<li />').prop({ 'class': 'tokenexpander-button' }).append(
|
||||
$('<a />').prop({ 'href': 'javascript:; '}).append(
|
||||
$('<i />').prop({ 'class': 'icon-code-fork' })
|
||||
)
|
||||
)
|
||||
|
||||
breakButton.hide().on('click', function(){
|
||||
self.handleExpandToken(editor, $form)
|
||||
return false
|
||||
})
|
||||
|
||||
$('ul:first', toolbar).prepend(breakButton)
|
||||
|
||||
editor
|
||||
.on('show.oc.tokenexpander', function(){
|
||||
canExpand = true
|
||||
breakButton.show()
|
||||
})
|
||||
.on('hide.oc.tokenexpander', function(){
|
||||
canExpand = false
|
||||
breakButton.hide()
|
||||
})
|
||||
.on('dblclick', function(ev){
|
||||
if ((ev.metaKey || ev.ctrlKey) && canExpand) {
|
||||
self.handleExpandToken(editor, $form)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
CmsPage.prototype.handleExpandToken = function(editor, $form) {
|
||||
editor.tokenExpander('expandToken', function(token, value){
|
||||
return $form.request('onExpandMarkupToken', {
|
||||
data: { tokenType: token, tokenName: value }
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
CmsPage.prototype.handleMtimeMismatch = function(form) {
|
||||
var $form = $(form)
|
||||
$form.popup({ handler: 'onOpenConcurrencyResolveForm' })
|
||||
|
||||
var popup = $form.data('oc.popup'),
|
||||
self = this
|
||||
|
||||
$(popup.$content).on('click', 'button[data-action=reload]', function(){
|
||||
popup.hide()
|
||||
self.reloadForm(form)
|
||||
})
|
||||
|
||||
$(popup.$content).on('click', 'button[data-action=save]', function(){
|
||||
popup.hide()
|
||||
|
||||
$('input[name=templateForceSave]', $form).val(1)
|
||||
$('a[data-request=onSave]', $form).trigger('click')
|
||||
$('input[name=templateForceSave]', $form).val(0)
|
||||
})
|
||||
}
|
||||
|
||||
CmsPage.prototype.reloadForm = function(form) {
|
||||
var
|
||||
$form = $(form),
|
||||
data = {
|
||||
type: $('[name=templateType]', $form).val(),
|
||||
theme: $('[name=theme]', $form).val(),
|
||||
path: $('[name=templatePath]', $form).val(),
|
||||
},
|
||||
tabId = data.type + '-' + data.theme + '-' + data.path,
|
||||
tabs = $('#cms-master-tabs').data('oc.tab'),
|
||||
tab = tabs.findByIdentifier(tabId),
|
||||
self = this
|
||||
|
||||
/*
|
||||
* Update tab
|
||||
*/
|
||||
|
||||
$.oc.stripeLoadIndicator.show()
|
||||
|
||||
$form.request('onOpenTemplate', {
|
||||
data: data
|
||||
}).done(function(data) {
|
||||
$('#cms-master-tabs').ocTab('updateTab', tab, data.tabTitle, data.tab)
|
||||
$('#cms-master-tabs').ocTab('unmodifyTab', tab)
|
||||
self.updateModifiedCounter()
|
||||
}).always(function() {
|
||||
$.oc.stripeLoadIndicator.hide()
|
||||
}).fail(function(jqXHR, textStatus, errorThrown) {
|
||||
alert(jqXHR.responseText.length ? jqXHR.responseText : jqXHR.statusText)
|
||||
})
|
||||
}
|
||||
|
||||
CmsPage.prototype.setPageTitle = function(title) {
|
||||
if (title.length)
|
||||
$.oc.layout.setPageTitle(title + ' | ')
|
||||
else
|
||||
$.oc.layout.setPageTitle(title)
|
||||
}
|
||||
|
||||
CmsPage.prototype.updateLayouts = function(form) {
|
||||
$(form).request('onGetTemplateList', {
|
||||
success: function(data) {
|
||||
$('#cms-master-tabs > .tab-content select[name="settings[layout]"]').each(function(){
|
||||
var
|
||||
$select = $(this),
|
||||
value = $select.val()
|
||||
|
||||
$select.find('option').remove()
|
||||
$.each(data.layouts, function(layoutFile, layoutName){
|
||||
$select.append($('<option>').attr('value', layoutFile).text(layoutName))
|
||||
})
|
||||
$select.trigger('pause.oc.changeMonitor')
|
||||
$select.val(value)
|
||||
$select.trigger('change')
|
||||
$select.trigger('resume.oc.changeMonitor')
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
$.oc.cmsPage = new CmsPage();
|
||||
}(window.jQuery);
|
||||
|
|
@ -0,0 +1,288 @@
|
|||
<?php namespace Indikator\DevTools\Classes;
|
||||
|
||||
use File;
|
||||
use Lang;
|
||||
use Config;
|
||||
use Cms\Classes\Theme;
|
||||
use Cms\Helpers\File as FileHelper;
|
||||
use October\Rain\Extension\Extendable;
|
||||
use ApplicationException;
|
||||
use ValidationException;
|
||||
|
||||
/**
|
||||
* The CMS asset file class.
|
||||
*
|
||||
* @package october\cms
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class Asset extends Extendable
|
||||
{
|
||||
/**
|
||||
* @var \Cms\Classes\Theme A reference to the CMS theme containing the object.
|
||||
*/
|
||||
protected $theme;
|
||||
|
||||
/**
|
||||
* @var string The plugin folder name.
|
||||
*/
|
||||
protected $dirName = 'plugins';
|
||||
|
||||
/**
|
||||
* @var string Specifies the file name corresponding the CMS object.
|
||||
*/
|
||||
public $fileName;
|
||||
|
||||
/**
|
||||
* @var string Specifies the file name, the CMS object was loaded from.
|
||||
*/
|
||||
protected $originalFileName = null;
|
||||
|
||||
/**
|
||||
* @var string Last modified time.
|
||||
*/
|
||||
public $mtime;
|
||||
|
||||
/**
|
||||
* @var string The entire file content.
|
||||
*/
|
||||
public $content;
|
||||
|
||||
/**
|
||||
* @var array The attributes that are mass assignable.
|
||||
*/
|
||||
protected $fillable = [
|
||||
'fileName',
|
||||
'content'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array Allowable file extensions.
|
||||
*/
|
||||
protected $allowedExtensions = [];
|
||||
|
||||
/**
|
||||
* @var bool Indicates if the model exists.
|
||||
*/
|
||||
public $exists = false;
|
||||
|
||||
/**
|
||||
* Creates an instance of the object and associates it with a CMS theme.
|
||||
* @param \Cms\Classes\Theme $theme Specifies the theme the object belongs to.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->allowedExtensions = self::getEditableExtensions();
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the object from a file.
|
||||
* This method is used in the CMS back-end. It doesn't use any caching.
|
||||
* @param \Cms\Classes\Theme $theme Specifies the theme the object belongs to.
|
||||
* @param string $fileName Specifies the file name, with the extension.
|
||||
* The file name can contain only alphanumeric symbols, dashes and dots.
|
||||
* @return mixed Returns a CMS object instance or null if the object wasn't found.
|
||||
*/
|
||||
public static function load($theme, $fileName)
|
||||
{
|
||||
return (new static($theme))->find($fileName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the theme datasource for the model.
|
||||
* @param \Cms\Classes\Theme $theme Specifies a parent theme.
|
||||
* @return $this
|
||||
*/
|
||||
public static function inTheme($theme)
|
||||
{
|
||||
if (is_string($theme)) {
|
||||
$theme = Theme::load($theme);
|
||||
}
|
||||
return new static($theme);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a single template by its file name.
|
||||
*
|
||||
* @param string $fileName
|
||||
* @return mixed|static
|
||||
*/
|
||||
public function find($fileName)
|
||||
{
|
||||
$filePath = $this->getFilePath($fileName);
|
||||
|
||||
if (!File::isFile($filePath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (($content = @File::get($filePath)) === false) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$this->fileName = $fileName;
|
||||
$this->originalFileName = $fileName;
|
||||
$this->mtime = File::lastModified($filePath);
|
||||
$this->content = $content;
|
||||
$this->exists = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the object attributes.
|
||||
* @param array $attributes A list of attributes to set.
|
||||
*/
|
||||
public function fill(array $attributes)
|
||||
{
|
||||
foreach ($attributes as $key => $value) {
|
||||
if (!in_array($key, $this->fillable)) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.cms_object.invalid_property',
|
||||
['name' => $key]
|
||||
));
|
||||
}
|
||||
|
||||
$this->$key = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the object to the disk.
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$this->validateFileName();
|
||||
|
||||
$fullPath = $this->getFilePath();
|
||||
|
||||
if (File::isFile($fullPath) && $this->originalFileName !== $this->fileName) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.cms_object.file_already_exists',
|
||||
['name' => $this->fileName]
|
||||
));
|
||||
}
|
||||
|
||||
$dirPath = base_path().'/'.$this->dirName;
|
||||
if (!file_exists($dirPath) || !is_dir($dirPath)) {
|
||||
if (!File::makeDirectory($dirPath, 0777, true, true)) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.cms_object.error_creating_directory',
|
||||
['name' => $dirPath]
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if (($pos = strpos($this->fileName, '/')) !== false) {
|
||||
$dirPath = dirname($fullPath);
|
||||
|
||||
if (!is_dir($dirPath) && !File::makeDirectory($dirPath, 0777, true, true)) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.cms_object.error_creating_directory',
|
||||
['name' => $dirPath]
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
$newFullPath = $fullPath;
|
||||
if (@File::put($fullPath, $this->content) === false) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.cms_object.error_saving',
|
||||
['name' => $this->fileName]
|
||||
));
|
||||
}
|
||||
|
||||
if (strlen($this->originalFileName) && $this->originalFileName !== $this->fileName) {
|
||||
$fullPath = $this->getFilePath($this->originalFileName);
|
||||
|
||||
if (File::isFile($fullPath)) {
|
||||
@unlink($fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
clearstatcache();
|
||||
|
||||
$this->mtime = @File::lastModified($newFullPath);
|
||||
$this->originalFileName = $this->fileName;
|
||||
$this->exists = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the supplied filename, extension and path.
|
||||
* @param string $fileName
|
||||
*/
|
||||
protected function validateFileName($fileName = null)
|
||||
{
|
||||
if ($fileName === null) {
|
||||
$fileName = $this->fileName;
|
||||
}
|
||||
|
||||
$fileName = trim($fileName);
|
||||
|
||||
if (!strlen($fileName)) {
|
||||
throw new ValidationException(['fileName' =>
|
||||
Lang::get('cms::lang.cms_object.file_name_required', [
|
||||
'allowed' => implode(', ', $this->allowedExtensions),
|
||||
'invalid' => pathinfo($fileName, PATHINFO_EXTENSION)
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
if (!FileHelper::validateExtension($fileName, $this->allowedExtensions, false)) {
|
||||
throw new ValidationException(['fileName' =>
|
||||
Lang::get('cms::lang.cms_object.invalid_file_extension', [
|
||||
'allowed' => implode(', ', $this->allowedExtensions),
|
||||
'invalid' => pathinfo($fileName, PATHINFO_EXTENSION)
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
||||
if (!FileHelper::validatePath($fileName, null)) {
|
||||
throw new ValidationException(['fileName' =>
|
||||
Lang::get('cms::lang.cms_object.invalid_file', [
|
||||
'name' => $fileName
|
||||
])
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file name.
|
||||
* @return string
|
||||
*/
|
||||
public function getFileName()
|
||||
{
|
||||
return $this->fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the absolute file path.
|
||||
* @param string $fileName Specifies the file name to return the path to.
|
||||
* @return string
|
||||
*/
|
||||
public function getFilePath($fileName = null)
|
||||
{
|
||||
if ($fileName === null) {
|
||||
$fileName = $this->fileName;
|
||||
}
|
||||
|
||||
return base_path().'/'.$this->dirName.'/'.$fileName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of editable asset extensions.
|
||||
* The list can be overridden with the cms.editableAssetTypes configuration option.
|
||||
* @return array
|
||||
*/
|
||||
public static function getEditableExtensions()
|
||||
{
|
||||
$defaultTypes = ['css', 'js', 'less', 'sass', 'scss', 'php', 'htm', 'html', 'yaml', 'md', 'txt'];
|
||||
|
||||
$configTypes = Config::get('cms.editableAssetTypes');
|
||||
if (!$configTypes) {
|
||||
return $defaultTypes;
|
||||
}
|
||||
|
||||
return $configTypes;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
# ===================================
|
||||
# Form Field Definitions
|
||||
# ===================================
|
||||
|
||||
fields:
|
||||
|
||||
fileName:
|
||||
label: cms::lang.editor.filename
|
||||
attributes:
|
||||
default-focus: 1
|
||||
|
||||
toolbar:
|
||||
type: partial
|
||||
path: content_toolbar
|
||||
cssClass: collapse-visible
|
||||
|
||||
tabs:
|
||||
|
||||
cssClass: master-area
|
||||
|
||||
secondaryTabs:
|
||||
|
||||
stretch: true
|
||||
fields:
|
||||
|
||||
content:
|
||||
tab: cms::lang.editor.content
|
||||
stretch: true
|
||||
type: codeeditor
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "indikator/devtools-plugin",
|
||||
"type": "october-plugin",
|
||||
"description": "None",
|
||||
"require": {
|
||||
"composer/installers": "~1.0"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,380 @@
|
|||
<?php namespace Indikator\DevTools\Controllers;
|
||||
|
||||
use Url;
|
||||
use Lang;
|
||||
use Flash;
|
||||
use Config;
|
||||
use Request;
|
||||
use Response;
|
||||
use Exception;
|
||||
use BackendMenu;
|
||||
use Indikator\DevTools\Classes\Asset;
|
||||
use Indikator\DevTools\Widgets\AssetList;
|
||||
use Cms\Classes\Router;
|
||||
use Backend\Classes\Controller;
|
||||
use Backend\Classes\WidgetManager;
|
||||
use System\Helpers\DateTime;
|
||||
use October\Rain\Router\Router as RainRouter;
|
||||
use ApplicationException;
|
||||
|
||||
/**
|
||||
* CMS index
|
||||
*
|
||||
* @package october\cms
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class Editor extends Controller
|
||||
{
|
||||
use \Backend\Traits\InspectableContainer;
|
||||
|
||||
public $requiredPermissions = ['indikator.devtools.editor'];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
BackendMenu::setContext('Indikator.DevTools', 'editor', 'assets');
|
||||
|
||||
try {
|
||||
new AssetList($this, 'assetList');
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$this->handleError($ex);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Pages
|
||||
//
|
||||
|
||||
public function index()
|
||||
{
|
||||
$this->addJs('/plugins/indikator/devtools/assets/october.cmspage.js');
|
||||
$this->addJs('/modules/cms/assets/js/october.dragcomponents.js', 'core');
|
||||
$this->addJs('/modules/cms/assets/js/october.tokenexpander.js', 'core');
|
||||
$this->addCss('/modules/cms/assets/css/october.components.css', 'core');
|
||||
|
||||
$this->bodyClass = 'compact-container';
|
||||
$this->pageTitle = 'cms::lang.cms.menu_label';
|
||||
$this->pageTitleTemplate = '%s '.trans($this->pageTitle);
|
||||
|
||||
if (Request::ajax() && Request::input('formWidgetAlias')) {
|
||||
$this->bindFormWidgetToController();
|
||||
}
|
||||
}
|
||||
|
||||
public function index_onOpenTemplate()
|
||||
{
|
||||
$type = Request::input('type');
|
||||
$template = $this->loadTemplate($type, Request::input('path'));
|
||||
$widget = $this->makeTemplateFormWidget($type, $template);
|
||||
|
||||
$this->vars['templatePath'] = Request::input('path');
|
||||
$this->vars['lastModified'] = DateTime::makeCarbon($template->mtime);
|
||||
|
||||
if ($type === 'page') {
|
||||
$router = new RainRouter;
|
||||
$this->vars['pageUrl'] = $router->urlFromPattern($template->url);
|
||||
}
|
||||
|
||||
return [
|
||||
'tabTitle' => $this->getTabTitle($type, $template),
|
||||
'tab' => $this->makePartial('form_page', [
|
||||
'form' => $widget,
|
||||
'templateType' => $type,
|
||||
'templateMtime' => $template->mtime
|
||||
])
|
||||
];
|
||||
}
|
||||
|
||||
public function onSave()
|
||||
{
|
||||
$type = Request::input('templateType');
|
||||
$templatePath = trim(Request::input('templatePath'));
|
||||
$template = $templatePath ? $this->loadTemplate($type, $templatePath) : $this->createTemplate($type);
|
||||
$formWidget = $this->makeTemplateFormWidget($type, $template);
|
||||
|
||||
$saveData = $formWidget->getSaveData();
|
||||
$postData = post();
|
||||
$templateData = [];
|
||||
|
||||
$settings = array_get($saveData, 'settings', []) + Request::input('settings', []);
|
||||
$settings = $this->upgradeSettings($settings);
|
||||
|
||||
if ($settings) {
|
||||
$templateData['settings'] = $settings;
|
||||
}
|
||||
|
||||
$fields = ['markup', 'code', 'fileName', 'content'];
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if (array_key_exists($field, $saveData)) {
|
||||
$templateData[$field] = $saveData[$field];
|
||||
}
|
||||
elseif (array_key_exists($field, $postData)) {
|
||||
$templateData[$field] = $postData[$field];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($templateData['markup']) && Config::get('cms.convertLineEndings', false) === true) {
|
||||
$templateData['markup'] = $this->convertLineEndings($templateData['markup']);
|
||||
}
|
||||
|
||||
if (!empty($templateData['code']) && Config::get('cms.convertLineEndings', false) === true) {
|
||||
$templateData['code'] = $this->convertLineEndings($templateData['code']);
|
||||
}
|
||||
|
||||
if (!Request::input('templateForceSave') && $template->mtime) {
|
||||
if (Request::input('templateMtime') != $template->mtime) {
|
||||
throw new ApplicationException('mtime-mismatch');
|
||||
}
|
||||
}
|
||||
|
||||
$template->attributes = [];
|
||||
$template->fill($templateData);
|
||||
$template->save();
|
||||
|
||||
/*
|
||||
* Extensibility
|
||||
*/
|
||||
$this->fireSystemEvent('cms.template.save', [$template, $type]);
|
||||
|
||||
Flash::success(Lang::get('cms::lang.template.saved'));
|
||||
|
||||
$result = [
|
||||
'templatePath' => $template->fileName,
|
||||
'templateMtime' => $template->mtime,
|
||||
'tabTitle' => $this->getTabTitle($type, $template)
|
||||
];
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function onOpenConcurrencyResolveForm()
|
||||
{
|
||||
return $this->makePartial('concurrency_resolve_form');
|
||||
}
|
||||
|
||||
public function onCreateTemplate()
|
||||
{
|
||||
$type = Request::input('type');
|
||||
$template = $this->createTemplate($type);
|
||||
|
||||
if ($type === 'asset') {
|
||||
$template->fileName = $this->widget->assetList->getCurrentRelativePath();
|
||||
}
|
||||
|
||||
$widget = $this->makeTemplateFormWidget($type, $template);
|
||||
|
||||
$this->vars['templatePath'] = '';
|
||||
|
||||
return [
|
||||
'tabTitle' => $this->getTabTitle($type, $template),
|
||||
'tab' => $this->makePartial('form_page', [
|
||||
'form' => $widget,
|
||||
'templateType' => $type,
|
||||
'templateMtime' => null
|
||||
])
|
||||
];
|
||||
}
|
||||
|
||||
public function onDeleteTemplates()
|
||||
{
|
||||
$type = Request::input('type');
|
||||
$templates = Request::input('template');
|
||||
$error = null;
|
||||
$deleted = [];
|
||||
|
||||
try {
|
||||
foreach ($templates as $path => $selected) {
|
||||
if ($selected) {
|
||||
$this->loadTemplate($type, $path)->delete();
|
||||
$deleted[] = $path;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$error = $ex->getMessage();
|
||||
}
|
||||
|
||||
/*
|
||||
* Extensibility
|
||||
*/
|
||||
$this->fireSystemEvent('cms.template.delete', [$type]);
|
||||
|
||||
return [
|
||||
'deleted' => $deleted,
|
||||
'error' => $error,
|
||||
'theme' => Request::input('theme')
|
||||
];
|
||||
}
|
||||
|
||||
public function onDelete()
|
||||
{
|
||||
$type = Request::input('templateType');
|
||||
|
||||
$this->loadTemplate($type, trim(Request::input('templatePath')))->delete();
|
||||
|
||||
/*
|
||||
* Extensibility
|
||||
*/
|
||||
$this->fireSystemEvent('cms.template.delete', [$type]);
|
||||
}
|
||||
|
||||
//
|
||||
// Methods for the internal use
|
||||
//
|
||||
|
||||
protected function resolveTypeClassName($type)
|
||||
{
|
||||
$types = [
|
||||
'asset' => Asset::class
|
||||
];
|
||||
|
||||
if (!array_key_exists($type, $types)) {
|
||||
throw new ApplicationException(trans('cms::lang.template.invalid_type'));
|
||||
}
|
||||
|
||||
return $types[$type];
|
||||
}
|
||||
|
||||
protected function loadTemplate($type, $path)
|
||||
{
|
||||
$class = $this->resolveTypeClassName($type);
|
||||
|
||||
if (!($template = call_user_func([$class, 'load'], $this->theme, $path))) {
|
||||
throw new ApplicationException(trans('cms::lang.template.not_found'));
|
||||
}
|
||||
|
||||
/*
|
||||
* Extensibility
|
||||
*/
|
||||
$this->fireSystemEvent('cms.template.processSettingsAfterLoad', [$template]);
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
protected function createTemplate($type)
|
||||
{
|
||||
$class = $this->resolveTypeClassName($type);
|
||||
|
||||
if (!($template = $class::inTheme($this->theme))) {
|
||||
throw new ApplicationException(trans('cms::lang.template.not_found'));
|
||||
}
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
protected function getTabTitle($type, $template)
|
||||
{
|
||||
if ($type === 'asset') {
|
||||
$result = in_array($type, ['asset', 'content']) ? $template->getFileName() : $template->getBaseFileName();
|
||||
if (!$result) {
|
||||
$result = trans('cms::lang.'.$type.'.new');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $template->getFileName();
|
||||
}
|
||||
|
||||
protected function makeTemplateFormWidget($type, $template, $alias = null)
|
||||
{
|
||||
$formConfigs = [
|
||||
'asset' => '~/plugins/indikator/devtools/classes/asset/fields.yaml'
|
||||
];
|
||||
|
||||
if (!array_key_exists($type, $formConfigs)) {
|
||||
throw new ApplicationException(trans('cms::lang.template.not_found'));
|
||||
}
|
||||
|
||||
$widgetConfig = $this->makeConfig($formConfigs[$type]);
|
||||
$widgetConfig->model = $template;
|
||||
$widgetConfig->alias = $alias ?: 'form'.studly_case($type).md5($template->getFileName()).uniqid();
|
||||
|
||||
$widget = $this->makeWidget('Backend\Widgets\Form', $widgetConfig);
|
||||
|
||||
return $widget;
|
||||
}
|
||||
|
||||
protected function upgradeSettings($settings)
|
||||
{
|
||||
/*
|
||||
* Handle component usage
|
||||
*/
|
||||
$componentProperties = post('component_properties');
|
||||
$componentNames = post('component_names');
|
||||
$componentAliases = post('component_aliases');
|
||||
|
||||
if ($componentProperties !== null) {
|
||||
if ($componentNames === null || $componentAliases === null) {
|
||||
throw new ApplicationException(trans('cms::lang.component.invalid_request'));
|
||||
}
|
||||
|
||||
$count = count($componentProperties);
|
||||
if (count($componentNames) != $count || count($componentAliases) != $count) {
|
||||
throw new ApplicationException(trans('cms::lang.component.invalid_request'));
|
||||
}
|
||||
|
||||
for ($index = 0; $index < $count; $index++) {
|
||||
$componentName = $componentNames[$index];
|
||||
$componentAlias = $componentAliases[$index];
|
||||
|
||||
$section = $componentName;
|
||||
if ($componentAlias != $componentName) {
|
||||
$section .= ' '.$componentAlias;
|
||||
}
|
||||
|
||||
$properties = json_decode($componentProperties[$index], true);
|
||||
unset($properties['oc.alias']);
|
||||
unset($properties['inspectorProperty']);
|
||||
unset($properties['inspectorClassName']);
|
||||
$settings[$section] = $properties;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle view bag
|
||||
*/
|
||||
$viewBag = post('viewBag');
|
||||
if ($viewBag !== null) {
|
||||
$settings['viewBag'] = $viewBag;
|
||||
}
|
||||
|
||||
/*
|
||||
* Extensibility
|
||||
*/
|
||||
$dataHolder = (object) ['settings' => $settings];
|
||||
|
||||
$this->fireSystemEvent('cms.template.processSettingsBeforeSave', [$dataHolder]);
|
||||
|
||||
return $dataHolder->settings;
|
||||
}
|
||||
|
||||
protected function bindFormWidgetToController()
|
||||
{
|
||||
$alias = Request::input('formWidgetAlias');
|
||||
$type = Request::input('templateType');
|
||||
$object = $this->loadTemplate($type, Request::input('templatePath'));
|
||||
|
||||
$widget = $this->makeTemplateFormWidget($type, $object, $alias);
|
||||
$widget->bindToController();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces Windows style (/r/n) line endings with unix style (/n)
|
||||
* line endings.
|
||||
* @param string $markup The markup to convert to unix style endings
|
||||
* @return string
|
||||
*/
|
||||
protected function convertLineEndings($markup)
|
||||
{
|
||||
$markup = str_replace(["\r\n", "\r"], "\n", $markup);
|
||||
|
||||
return $markup;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
<?= Form::open(['onsubmit'=>'return false']) ?>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(trans('backend::lang.form.concurrency_file_changed_title')) ?></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p><?= e(trans('backend::lang.form.concurrency_file_changed_description')) ?></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
data-action="reload"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.reload')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
data-action="save"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.save')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
<?= Form::close() ?>
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
<div class="form-buttons loading-indicator-container" data-toolbar-type="content">
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="btn btn-primary oc-icon-check save"
|
||||
data-request="onSave"
|
||||
data-load-indicator="<?= e(trans('backend::lang.form.saving')) ?>"
|
||||
data-hotkey="ctrl+s, cmd+s">
|
||||
<?= e(trans('backend::lang.form.save')) ?>
|
||||
</a>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-danger empty oc-icon-trash-o <?php if (!$templatePath): ?>hide<?php endif ?>"
|
||||
data-request="onDelete"
|
||||
data-request-confirm="<?= e(trans('cms::lang.content.delete_confirm_single')) ?>"
|
||||
data-request-success="$.oc.cmsPage.updateTemplateList('content'); $(this).trigger('close.oc.tab', [{force: true}])"
|
||||
data-control="delete-button"></button>
|
||||
|
||||
<?php if (isset($lastModified)): ?>
|
||||
<span
|
||||
class="btn empty oc-icon-calendar"
|
||||
title="<?= e(trans('backend::lang.media.last_modified')) ?>: <?= $lastModified ?>"
|
||||
data-toggle="tooltip"
|
||||
data-placement="right">
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?= Form::open([
|
||||
'class' => 'layout',
|
||||
'data-change-monitor' => 'true',
|
||||
'data-window-close-confirm' => e(trans('backend::lang.form.confirm_tab_close')),
|
||||
'data-inspector-external-parameters' => true
|
||||
]) ?>
|
||||
<?= $form->render() ?>
|
||||
|
||||
<input type="hidden" value="<?= e($form->alias) ?>" name="formWidgetAlias">
|
||||
<input type="hidden" value="<?= ($templateType) ?>" name="templateType">
|
||||
<input type="hidden" value="<?= ($templatePath) ?>" name="templatePath">
|
||||
<input type="hidden" value="<?= ($templateMtime) ?>" name="templateMtime">
|
||||
<input type="hidden" value="0" name="templateForceSave">
|
||||
|
||||
<?= Form::close() ?>
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<div class="layout control-scrollpanel" id="cms-side-panel">
|
||||
<div class="layout-cell">
|
||||
<div class="layout-relative fix-button-container">
|
||||
<form
|
||||
class="layout"
|
||||
data-content-id="assets"
|
||||
data-template-type="asset"
|
||||
data-type-icon="oc-icon-cubes"
|
||||
onsubmit="return false">
|
||||
<?= $this->widget->assetList->render() ?>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?= Block::put('sidepanel') ?>
|
||||
<?php if (!$this->fatalError): ?>
|
||||
<?= $this->makePartial('sidepanel') ?>
|
||||
<?php endif ?>
|
||||
<?= Block::endPut() ?>
|
||||
|
||||
<?= Block::put('body') ?>
|
||||
<?php if (!$this->fatalError): ?>
|
||||
<div
|
||||
data-control="tab"
|
||||
data-closable
|
||||
data-close-confirmation="<?= e(trans('backend::lang.form.confirm_tab_close')) ?>"
|
||||
data-pane-classes="layout-cell"
|
||||
data-max-title-symbols="15"
|
||||
data-title-as-file-names="true"
|
||||
class="layout control-tabs master-tabs fancy-layout oc-logo-transparent"
|
||||
id="cms-master-tabs">
|
||||
|
||||
<div class="layout-row min-size">
|
||||
<div class="tabs-container">
|
||||
<ul class="nav nav-tabs"></ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-content layout-row"></div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<p class="flash-message static error"><?= e(trans($this->fatalError)) ?></p>
|
||||
<?php endif ?>
|
||||
<?= Block::endPut() ?>
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?php namespace Indikator\DevTools\FormWidgets;
|
||||
|
||||
use Backend\Classes\FormField;
|
||||
use Backend\Classes\FormWidgetBase;
|
||||
|
||||
class Help extends FormWidgetBase
|
||||
{
|
||||
protected $defaultAlias = 'help';
|
||||
|
||||
public $content = 'cms';
|
||||
|
||||
public function init()
|
||||
{
|
||||
$this->fillFromConfig([
|
||||
'content'
|
||||
]);
|
||||
}
|
||||
|
||||
public function render()
|
||||
{
|
||||
$this->prepareVars();
|
||||
|
||||
return $this->makePartial('help');
|
||||
}
|
||||
|
||||
protected function prepareVars()
|
||||
{
|
||||
$this->vars['content'] = $this->content;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
<?php if ($content == 'cms'): ?>
|
||||
<div class="row" style="padding:24px;">
|
||||
<div class="col-md-4 col-sm-4 col-xs-6">
|
||||
<strong><?= e(trans('indikator.devtools::lang.help.cms')) ?></strong><br>
|
||||
<a href="https://octobercms.com/docs/cms/pages#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.pages')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/cms/partials#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.partials')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/cms/layouts#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.layouts')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/cms/content#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.content')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/cms/ajax#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.ajax')) ?></a><br>
|
||||
<br>
|
||||
<strong><?= e(trans('indikator.devtools::lang.help.functions')) ?></strong><br>
|
||||
<a href="https://octobercms.com/docs/markup/function-str#search-input" target="_blank">str()</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/function-form#search-input" target="_blank">form()</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/function-html#search-input" target="_blank">html()</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/function-dump#search-input" target="_blank">dump()</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 col-sm-4 col-xs-6">
|
||||
<strong><?= e(trans('indikator.devtools::lang.help.tags')) ?></strong><br>
|
||||
<a href="https://octobercms.com/docs/markup/tag-partial#search-input" target="_blank">{% partial %}</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/tag-content#search-input" target="_blank">{% content %}</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/tag-component#search-input" target="_blank">{% component %}</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/tag-placeholder#search-input" target="_blank">{% placeholder %}</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/tag-scripts#search-input" target="_blank">{% scripts %}</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/tag-styles#search-input" target="_blank">{% styles %}</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/tag-flash#search-input" target="_blank">{% flash %}</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/tag-verbatim#search-input" target="_blank">{% verbatim %}</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/tag-for#search-input" target="_blank">{% for %}</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/tag-if#search-input" target="_blank">{% if %}</a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 col-sm-4 col-xs-6">
|
||||
<strong><?= e(trans('indikator.devtools::lang.help.filters')) ?></strong><br>
|
||||
<a href="https://octobercms.com/docs/markup/filter-app#search-input" target="_blank">|app</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/filter-page#search-input" target="_blank">|page</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/filter-theme#search-input" target="_blank">|theme</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/filter-media#search-input" target="_blank">|media</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/filter-md#search-input" target="_blank">|md</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/filter-raw#search-input" target="_blank">|raw</a><br>
|
||||
<a href="https://octobercms.com/docs/markup/filter-default#search-input" target="_blank">|default</a><br>
|
||||
<br>
|
||||
<strong><?= e(trans('indikator.devtools::lang.help.database')) ?></strong><br>
|
||||
<a href="https://octobercms.com/docs/database/basics#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.basic')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/database/query#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.queries')) ?></a>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="row" style="padding:24px;">
|
||||
<div class="col-md-4 col-sm-4 col-xs-6">
|
||||
<strong><?= e(trans('indikator.devtools::lang.help.plugins')) ?></strong><br>
|
||||
<a href="https://octobercms.com/docs/plugin/registration#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.registration')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/plugin/updates#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.version_history')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/plugin/components#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.building_components')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/plugin/settings#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.settings_config')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/plugin/localization#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.localization')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/plugin/scheduling#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.task_scheduling')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/plugin/extending#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.extending_plugins')) ?></a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 col-sm-4 col-xs-6">
|
||||
<strong><?= e(trans('indikator.devtools::lang.help.backend')) ?></strong><br>
|
||||
<a href="https://octobercms.com/docs/backend/controllers-ajax#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.controllers_ajax')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/backend/views-partials#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.views_partials')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/backend/widgets#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.widgets')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/backend/forms#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.forms')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/backend/lists#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.lists')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/backend/relations#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.relations')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/backend/reorder#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.sorting_records')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/backend/import-export#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.importing_exporting')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/backend/users#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.users_permissions')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/ui#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.user_interface_guide')) ?></a>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 col-sm-4 col-xs-6">
|
||||
<strong><?= e(trans('indikator.devtools::lang.help.services')) ?></strong><br>
|
||||
<a href="https://octobercms.com/docs/services/application#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.application')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/services/behaviors#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.behaviors')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/services/events#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.events')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/services/html#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.forms_html')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/services/mail#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.mail')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/services/request-input#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.request_input')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/services/response-view#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.response_view')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/services/router#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.router')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/services/session#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.session')) ?></a><br>
|
||||
<a href="https://octobercms.com/docs/services/validation#search-input" target="_blank"><?= e(trans('indikator.devtools::lang.help.validation')) ?></a>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'plugin' => [
|
||||
'name' => 'Developer Tools',
|
||||
'description' => 'Useful features for developers.',
|
||||
'author' => 'Gergő Szabó'
|
||||
],
|
||||
'editor' => [
|
||||
'menu_label' => 'Code editor',
|
||||
'plugins' => 'Plugins',
|
||||
'permission' => 'Use the code editor'
|
||||
],
|
||||
'help' => [
|
||||
'menu_label' => 'Developer Tools',
|
||||
'menu_description' => 'Settings of developer features.',
|
||||
'tab' => 'Help',
|
||||
'cms' => 'CMS',
|
||||
'pages' => 'Pages',
|
||||
'partials' => 'Partials',
|
||||
'layouts' => 'Layouts',
|
||||
'content' => 'Content',
|
||||
'ajax' => 'AJAX',
|
||||
'functions' => 'Functions',
|
||||
'tags' => 'Tags',
|
||||
'filters' => 'Filters',
|
||||
'database' => 'Database',
|
||||
'basic' => 'Basic',
|
||||
'queries' => 'Queries',
|
||||
'plugins' => 'Plugins',
|
||||
'registration' => 'Registration',
|
||||
'version_history' => 'Version history',
|
||||
'building_components' => 'Building components',
|
||||
'settings_config' => 'Settings & Config',
|
||||
'localization' => 'Localization',
|
||||
'task_scheduling' => 'Task scheduling',
|
||||
'extending_plugins' => 'Extending plugins',
|
||||
'backend' => 'Backend',
|
||||
'controllers_ajax' => 'Controllers & AJAX',
|
||||
'views_partials' => 'Views & Partials',
|
||||
'widgets' => 'Widgets',
|
||||
'forms' => 'Forms',
|
||||
'lists' => 'Lists',
|
||||
'relations' => 'Relations',
|
||||
'sorting_records' => 'Sorting records',
|
||||
'importing_exporting' => 'Importing & Exporting',
|
||||
'users_permissions' => 'Users & Permissions',
|
||||
'user_interface_guide' => 'User interface guide',
|
||||
'services' => 'Services',
|
||||
'application' => 'Application',
|
||||
'behaviors' => 'Behaviors',
|
||||
'events' => 'Events',
|
||||
'forms_html' => 'Forms & Html',
|
||||
'mail' => 'Mail',
|
||||
'request_input' => 'Request & Input',
|
||||
'response_view' => 'Response & View',
|
||||
'router' => 'Router',
|
||||
'session' => 'Session',
|
||||
'validation' => 'Validation',
|
||||
'permission' => 'Manage settings'
|
||||
],
|
||||
'form' => [
|
||||
'wysiwyg_label' => 'Wysiwyg Editor',
|
||||
'wysiwyg_enabled' => 'Enabled on the Content page',
|
||||
'help_label' => 'Help',
|
||||
'help_enabled' => 'Enable the Help tab on the CMS pages',
|
||||
'select_none' => '-- None --',
|
||||
'select_superuser' => 'Show it for the Superusers',
|
||||
'select_admingroup' => 'Show it for the following admin group',
|
||||
'select_adminid' => 'Show it for the following administrator',
|
||||
]
|
||||
];
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'plugin' => [
|
||||
'name' => 'Fejlesztői eszközök',
|
||||
'description' => 'Hasznos szolgáltatások fejlesztőknek.',
|
||||
'author' => 'Szabó Gergő'
|
||||
],
|
||||
'editor' => [
|
||||
'menu_label' => 'Kódszerkesztő',
|
||||
'plugins' => 'Bővítmények',
|
||||
'permission' => 'Szerkesztő használata'
|
||||
],
|
||||
'help' => [
|
||||
'menu_label' => 'Fejlesztőknek',
|
||||
'menu_description' => 'Szolgáltatások és lehetőségek beállítása.',
|
||||
'tab' => 'Súgó',
|
||||
'cms' => 'Testreszabás',
|
||||
'pages' => 'Lapok',
|
||||
'partials' => 'Részlapok',
|
||||
'layouts' => 'Elrendezések',
|
||||
'content' => 'Tartalom',
|
||||
'ajax' => 'AJAX',
|
||||
'functions' => 'Függvények',
|
||||
'tags' => 'Tag-ek',
|
||||
'filters' => 'Filterek',
|
||||
'database' => 'Adatbázis',
|
||||
'basic' => 'Alapok',
|
||||
'queries' => 'Lekérdezések',
|
||||
'plugins' => 'Bővítmények',
|
||||
'registration' => 'Regisztrálás',
|
||||
'version_history' => 'Verzió előzmények',
|
||||
'building_components' => 'Komponensek',
|
||||
'settings_config' => 'Beállítások',
|
||||
'localization' => 'Többnyelvűsítés',
|
||||
'task_scheduling' => 'Időzítés',
|
||||
'extending_plugins' => 'Kiegészítés',
|
||||
'backend' => 'Admin',
|
||||
'controllers_ajax' => 'Kontroller és AJAX',
|
||||
'views_partials' => 'Részlapok',
|
||||
'widgets' => 'Widgetek',
|
||||
'forms' => 'Űrlapok',
|
||||
'lists' => 'Listák',
|
||||
'relations' => 'Kapcsolatok',
|
||||
'sorting_records' => 'Elemek rendezése',
|
||||
'importing_exporting' => 'Import és export',
|
||||
'users_permissions' => 'Jogosultságok',
|
||||
'user_interface_guide' => 'Felhasználói felület',
|
||||
'services' => 'Szolgáltatások',
|
||||
'application' => 'Alkalmazás',
|
||||
'behaviors' => 'Viselkedések',
|
||||
'events' => 'Események',
|
||||
'forms_html' => 'Űrlapok és HTML',
|
||||
'mail' => 'Levelezés',
|
||||
'request_input' => 'Űrlap értékek',
|
||||
'response_view' => 'Válasz megjelenítés',
|
||||
'router' => 'Router kezelés',
|
||||
'session' => 'Munkamenet',
|
||||
'validation' => 'Ellenőrzés',
|
||||
'permission' => 'Beállítások kezelése'
|
||||
],
|
||||
'form' => [
|
||||
'wysiwyg_label' => 'Szövegszerkesztő',
|
||||
'wysiwyg_enabled' => 'Engedélyezés a Tartalom oldalon',
|
||||
'help_label' => 'Súgó',
|
||||
'help_enabled' => 'Engedélyezés a Testreszabás aloldalakon',
|
||||
'select_none' => '-- nincs --',
|
||||
'select_superuser' => 'Szuperadminok láthatják',
|
||||
'select_admingroup' => 'A következő admin csoport tagjai láthatják',
|
||||
'select_adminid' => 'A következő admin felhasználó láthatja'
|
||||
]
|
||||
];
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
<?php namespace Indikator\DevTools\Models;
|
||||
|
||||
use Model;
|
||||
use Db;
|
||||
|
||||
class Settings extends Model
|
||||
{
|
||||
public $implement = ['System.Behaviors.SettingsModel'];
|
||||
|
||||
public $settingsCode = 'indikator_devtools_settings';
|
||||
|
||||
public $settingsFields = 'fields.yaml';
|
||||
|
||||
public $selectList = [0 => 'indikator.devtools::lang.form.select_none'];
|
||||
|
||||
public function getHelpAdmingroupOptions()
|
||||
{
|
||||
$result = $this->selectList;
|
||||
$sql = Db::table('backend_user_groups')->orderBy('name', 'asc')->get()->all();
|
||||
|
||||
foreach ($sql as $item) {
|
||||
$result[$item->id] = $item->name.' ('.Db::table('backend_users_groups')->where('user_group_id', $item->id)->count().')';
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getHelpAdminidOptions()
|
||||
{
|
||||
$result = $this->selectList;
|
||||
$sql = Db::table('backend_users')->orderBy('login', 'asc')->get()->all();
|
||||
|
||||
foreach ($sql as $item) {
|
||||
$result[$item->id] = $item->login.' ('.$item->email.')';
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getWysiwygAdmingroupOptions()
|
||||
{
|
||||
$result = $this->selectList;
|
||||
$sql = Db::table('backend_user_groups')->orderBy('name', 'asc')->get()->all();
|
||||
|
||||
foreach ($sql as $item) {
|
||||
$result[$item->id] = $item->name.' ('.Db::table('backend_users_groups')->where('user_group_id', $item->id)->count().')';
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function getWysiwygAdminidOptions()
|
||||
{
|
||||
$result = $this->selectList;
|
||||
$sql = Db::table('backend_users')->orderBy('login', 'asc')->get()->all();
|
||||
|
||||
foreach ($sql as $item) {
|
||||
$result[$item->id] = $item->login.' ('.$item->email.')';
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
# ===================================
|
||||
# Field Definitions
|
||||
# ===================================
|
||||
|
||||
fields:
|
||||
|
||||
help_title:
|
||||
label: indikator.devtools::lang.form.help_label
|
||||
type: section
|
||||
comment: ''
|
||||
span: left
|
||||
|
||||
wysiwyg_title:
|
||||
label: indikator.devtools::lang.form.wysiwyg_label
|
||||
type: section
|
||||
comment: ''
|
||||
span: right
|
||||
|
||||
help_enabled:
|
||||
label: indikator.devtools::lang.form.help_enabled
|
||||
type: switch
|
||||
default: false
|
||||
span: left
|
||||
|
||||
wysiwyg_enabled:
|
||||
label: indikator.devtools::lang.form.wysiwyg_enabled
|
||||
type: switch
|
||||
default: false
|
||||
span: right
|
||||
|
||||
help_superuser:
|
||||
label: indikator.devtools::lang.form.select_superuser
|
||||
type: switch
|
||||
default: false
|
||||
trigger:
|
||||
action: show
|
||||
field: help_enabled
|
||||
condition: checked
|
||||
span: left
|
||||
|
||||
wysiwyg_superuser:
|
||||
label: indikator.devtools::lang.form.select_superuser
|
||||
type: switch
|
||||
default: false
|
||||
trigger:
|
||||
action: show
|
||||
field: wysiwyg_enabled
|
||||
condition: checked
|
||||
span: right
|
||||
|
||||
help_admingroup:
|
||||
label: indikator.devtools::lang.form.select_admingroup
|
||||
type: dropdown
|
||||
trigger:
|
||||
action: show
|
||||
field: help_enabled
|
||||
condition: checked
|
||||
span: left
|
||||
|
||||
wysiwyg_admingroup:
|
||||
label: indikator.devtools::lang.form.select_admingroup
|
||||
type: dropdown
|
||||
trigger:
|
||||
action: show
|
||||
field: wysiwyg_enabled
|
||||
condition: checked
|
||||
span: right
|
||||
|
||||
help_adminid:
|
||||
label: indikator.devtools::lang.form.select_adminid
|
||||
type: dropdown
|
||||
trigger:
|
||||
action: show
|
||||
field: help_enabled
|
||||
condition: checked
|
||||
span: left
|
||||
|
||||
wysiwyg_adminid:
|
||||
label: indikator.devtools::lang.form.select_adminid
|
||||
type: dropdown
|
||||
trigger:
|
||||
action: show
|
||||
field: wysiwyg_enabled
|
||||
condition: checked
|
||||
span: right
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
Route::get('/phpinfo', function()
|
||||
{
|
||||
$admin = BackendAuth::getUser();
|
||||
|
||||
if (isset($admin) && $admin->is_superuser == 1) {
|
||||
echo phpinfo();
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
1.0.0: First version of Developer Tools.
|
||||
1.1.0: Edit plugins with the code editor.
|
||||
1.1.1: Translate some English texts.
|
||||
1.1.2: Fixed the Create file issue.
|
||||
1.1.3: Added new icon for main navigation.
|
||||
1.1.4: Show the PHP's configuration.
|
||||
1.1.5: Minor code improvements and bugfix.
|
||||
1.1.6: The top menu icon shows again.
|
||||
1.1.7: Fixed the Create folder issue.
|
||||
1.1.8: !!! Updated for October 420+.
|
||||
1.1.9:
|
||||
- Updated the main navigation icon.
|
||||
- Added last modified date.
|
||||
1.2.0: The syntax highlighting works again!
|
||||
1.2.1: Help links open in a new window.
|
||||
|
|
@ -0,0 +1,709 @@
|
|||
<?php namespace Indikator\DevTools\Widgets;
|
||||
|
||||
use Str;
|
||||
use Url;
|
||||
use File;
|
||||
use Lang;
|
||||
use Input;
|
||||
use Config;
|
||||
use Request;
|
||||
use Response;
|
||||
use Validator;
|
||||
use Cms\Classes\Theme;
|
||||
use Indikator\DevTools\Classes\Asset;
|
||||
use Backend\Classes\WidgetBase;
|
||||
use System\Classes\PluginManager;
|
||||
use ApplicationException;
|
||||
use ValidationException;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesser;
|
||||
use October\Rain\Filesystem\Definitions as FileDefinitions;
|
||||
use RecursiveIteratorIterator;
|
||||
use RecursiveDirectoryIterator;
|
||||
use DirectoryIterator;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* CMS asset list widget.
|
||||
*
|
||||
* @package october\cms
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*/
|
||||
class AssetList extends WidgetBase
|
||||
{
|
||||
use \Backend\Traits\SelectableWidget;
|
||||
|
||||
protected $searchTerm = false;
|
||||
|
||||
protected $theme;
|
||||
|
||||
/**
|
||||
* @var string Message to display when there are no records in the list.
|
||||
*/
|
||||
public $noRecordsMessage = 'cms::lang.asset.no_list_records';
|
||||
|
||||
/**
|
||||
* @var string Message to display when the Delete button is clicked.
|
||||
*/
|
||||
public $deleteConfirmation = 'cms::lang.asset.delete_confirm';
|
||||
|
||||
/**
|
||||
* @var array Valid asset file extensions
|
||||
*/
|
||||
protected $assetExtensions;
|
||||
|
||||
public function __construct($controller, $alias)
|
||||
{
|
||||
$this->alias = $alias;
|
||||
$this->theme = Theme::getEditTheme();
|
||||
$this->selectionInputName = 'file';
|
||||
$this->assetExtensions = FileDefinitions::get('assetExtensions');
|
||||
|
||||
parent::__construct($controller, []);
|
||||
$this->bindToController();
|
||||
|
||||
$this->checkUploadPostback();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
protected function loadAssets()
|
||||
{
|
||||
$this->addCss('/modules/cms/widgets/assetlist/assets/css/assetlist.css');
|
||||
$this->addJs('/modules/cms/widgets/assetlist/assets/js/assetlist.js');
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the widget.
|
||||
* @return string
|
||||
*/
|
||||
public function render()
|
||||
{
|
||||
return $this->makePartial('body', [
|
||||
'data' => $this->getData()
|
||||
]);
|
||||
}
|
||||
|
||||
//
|
||||
// Event handlers
|
||||
//
|
||||
|
||||
public function onOpenDirectory()
|
||||
{
|
||||
$path = Input::get('path');
|
||||
if (!$this->validatePath($path)) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.asset.invalid_path'));
|
||||
}
|
||||
|
||||
$delay = Input::get('delay');
|
||||
if ($delay) {
|
||||
usleep(1000000 * $delay);
|
||||
}
|
||||
|
||||
$this->putSession('currentPath', $path);
|
||||
return [
|
||||
'#'.$this->getId('asset-list') => $this->makePartial('items', ['items' => $this->getData()])
|
||||
];
|
||||
}
|
||||
|
||||
public function onRefresh()
|
||||
{
|
||||
return [
|
||||
'#'.$this->getId('asset-list') => $this->makePartial('items', ['items' => $this->getData()])
|
||||
];
|
||||
}
|
||||
|
||||
public function onUpdate()
|
||||
{
|
||||
$this->extendSelection();
|
||||
|
||||
return $this->onRefresh();
|
||||
}
|
||||
|
||||
public function onDeleteFiles()
|
||||
{
|
||||
$fileList = Request::input('file');
|
||||
$error = null;
|
||||
$deleted = [];
|
||||
|
||||
try {
|
||||
$assetsPath = $this->getAssetsPath();
|
||||
|
||||
foreach ($fileList as $path => $selected) {
|
||||
if ($selected) {
|
||||
if (!$this->validatePath($path)) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.asset.invalid_path'));
|
||||
}
|
||||
|
||||
$fullPath = $assetsPath.'/'.$path;
|
||||
if (File::exists($fullPath)) {
|
||||
if (!File::isDirectory($fullPath)) {
|
||||
if (!@File::delete($fullPath)) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.asset.error_deleting_file',
|
||||
['name' => $path]
|
||||
));
|
||||
}
|
||||
}
|
||||
else {
|
||||
$empty = File::isDirectoryEmpty($fullPath);
|
||||
if ($empty === false) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.asset.error_deleting_dir_not_empty',
|
||||
['name' => $path]
|
||||
));
|
||||
}
|
||||
|
||||
if (!@rmdir($fullPath)) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.asset.error_deleting_dir',
|
||||
['name' => $path]
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
$deleted[] = $path;
|
||||
$this->removeSelection($path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$error = $ex->getMessage();
|
||||
}
|
||||
|
||||
return [
|
||||
'deleted' => $deleted,
|
||||
'error' => $error
|
||||
];
|
||||
}
|
||||
|
||||
public function onLoadRenamePopup()
|
||||
{
|
||||
$path = Input::get('renamePath');
|
||||
if (!$this->validatePath($path)) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.asset.invalid_path'));
|
||||
}
|
||||
|
||||
$this->vars['originalPath'] = $path;
|
||||
$this->vars['name'] = basename($path);
|
||||
|
||||
return $this->makePartial('rename_form');
|
||||
}
|
||||
|
||||
public function onApplyName()
|
||||
{
|
||||
$newName = trim(Input::get('name'));
|
||||
if (!strlen($newName)) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.asset.name_cant_be_empty'));
|
||||
}
|
||||
|
||||
if (!$this->validatePath($newName)) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.asset.invalid_path'));
|
||||
}
|
||||
|
||||
if (!$this->validateName($newName)) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.asset.invalid_name'));
|
||||
}
|
||||
|
||||
$originalPath = Input::get('originalPath');
|
||||
if (!$this->validatePath($originalPath)) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.asset.invalid_path'));
|
||||
}
|
||||
|
||||
$originalFullPath = $this->getFullPath($originalPath);
|
||||
if (!file_exists($originalFullPath)) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.asset.original_not_found'));
|
||||
}
|
||||
|
||||
if (!is_dir($originalFullPath) && !$this->validateFileType($newName)) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.asset.type_not_allowed',
|
||||
['allowed_types' => implode(', ', $this->assetExtensions)]
|
||||
));
|
||||
}
|
||||
|
||||
$newFullPath = $this->getFullPath(dirname($originalPath).'/'.$newName);
|
||||
if (file_exists($newFullPath) && $newFullPath !== $originalFullPath) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.asset.already_exists'));
|
||||
}
|
||||
|
||||
if (!@rename($originalFullPath, $newFullPath)) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.asset.error_renaming'));
|
||||
}
|
||||
|
||||
return [
|
||||
'#'.$this->getId('asset-list') => $this->makePartial('items', ['items' => $this->getData()])
|
||||
];
|
||||
}
|
||||
|
||||
public function onLoadNewDirPopup()
|
||||
{
|
||||
return $this->makePartial('new_dir_form');
|
||||
}
|
||||
|
||||
public function onNewDirectory()
|
||||
{
|
||||
$newName = trim(Input::get('name'));
|
||||
if (!strlen($newName)) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.asset.name_cant_be_empty'));
|
||||
}
|
||||
|
||||
if (!$this->validatePath($newName)) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.asset.invalid_path'));
|
||||
}
|
||||
|
||||
if (!$this->validateName($newName)) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.asset.invalid_name'));
|
||||
}
|
||||
|
||||
$newFullPath = $this->getCurrentPath().'/'.$newName;
|
||||
if (file_exists($newFullPath)) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.asset.already_exists'));
|
||||
}
|
||||
|
||||
if (!File::makeDirectory($newFullPath)) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.cms_object.error_creating_directory',
|
||||
['name' => $newName]
|
||||
));
|
||||
}
|
||||
|
||||
return [
|
||||
'#'.$this->getId('asset-list') => $this->makePartial('items', ['items' => $this->getData()])
|
||||
];
|
||||
}
|
||||
|
||||
public function onLoadMovePopup()
|
||||
{
|
||||
$fileList = Request::input('file');
|
||||
$directories = [];
|
||||
|
||||
$selectedList = array_filter($fileList, function ($value) {
|
||||
return $value == 1;
|
||||
});
|
||||
|
||||
$this->listDestinationDirectories($directories, $selectedList);
|
||||
|
||||
$this->vars['directories'] = $directories;
|
||||
$this->vars['selectedList'] = base64_encode(json_encode(array_keys($selectedList)));
|
||||
|
||||
return $this->makePartial('move_form');
|
||||
}
|
||||
|
||||
public function onMove()
|
||||
{
|
||||
$selectedList = Input::get('selectedList');
|
||||
if (!strlen($selectedList)) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.asset.selected_files_not_found'));
|
||||
}
|
||||
|
||||
$destinationDir = Input::get('dest');
|
||||
if (!strlen($destinationDir)) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.asset.select_destination_dir'));
|
||||
}
|
||||
|
||||
$destinationFullPath = $this->getFullPath($destinationDir);
|
||||
if (!file_exists($destinationFullPath) || !is_dir($destinationFullPath)) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.asset.destination_not_found'));
|
||||
}
|
||||
|
||||
$list = @json_decode(@base64_decode($selectedList));
|
||||
if ($list === false) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.asset.selected_files_not_found'));
|
||||
}
|
||||
|
||||
foreach ($list as $path) {
|
||||
if (!$this->validatePath($path)) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.asset.invalid_path'));
|
||||
}
|
||||
|
||||
$basename = basename($path);
|
||||
$originalFullPath = $this->getFullPath($path);
|
||||
$newFullPath = rtrim($destinationFullPath, '/').'/'.$basename;
|
||||
$safeDir = $this->getAssetsPath();
|
||||
|
||||
if ($originalFullPath == $newFullPath) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_file($originalFullPath)) {
|
||||
if (!@File::move($originalFullPath, $newFullPath)) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.asset.error_moving_file',
|
||||
['file' => $basename]
|
||||
));
|
||||
}
|
||||
}
|
||||
elseif (is_dir($originalFullPath)) {
|
||||
if (!@File::copyDirectory($originalFullPath, $newFullPath)) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.asset.error_moving_directory',
|
||||
['dir' => $basename]
|
||||
));
|
||||
}
|
||||
|
||||
if (strpos($originalFullPath, '../') !== false) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.asset.error_deleting_directory',
|
||||
['dir' => $basename]
|
||||
));
|
||||
}
|
||||
|
||||
if (strpos($originalFullPath, $safeDir) !== 0) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.asset.error_deleting_directory',
|
||||
['dir' => $basename]
|
||||
));
|
||||
}
|
||||
|
||||
if (!@File::deleteDirectory($originalFullPath)) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.asset.error_deleting_directory',
|
||||
['dir' => $basename]
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'#'.$this->getId('asset-list') => $this->makePartial('items', ['items' => $this->getData()])
|
||||
];
|
||||
}
|
||||
|
||||
public function onSearch()
|
||||
{
|
||||
$this->setSearchTerm(Input::get('search'));
|
||||
$this->extendSelection();
|
||||
|
||||
return $this->onRefresh();
|
||||
}
|
||||
|
||||
/*
|
||||
* Methods for the internal use
|
||||
*/
|
||||
|
||||
protected function getData()
|
||||
{
|
||||
$assetsPath = $this->getAssetsPath();
|
||||
|
||||
if (!file_exists($assetsPath) || !is_dir($assetsPath)) {
|
||||
if (!File::makeDirectory($assetsPath)) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.cms_object.error_creating_directory',
|
||||
['name' => $assetsPath]
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
$searchTerm = Str::lower($this->getSearchTerm());
|
||||
|
||||
if (!strlen($searchTerm)) {
|
||||
$currentPath = $this->getCurrentPath();
|
||||
return $this->getDirectoryContents(
|
||||
new DirectoryIterator($currentPath)
|
||||
);
|
||||
}
|
||||
|
||||
return $this->findFiles();
|
||||
}
|
||||
|
||||
protected function getAssetsPath()
|
||||
{
|
||||
return base_path().'/plugins';
|
||||
}
|
||||
|
||||
protected function getThemeFileUrl($path)
|
||||
{
|
||||
return Url::to('plugins'.$path);
|
||||
}
|
||||
|
||||
public function getCurrentRelativePath()
|
||||
{
|
||||
$path = $this->getSession('currentPath', '/');
|
||||
|
||||
if (!$this->validatePath($path)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($path == '.') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ltrim($path, '/');
|
||||
}
|
||||
|
||||
protected function getCurrentPath()
|
||||
{
|
||||
$assetsPath = $this->getAssetsPath();
|
||||
|
||||
$path = $assetsPath.'/'.$this->getCurrentRelativePath();
|
||||
if (!is_dir($path)) {
|
||||
return $assetsPath;
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
protected function getRelativePath($path)
|
||||
{
|
||||
$prefix = $this->getAssetsPath();
|
||||
|
||||
if (substr($path, 0, strlen($prefix)) == $prefix) {
|
||||
$path = substr($path, strlen($prefix));
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
protected function getFullPath($path)
|
||||
{
|
||||
return $this->getAssetsPath().'/'.ltrim($path, '/');
|
||||
}
|
||||
|
||||
protected function validatePath($path)
|
||||
{
|
||||
if (!preg_match('/^[0-9a-z\.\s_\-\/]+$/i', $path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strpos($path, '..') !== false || strpos($path, './') !== false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function validateName($name)
|
||||
{
|
||||
if (!preg_match('/^[0-9a-z\.\s_\-]+$/i', $name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strpos($name, '..') !== false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function getDirectoryContents($dir)
|
||||
{
|
||||
$editableAssetTypes = Asset::getEditableExtensions();
|
||||
|
||||
$result = $files = [];
|
||||
|
||||
foreach ($dir as $node) {
|
||||
if (substr($node->getFileName(), 0, 1) == '.') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($node->isDir() && !$node->isDot()) {
|
||||
$result[$node->getFilename()] = (object)[
|
||||
'type' => 'directory',
|
||||
'path' => File::normalizePath($this->getRelativePath($node->getPathname())),
|
||||
'name' => $node->getFilename(),
|
||||
'editable' => false
|
||||
];
|
||||
}
|
||||
elseif ($node->isFile()) {
|
||||
$files[] = (object)[
|
||||
'type' => 'file',
|
||||
'path' => File::normalizePath($this->getRelativePath($node->getPathname())),
|
||||
'name' => $node->getFilename(),
|
||||
'editable' => in_array(strtolower($node->getExtension()), $editableAssetTypes)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($files as $file) {
|
||||
$result[] = $file;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function listDestinationDirectories(&$result, $excludeList, $startDir = null, $level = 0)
|
||||
{
|
||||
if ($startDir === null) {
|
||||
$startDir = $this->getAssetsPath();
|
||||
|
||||
$result['/'] = 'assets';
|
||||
$level = 1;
|
||||
}
|
||||
|
||||
$dirs = new DirectoryIterator($startDir);
|
||||
foreach ($dirs as $node) {
|
||||
if (substr($node->getFileName(), 0, 1) == '.') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($node->isDir() && !$node->isDot()) {
|
||||
$fullPath = $node->getPathname();
|
||||
$relativePath = $this->getRelativePath($fullPath);
|
||||
if (array_key_exists($relativePath, $excludeList)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result[$relativePath] = str_repeat(' ', $level * 4).$node->getFilename();
|
||||
|
||||
$this->listDestinationDirectories($result, $excludeList, $fullPath, $level+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function getSearchTerm()
|
||||
{
|
||||
return $this->searchTerm !== false ? $this->searchTerm : $this->getSession('search');
|
||||
}
|
||||
|
||||
protected function isSearchMode()
|
||||
{
|
||||
return strlen($this->getSearchTerm());
|
||||
}
|
||||
|
||||
protected function getUpPath()
|
||||
{
|
||||
$path = $this->getCurrentRelativePath();
|
||||
if (!strlen(rtrim(ltrim($path, '/'), '/'))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return dirname($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for valid asset file extension
|
||||
* @param string
|
||||
* @return bool
|
||||
*/
|
||||
protected function validateFileType($name)
|
||||
{
|
||||
$extension = strtolower(File::extension($name));
|
||||
|
||||
if (!in_array($extension, $this->assetExtensions)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the current request to see if it is a postback containing a file upload
|
||||
* for this particular widget.
|
||||
*/
|
||||
protected function checkUploadPostback()
|
||||
{
|
||||
$fileName = null;
|
||||
|
||||
try {
|
||||
$uploadedFile = Input::file('file_data');
|
||||
|
||||
if (!is_object($uploadedFile)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$fileName = $uploadedFile->getClientOriginalName();
|
||||
|
||||
/*
|
||||
* Check valid upload
|
||||
*/
|
||||
if (!$uploadedFile->isValid()) {
|
||||
throw new ApplicationException(Lang::get('cms::lang.asset.file_not_valid'));
|
||||
}
|
||||
|
||||
/*
|
||||
* Check file size
|
||||
*/
|
||||
$maxSize = UploadedFile::getMaxFilesize();
|
||||
if ($uploadedFile->getSize() > $maxSize) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.asset.too_large',
|
||||
['max_size' => File::sizeToString($maxSize)]
|
||||
));
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for valid file extensions
|
||||
*/
|
||||
if (!$this->validateFileType($fileName)) {
|
||||
throw new ApplicationException(Lang::get(
|
||||
'cms::lang.asset.type_not_allowed',
|
||||
['allowed_types' => implode(', ', $this->assetExtensions)]
|
||||
));
|
||||
}
|
||||
|
||||
/*
|
||||
* Accept the uploaded file
|
||||
*/
|
||||
$uploadedFile->move($this->getCurrentPath(), $uploadedFile->getClientOriginalName());
|
||||
|
||||
die('success');
|
||||
}
|
||||
catch (Exception $ex) {
|
||||
$message = $fileName !== null
|
||||
? Lang::get('cms::lang.asset.error_uploading_file', ['name' => $fileName, 'error' => $ex->getMessage()])
|
||||
: $ex->getMessage();
|
||||
|
||||
die($message);
|
||||
}
|
||||
}
|
||||
|
||||
protected function setSearchTerm($term)
|
||||
{
|
||||
$this->searchTerm = trim($term);
|
||||
$this->putSession('search', $this->searchTerm);
|
||||
}
|
||||
|
||||
protected function findFiles()
|
||||
{
|
||||
$iterator = new RecursiveIteratorIterator(
|
||||
new RecursiveDirectoryIterator($this->getAssetsPath(), RecursiveDirectoryIterator::SKIP_DOTS),
|
||||
RecursiveIteratorIterator::SELF_FIRST,
|
||||
RecursiveIteratorIterator::CATCH_GET_CHILD
|
||||
);
|
||||
|
||||
$editableAssetTypes = Asset::getEditableExtensions();
|
||||
$searchTerm = Str::lower($this->getSearchTerm());
|
||||
$words = explode(' ', $searchTerm);
|
||||
|
||||
$result = [];
|
||||
foreach ($iterator as $item) {
|
||||
if (!$item->isDir()) {
|
||||
if (substr($item->getFileName(), 0, 1) == '.') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$path = $this->getRelativePath($item->getPathname());
|
||||
|
||||
if ($this->pathMatchesSearch($words, $path)) {
|
||||
$result[] = (object)[
|
||||
'type' => 'file',
|
||||
'path' => File::normalizePath($path),
|
||||
'name' => $item->getFilename(),
|
||||
'editable' => in_array(strtolower($item->getExtension()), $editableAssetTypes)
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function pathMatchesSearch(&$words, $path)
|
||||
{
|
||||
foreach ($words as $word) {
|
||||
$word = trim($word);
|
||||
if (!strlen($word)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!Str::contains(Str::lower($path), $word)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
<?= $this->makePartial('toolbar') ?>
|
||||
<div class="layout-row" id="asset-list-container" data-alias="<?= $this->alias ?>">
|
||||
<div class="layout-cell">
|
||||
<div class="layout-relative">
|
||||
<?= $this->makePartial('files', ['data'=>$data]) ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<div class="layout-absolute">
|
||||
<div class="control-scrollbar" data-control="scrollbar">
|
||||
<div class="control-assetlist" data-control="assetlist" id="<?= $this->getId('asset-list') ?>">
|
||||
<?= $this->makePartial('items', ['items'=>$data]) ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
$searchMode = $this->isSearchMode();
|
||||
|
||||
if (($upPath = $this->getUpPath()) !== null && !$searchMode):
|
||||
?>
|
||||
<p class="parent">
|
||||
<a href="<?= $upPath ?>" data-path="<?= $upPath ?>" class="link"><?= $this->getCurrentRelativePath() ?></a>
|
||||
</p>
|
||||
<?php endif ?>
|
||||
<div class="list-container animate">
|
||||
<?php if ($items): ?>
|
||||
<ul class="list">
|
||||
<?php foreach ($items as $item):
|
||||
$dataId = 'asset-'.ltrim($item->path, '/');
|
||||
?>
|
||||
<li class="<?= $item->type ?>" <?php if ($item->editable): ?>data-editable<?php endif ?> data-item-path="<?= e(ltrim($item->path, '/')) ?>" data-item-type="asset" data-id="<?= e($dataId) ?>">
|
||||
<a class="link" target="_blank" data-path="<?= $item->path ?>" href="<?= $this->getThemeFileUrl($item->path) ?>">
|
||||
<?= e($item->name) ?>
|
||||
|
||||
<?php if ($searchMode): ?>
|
||||
<span class="description">
|
||||
<?= e(dirname($item->path)) ?>
|
||||
</span>
|
||||
<?php endif ?>
|
||||
</a>
|
||||
|
||||
<div class="controls">
|
||||
<a
|
||||
href="javascript:;"
|
||||
class="control icon btn-primary oc-icon-terminal"
|
||||
title="<?= e(trans('cms::lang.asset.rename')) ?>"
|
||||
data-control="popup"
|
||||
data-request-data="renamePath: '<?= e($item->path) ?>'"
|
||||
data-handler="<?= $this->getEventHandler('onLoadRenamePopup') ?>"
|
||||
><?= e(trans('cms::lang.asset.rename')) ?></a>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="file[<?= e($item->path) ?>]" value="0">
|
||||
<div class="checkbox custom-checkbox nolabel">
|
||||
<?php $cbId = 'cb'.md5($item->path) ?>
|
||||
<input
|
||||
id="<?= $cbId ?>"
|
||||
type="checkbox"
|
||||
name="file[<?= e($item->path) ?>]"
|
||||
<?= $this->isItemSelected($item->path) ? 'checked' : null ?>
|
||||
data-request="<?= $this->getEventHandler('onSelect') ?>"
|
||||
value="1">
|
||||
<label for="<?= $cbId ?>"><?= e(trans('cms::lang.asset.select')) ?></label>
|
||||
</div>
|
||||
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
<?php else: ?>
|
||||
<p class="no-data"><?= e(trans($this->noRecordsMessage)) ?></p>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?= Form::open([
|
||||
'data-request'=>$this->getEventHandler('onMove'),
|
||||
'data-request-success'=>"\$(this).trigger('close.oc.popup')",
|
||||
'data-stripe-load-indicator'=>1,
|
||||
'id'=>'asset-move-popup-form'
|
||||
]) ?>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(trans('cms::lang.asset.move_popup_title')) ?></h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label><?= e(trans('cms::lang.asset.move_destination')) ?></label>
|
||||
<select
|
||||
class="form-control custom-select"
|
||||
name="dest"
|
||||
data-placeholder="<?= e(trans('cms::lang.asset.move_please_select')) ?>">
|
||||
<option></option>
|
||||
<?php foreach ($directories as $path=>$directory):?>
|
||||
<option value="<?= e($path) ?>"><?= e($directory) ?></option>
|
||||
<?php endforeach ?>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="selectedList" value="<?= e($selectedList) ?>">
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('cms::lang.asset.move_button')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
<?= Form::close() ?>
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?= Form::open([
|
||||
'data-request'=>$this->getEventHandler('onNewDirectory'),
|
||||
'data-request-success'=>"\$(this).trigger('close.oc.popup')",
|
||||
'data-stripe-load-indicator'=>1,
|
||||
'id'=>'asset-new-dir-popup-form'
|
||||
]) ?>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(trans('cms::lang.asset.directory_popup_title')) ?></h4>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label><?= e(trans('cms::lang.asset.directory_name')) ?></label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
value=""
|
||||
class="form-control"
|
||||
autocomplete="off">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.create')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
setTimeout(
|
||||
function() {
|
||||
$('#asset-new-dir-popup-form input.form-control').focus()
|
||||
}, 310
|
||||
)
|
||||
</script>
|
||||
<?= Form::close() ?>
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
<?= Form::ajax($this->getEventHandler('onApplyName'), [
|
||||
'success' => "\$el.trigger('close.oc.popup');",
|
||||
'data-stripe-load-indicator' => 1,
|
||||
'id' => 'asset-rename-popup-form'
|
||||
]) ?>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="popup">×</button>
|
||||
<h4 class="modal-title"><?= e(trans('cms::lang.asset.rename_popup_title')) ?></h4>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label><?= e(trans('cms::lang.asset.rename_new_name')) ?></label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
value="<?= e($name) ?>"
|
||||
class="form-control"
|
||||
autocomplete="off">
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="originalPath" value="<?= e($originalPath) ?>">
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary">
|
||||
<?= e(trans('backend::lang.form.apply')) ?>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-default"
|
||||
data-dismiss="popup">
|
||||
<?= e(trans('backend::lang.form.cancel')) ?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
setTimeout(
|
||||
function() {
|
||||
$('#asset-rename-popup-form input.form-control').focus()
|
||||
}, 310
|
||||
)
|
||||
</script>
|
||||
<?= Form::close() ?>
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
<div class="layout-row min-size">
|
||||
<div class="control-toolbar toolbar-padded">
|
||||
|
||||
<!-- Control Panel -->
|
||||
<div class="toolbar-item" data-calculate-width>
|
||||
<div class="btn-group">
|
||||
<div class="dropdown last">
|
||||
<button type="button" class="btn btn-default oc-icon-plus"
|
||||
data-control="create-asset"
|
||||
data-toggle="dropdown"
|
||||
><?= e(trans('cms::lang.sidebar.add')) ?></button>
|
||||
|
||||
<ul class="dropdown-menu offset-left" role="menu" data-dropdown-title="<?= e(trans('cms::lang.asset.drop_down_add_title')) ?>">
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1" href="javascript:;" data-control="create-template" class="oc-icon-file-text-o"><?= e(trans('cms::lang.asset.create_file')) ?></a></li>
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1" href="javascript:;" data-control="upload-assets" class="oc-icon-upload"><?= e(trans('cms::lang.asset.upload_files')) ?></a></li>
|
||||
<li role="presentation" class="divider"></li>
|
||||
<li role="presentation"><a
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
href="javascript:;"
|
||||
class="oc-icon-folder-o"
|
||||
data-control="popup"
|
||||
data-handler="<?= $this->getEventHandler('onLoadNewDirPopup') ?>"
|
||||
><?= e(trans('cms::lang.asset.create_directory')) ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="dropdown hide"
|
||||
id="<?= $this->getId('tools-button') ?>"
|
||||
data-trigger-action="show"
|
||||
data-trigger="<?= '#'.$this->getId('asset-list') ?> input[type=checkbox]"
|
||||
data-trigger-condition="checked">
|
||||
<button type="button" class="btn btn-default empty oc-icon-wrench last"
|
||||
data-toggle="dropdown"
|
||||
data-control="asset-tools"
|
||||
></button>
|
||||
|
||||
<ul class="dropdown-menu" role="menu" data-dropdown-title="<?= e(trans('cms::lang.asset.drop_down_operation_title')) ?>">
|
||||
<li role="presentation"><a
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
href="javascript:;"
|
||||
data-control="delete-asset"
|
||||
data-confirmation="<?= e(trans($this->deleteConfirmation)) ?>"
|
||||
class="oc-icon-trash-o"
|
||||
><?= e(trans('cms::lang.asset.delete')) ?></a></li>
|
||||
<li role="presentation"><a
|
||||
role="menuitem"
|
||||
tabindex="-1"
|
||||
href="javascript:;"
|
||||
class="oc-icon-angle-double-right"
|
||||
data-control="popup"
|
||||
data-handler="<?= $this->getEventHandler('onLoadMovePopup') ?>"
|
||||
><?= e(trans('cms::lang.asset.move')) ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Asset Search -->
|
||||
<div class="relative toolbar-item loading-indicator-container size-input-text">
|
||||
<input
|
||||
type="text"
|
||||
name="search"
|
||||
value="<?= e($this->getSearchTerm()) ?>"
|
||||
class="form-control icon search" autocomplete="off"
|
||||
placeholder="<?= e(trans('cms::lang.sidebar.search')) ?>"
|
||||
data-track-input
|
||||
data-load-indicator
|
||||
data-load-indicator-opaque
|
||||
data-request-success="$('<?= '#'.$this->getId('tools-button') ?>').trigger('oc.triggerOn.update')"
|
||||
data-request="<?= $this->getEventHandler('onSearch') ?>"
|
||||
>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php namespace October\Demo;
|
||||
|
||||
/**
|
||||
* The plugin.php file (called the plugin initialization script) defines the plugin information class.
|
||||
*/
|
||||
|
||||
use System\Classes\PluginBase;
|
||||
|
||||
class Plugin extends PluginBase
|
||||
{
|
||||
|
||||
public function pluginDetails()
|
||||
{
|
||||
return [
|
||||
'name' => 'October Demo',
|
||||
'description' => 'Provides features used by the provided demonstration theme.',
|
||||
'author' => 'Alexey Bobkov, Samuel Georges',
|
||||
'icon' => 'icon-leaf'
|
||||
];
|
||||
}
|
||||
|
||||
public function registerComponents()
|
||||
{
|
||||
return [
|
||||
'\October\Demo\Components\Todo' => 'demoTodo'
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
<?php namespace October\Demo\Components;
|
||||
|
||||
use Cms\Classes\ComponentBase;
|
||||
use ApplicationException;
|
||||
|
||||
class Todo extends ComponentBase
|
||||
{
|
||||
|
||||
public function componentDetails()
|
||||
{
|
||||
return [
|
||||
'name' => 'Todo List',
|
||||
'description' => 'Implements a simple to-do list.'
|
||||
];
|
||||
}
|
||||
|
||||
public function defineProperties()
|
||||
{
|
||||
return [
|
||||
'max' => [
|
||||
'description' => 'The most amount of todo items allowed',
|
||||
'title' => 'Max items',
|
||||
'default' => 10,
|
||||
'type' => 'string',
|
||||
'validationPattern' => '^[0-9]+$',
|
||||
'validationMessage' => 'The Max Items value is required and should be integer.'
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function onAddItem()
|
||||
{
|
||||
$items = post('items', []);
|
||||
|
||||
if (count($items) >= $this->property('max')) {
|
||||
throw new ApplicationException(sprintf('Sorry only %s items are allowed.', $this->property('max')));
|
||||
}
|
||||
|
||||
if (($newItem = post('newItem')) != '') {
|
||||
$items[] = $newItem;
|
||||
}
|
||||
|
||||
$this->page['items'] = $items;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<form
|
||||
role="form"
|
||||
data-request="{{ __SELF__ }}::onAddItem"
|
||||
data-request-update="'{{ __SELF__ }}::list': '#result'"
|
||||
data-request-success="$('#input-item').val('')"
|
||||
data-request-flash>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">To Do List</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="input-group">
|
||||
<input type="text" id="input-item" class="form-control" value="" name="newItem" placeholder="What needs to be done?">
|
||||
<span class="input-group-btn">
|
||||
<button type="submit" class="btn btn btn-primary" data-attach-loading>Add</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-group" id="result">
|
||||
</ul>
|
||||
</div>
|
||||
</form>
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{% for item in items %}
|
||||
<li class="list-group-item">
|
||||
<input type="hidden" name="items[]" value="{{ item }}" />
|
||||
|
||||
{{ item }}
|
||||
|
||||
<button type="button"
|
||||
class="close pull-right"
|
||||
aria-hidden="true"
|
||||
onclick="$(this).closest('li').remove()">×</button>
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"name": "october/demo-plugin",
|
||||
"type": "october-plugin",
|
||||
"description": "Demo OctoberCMS plugin",
|
||||
"keywords": ["october", "cms", "demo", "plugin"],
|
||||
"license": "MIT",
|
||||
|
||||
"authors": [
|
||||
{
|
||||
"name": "Alexey Bobkov",
|
||||
"email": "aleksey.bobkov@gmail.com",
|
||||
"role": "Co-founder"
|
||||
},
|
||||
{
|
||||
"name": "Samuel Georges",
|
||||
"email": "daftspunky@gmail.com",
|
||||
"role": "Co-founder"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.4",
|
||||
"composer/installers": "~1.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"October\\Demo\\": ""
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
1.0.1: First version of Demo
|
||||
|
|
@ -122,6 +122,16 @@ class Post extends Model
|
|||
*/
|
||||
public function filterFields($fields, $context = null)
|
||||
{
|
||||
if(isset($fields->locale)){
|
||||
if($this->locale == 'en')
|
||||
{
|
||||
$fields->tags_en->hidden = false;
|
||||
$fields->tags_ru->hidden = true;
|
||||
}else{
|
||||
$fields->tags_en->hidden = true;
|
||||
$fields->tags_ru->hidden = false;
|
||||
}
|
||||
}
|
||||
if (!isset($fields->published, $fields->published_at)) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?php namespace Tps\Reklama;
|
||||
|
||||
use Config;
|
||||
use System\Classes\PluginBase;
|
||||
|
||||
class Plugin extends PluginBase
|
||||
|
|
@ -31,4 +32,20 @@ class Plugin extends PluginBase
|
|||
]
|
||||
];
|
||||
}
|
||||
|
||||
public function registerMarkupTags() {
|
||||
return [
|
||||
'filters' => [
|
||||
// A global function, i.e str_plural()
|
||||
'media_cdn' => [$this, 'absoluteMediaUrl'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
public function absoluteMediaUrl($file) {
|
||||
$original = \System\Classes\MediaLibrary::url($file);
|
||||
|
||||
// modify here and return
|
||||
return Config::get('cms.cdn').$original;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
data-request-data = '[{id:{{reklama.id}}},{url:"{{reklama.url}}"}]'
|
||||
{% endif %}
|
||||
>
|
||||
<img src="{{reklama.media|media}}" alt="{{reklama.title}}">
|
||||
<img src="{{reklama.media|media_cdn}}" alt="{{reklama.title}}">
|
||||
</a>
|
||||
{% endfor %}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,5 +5,5 @@
|
|||
data-request-data = '[{id:{{reklama.id}}},{url:"{{reklama.url}}"}]'
|
||||
{% endif %}
|
||||
>
|
||||
<img src="{{reklama.media|media}}" alt="{{reklama.title}}">
|
||||
<img src="{{reklama.media|media_cdn}}" alt="{{reklama.title}}">
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,56 @@
|
|||
<div class="sideSlider">
|
||||
{% if group and group.adds %}
|
||||
<div class="advertisiment_{{__SELF__}}">
|
||||
{% for reklama in group.adds %}
|
||||
<a class="sideSlider__item"
|
||||
<a class="advertisiment__item"
|
||||
{% if reklama.enable_stats and reklama.url !="#" %}
|
||||
data-request="{{__SELF__}}::onRedirect"
|
||||
data-request-data = '[{id:{{reklama.id}}},{url:"{{reklama.url}}"}]'
|
||||
{% endif %}
|
||||
>
|
||||
<img src="{{reklama.media|media}}" alt="{{reklama.title}}">
|
||||
<img src="{{reklama.media|media_cdn}}" alt="{{reklama.title}}">
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// advertisement start
|
||||
$(function () {
|
||||
var $slideshow = $(".advertisiment_{{__SELF__}}");
|
||||
var ImagePauses = {{group.adds.pluck('display')}};
|
||||
|
||||
// Init
|
||||
$slideshow.slick({
|
||||
dots: false,
|
||||
arrows: false,
|
||||
infinite: true,
|
||||
initialSlide: 0,
|
||||
autoplay: true,
|
||||
autoplaySpeed: ImagePauses[0] * 1000,
|
||||
fade: true,
|
||||
cssEase: "linear",
|
||||
adaptiveHeight: true,
|
||||
});
|
||||
|
||||
// Sliding settings
|
||||
$slideshow.on("afterChange", function (event, slick, currentSlide) {
|
||||
// Console log, can be removed
|
||||
console.log(
|
||||
"Current slide: " +
|
||||
currentSlide +
|
||||
". Setting speed to: " +
|
||||
ImagePauses[currentSlide]
|
||||
);
|
||||
|
||||
// Update autoplay speed according to slide index
|
||||
$slideshow.slick(
|
||||
"slickSetOption",
|
||||
"autoplaySpeed",
|
||||
ImagePauses[currentSlide] * 1000,
|
||||
true
|
||||
);
|
||||
});
|
||||
});
|
||||
// advertisement end
|
||||
</script>
|
||||
|
||||
{% endif %}
|
||||
|
|
|
|||
|
|
@ -251,4 +251,6 @@
|
|||
'AhmadFatoni\\ApiGenerator\\Controllers\\ApiGeneratorController' => 'plugins/ahmadfatoni/apigenerator/controllers/ApiGeneratorController.php',
|
||||
'Zen\\Robots\\Controllers\\Generate' => 'plugins/zen/robots/controllers/Generate.php',
|
||||
'Tps\\Reklama\\Widgets\\Stats' => 'plugins/tps/reklama/widgets/Stats.php',
|
||||
'indikator\\devtools\\Plugin' => 'plugins/indikator/devtools/Plugin.php',
|
||||
'Indikator\\DevTools\\Models\\Settings' => 'plugins/indikator/devtools/models/Settings.php',
|
||||
);
|
||||
|
|
@ -121,41 +121,4 @@ if (
|
|||
);
|
||||
// lazyload end
|
||||
// =============================================
|
||||
// advertisement start
|
||||
$(function () {
|
||||
var $slideshow = $(".advertisiment");
|
||||
var ImagePauses = [6000, 2000, 3000, 10000];
|
||||
|
||||
// Init
|
||||
$slideshow.slick({
|
||||
dots: false,
|
||||
arrows: false,
|
||||
infinite: true,
|
||||
initialSlide: 0,
|
||||
autoplay: true,
|
||||
autoplaySpeed: ImagePauses[0],
|
||||
fade: true,
|
||||
cssEase: "linear",
|
||||
adaptiveHeight: true,
|
||||
});
|
||||
|
||||
// Sliding settings
|
||||
$slideshow.on("afterChange", function (event, slick, currentSlide) {
|
||||
// Console log, can be removed
|
||||
console.log(
|
||||
"Current slide: " +
|
||||
currentSlide +
|
||||
". Setting speed to: " +
|
||||
ImagePauses[currentSlide]
|
||||
);
|
||||
|
||||
// Update autoplay speed according to slide index
|
||||
$slideshow.slick(
|
||||
"slickSetOption",
|
||||
"autoplaySpeed",
|
||||
ImagePauses[currentSlide],
|
||||
true
|
||||
);
|
||||
});
|
||||
});
|
||||
// advertisement end
|
||||
|
|
|
|||
|
|
@ -1,25 +1,26 @@
|
|||
items:
|
||||
-
|
||||
title: Главная
|
||||
nesting: null
|
||||
type: url
|
||||
url: /
|
||||
code: ''
|
||||
reference: null
|
||||
cmsPage: null
|
||||
replace: null
|
||||
viewBag:
|
||||
locale:
|
||||
en:
|
||||
title: Home
|
||||
url: ''
|
||||
isHidden: '1'
|
||||
isHidden: '0'
|
||||
cssClass: ''
|
||||
isExternal: '0'
|
||||
-
|
||||
title: Рубрики
|
||||
nesting: null
|
||||
type: header
|
||||
url: null
|
||||
code: ''
|
||||
reference: null
|
||||
cmsPage: null
|
||||
replace: null
|
||||
viewBag:
|
||||
locale:
|
||||
en:
|
||||
|
|
@ -31,8 +32,11 @@ items:
|
|||
items:
|
||||
-
|
||||
title: 'New menu item'
|
||||
nesting: null
|
||||
type: all-blog-categories
|
||||
url: null
|
||||
code: ''
|
||||
reference: null
|
||||
cmsPage: category
|
||||
replace: 1
|
||||
viewBag:
|
||||
|
|
|
|||
|
|
@ -18,10 +18,20 @@ code = "media"
|
|||
type = "carousel"
|
||||
random = 0
|
||||
|
||||
[adverts adv_bank]
|
||||
code = "bank"
|
||||
type = "slider"
|
||||
random = 0
|
||||
|
||||
[adverts samsung]
|
||||
code = "samsung"
|
||||
type = "single"
|
||||
random = 0
|
||||
|
||||
[adverts adv_bottom]
|
||||
code = "bottom"
|
||||
type = "slider"
|
||||
random = 0
|
||||
==
|
||||
{% partial 'slider' %}
|
||||
|
||||
|
|
@ -31,20 +41,6 @@ random = 0
|
|||
<section class="banner">
|
||||
<div class="auto__container">
|
||||
<div class="banner__inner">
|
||||
<div class="advertisement">
|
||||
<a href="#" class="advertisiment__item">
|
||||
<img src="{{'assets/images/example.svg'|theme}}" alt="">
|
||||
</a>
|
||||
<a href="#" class="advertisiment__item">
|
||||
<img src="{{'assets/images/example.svg'|theme}}" alt="">
|
||||
</a>
|
||||
<a href="#" class="advertisiment__item">
|
||||
<img src="{{'assets/images/example.svg'|theme}}" alt="">
|
||||
</a>
|
||||
<a href="#" class="advertisiment__item">
|
||||
<img src="{{'assets/images/example.svg'|theme}}" alt="">
|
||||
</a>
|
||||
</div>
|
||||
{% component 'samsung' %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -63,21 +59,7 @@ random = 0
|
|||
<div class="main__inner">
|
||||
<div class="main__content">
|
||||
<div class="main__banner">
|
||||
<div class="advertisement">
|
||||
<a href="#" class="advertisiment__item">
|
||||
<img src="{{'assets/images/example.svg'|theme}}" alt="">
|
||||
</a>
|
||||
<a href="#" class="advertisiment__item">
|
||||
<img src="{{'assets/images/example.svg'|theme}}" alt="">
|
||||
</a>
|
||||
<a href="#" class="advertisiment__item">
|
||||
<img src="{{'assets/images/example.svg'|theme}}" alt="">
|
||||
</a>
|
||||
<a href="#" class="advertisiment__item">
|
||||
<img src="{{'assets/images/example.svg'|theme}}" alt="">
|
||||
</a>
|
||||
</div>
|
||||
<!--{% component 'reklama' %}-->
|
||||
{% component 'adv_bank' %}
|
||||
</div>
|
||||
<div class="main__body">
|
||||
<div class="main__body-row">
|
||||
|
|
@ -86,20 +68,7 @@ random = 0
|
|||
</div>
|
||||
</div>
|
||||
<div class="main__banner">
|
||||
<div class="advertisement">
|
||||
<a href="#" class="advertisiment__item">
|
||||
<img src="{{'assets/images/example.svg'|theme}}" alt="">
|
||||
</a>
|
||||
<a href="#" class="advertisiment__item">
|
||||
<img src="{{'assets/images/example.svg'|theme}}" alt="">
|
||||
</a>
|
||||
<a href="#" class="advertisiment__item">
|
||||
<img src="{{'assets/images/example.svg'|theme}}" alt="">
|
||||
</a>
|
||||
<a href="#" class="advertisiment__item">
|
||||
<img src="{{'assets/images/example.svg'|theme}}" alt="">
|
||||
</a>
|
||||
</div>
|
||||
{% component 'adv_bottom' %}
|
||||
</div>
|
||||
<div class="main__body">
|
||||
<div class="main__body-row">
|
||||
|
|
@ -108,20 +77,7 @@ random = 0
|
|||
</div>
|
||||
</div>
|
||||
<div class="main__banner">
|
||||
<div class="advertisement">
|
||||
<a href="#" class="advertisiment__item">
|
||||
<img src="{{'assets/images/example.svg'|theme}}" alt="">
|
||||
</a>
|
||||
<a href="#" class="advertisiment__item">
|
||||
<img src="{{'assets/images/example.svg'|theme}}" alt="">
|
||||
</a>
|
||||
<a href="#" class="advertisiment__item">
|
||||
<img src="{{'assets/images/example.svg'|theme}}" alt="">
|
||||
</a>
|
||||
<a href="#" class="advertisiment__item">
|
||||
<img src="{{'assets/images/example.svg'|theme}}" alt="">
|
||||
</a>
|
||||
</div>
|
||||
<!-- {% component 'slider' %}-->
|
||||
</div>
|
||||
<div class="main__body">
|
||||
<div class="main__body-row">
|
||||
|
|
@ -130,20 +86,7 @@ random = 0
|
|||
</div>
|
||||
</div>
|
||||
<div class="main__banner">
|
||||
<div class="advertisement">
|
||||
<a href="#" class="advertisiment__item">
|
||||
<img src="{{'assets/images/example.svg'|theme}}" alt="">
|
||||
</a>
|
||||
<a href="#" class="advertisiment__item">
|
||||
<img src="{{'assets/images/example.svg'|theme}}" alt="">
|
||||
</a>
|
||||
<a href="#" class="advertisiment__item">
|
||||
<img src="{{'assets/images/example.svg'|theme}}" alt="">
|
||||
</a>
|
||||
<a href="#" class="advertisiment__item">
|
||||
<img src="{{'assets/images/example.svg'|theme}}" alt="">
|
||||
</a>
|
||||
</div>
|
||||
<!-- {% component 'adv_bottom' %}-->
|
||||
</div>
|
||||
{% partial 'index/partner_news'%}
|
||||
</div>
|
||||
|
|
@ -183,4 +126,4 @@ random = 0
|
|||
|
||||
{% put scripts %}
|
||||
<script src="{{['assets/slick/slick.min.js','assets/js/lazy.js','assets/js/main.js']|theme}}"></script>
|
||||
{% endput %}
|
||||
{% endput %}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ slug = "{{ :slug }}"
|
|||
</h1>
|
||||
{% if post.featured_image %}
|
||||
<picture class="news__image">
|
||||
<img src="{{post.featured_image|media|modify({filter:'post'})}}" alt="{{post.title}}">
|
||||
<img src="{{post.featured_image|media_cdn|modify({filter:'post'})}}" alt="{{post.title}}">
|
||||
</picture>
|
||||
{% endif %}
|
||||
</header>
|
||||
|
|
@ -72,5 +72,29 @@ slug = "{{ :slug }}"
|
|||
update: { view: '@#view' },
|
||||
|
||||
})
|
||||
var re = /\[video poster=\"(.+?)\".+?mp4=\"(.+?)\"/g;
|
||||
// re = /(\S+)=["']?((?:.(?!["']?\s+(?:\S+)=|\s*\/?[>"']))+.)["']?/g
|
||||
var ptags = document.querySelectorAll('p');
|
||||
|
||||
ptags.forEach(s =>{
|
||||
|
||||
var m;
|
||||
m = re.exec(s.innerText);
|
||||
if (m) {
|
||||
var video =document.createElement('video');
|
||||
video.setAttribute('src',m[2]);
|
||||
video.setAttribute('controls',"")
|
||||
video.setAttribute('width',"100%")
|
||||
video.setAttribute('type',"video/mp4")
|
||||
video.setAttribute('poster',m[1])
|
||||
s.parentNode.replaceChild(video,s);
|
||||
|
||||
console.log(m);
|
||||
console.log(m[1]);
|
||||
}
|
||||
else{
|
||||
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endput %}
|
||||
{% endput %}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
{{'page.more'|_}}
|
||||
</a>
|
||||
</div>
|
||||
<a href="{{posts.first.featured_image|media| modify({ filter: 'thumb-medium' })}}" class="primary progressive replace">
|
||||
<a href="{{posts.first.featured_image|media_cdn| modify({ filter: 'thumb-medium' })}}" class="primary progressive replace">
|
||||
<img class="preview" src="{{'assets/images/news/1.jpg'|theme}}" alt="{{posts.first.title}}">
|
||||
</a>
|
||||
<div class="main__body-card">
|
||||
|
|
@ -32,7 +32,7 @@
|
|||
</a>
|
||||
</div>
|
||||
|
||||
{% partial 'index/category_post_item' post = posts.1 %}
|
||||
{% partial 'index/category_post_item' post = posts.first %}
|
||||
{% partial 'index/category_post_item' post = posts.last %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
<div class="event__body">
|
||||
<div class="event__body-row">
|
||||
<div class="event__body-image">
|
||||
<a href="{{firstPost.featured_image|media| modify({ filter: 'thumb-large' })}}" class="primary progressive replace">
|
||||
<a href="{{firstPost.featured_image|media_cdn| modify({ filter: 'thumb-large' })}}" class="primary progressive replace">
|
||||
<img class="preview" src="{{'assets/images/news/1.jpg'|theme}}" alt="{{firstPost.title}}">
|
||||
</a>
|
||||
<div class="event__body-card">
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<div class="heading__row">
|
||||
<a href="{{post.featured_image|media}}" class="heading__image primary progressive replace">
|
||||
<a href="{{post.featured_image|media_cdn}}" class="heading__image primary progressive replace">
|
||||
<!-- <picture>-->
|
||||
<!-- <source media="min-width:650px" srcset="{{post.featured_image|media}}">-->
|
||||
<!-- <img class="preview" src="{{post.featured_image|media}}" alt="">-->
|
||||
<!-- <source media="min-width:650px" srcset="{{post.featured_image|media_cdn}}">-->
|
||||
<!-- <img class="preview" src="{{post.featured_image|media_cdn}}" alt="">-->
|
||||
<!-- </picture>-->
|
||||
<img class="preview" src="{{'assets/images/news/1.jpg'|theme}}" alt="">
|
||||
</a>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ postPage = "post"
|
|||
<div class="slider__inner">
|
||||
{% for post in posts %}
|
||||
<div class="slider__item">
|
||||
<img src="{{post.featured_image|media| modify({ filter: 'slider' })}}" alt="{{post.title}}">
|
||||
<img src="{{post.featured_image|media_cdn| modify({ filter: 'slider' })}}" alt="{{post.title}}">
|
||||
{% if post.categories.count()>0%}
|
||||
<div class="slider__item-category">
|
||||
{{post.categories.implode('name', ', ')}}
|
||||
|
|
|
|||
|
|
@ -6,14 +6,7 @@ $vendorDir = dirname(dirname(__FILE__));
|
|||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'ArithmeticError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php',
|
||||
'AssertionError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php',
|
||||
'DivisionByZeroError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php',
|
||||
'Error' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/Error.php',
|
||||
'JSMin' => $vendorDir . '/linkorb/jsmin-php/src/jsmin-1.1.1.php',
|
||||
'JSMinException' => $vendorDir . '/linkorb/jsmin-php/src/jsmin-1.1.1.php',
|
||||
'Normalizer' => $vendorDir . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
|
||||
'ParseError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/ParseError.php',
|
||||
'SessionUpdateTimestampHandlerInterface' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php',
|
||||
'TypeError' => $vendorDir . '/symfony/polyfill-php70/Resources/stubs/TypeError.php',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -8,9 +8,7 @@ $baseDir = dirname($vendorDir);
|
|||
return array(
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'023d27dca8066ef29e6739335ea73bad' => $vendorDir . '/symfony/polyfill-php70/bootstrap.php',
|
||||
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'bd9634f2d41831496de0d3dfe4c94881' => $vendorDir . '/symfony/polyfill-php56/bootstrap.php',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
|
||||
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||
|
|
|
|||
|
|
@ -12,6 +12,5 @@ return array(
|
|||
'Parsedown' => array($vendorDir . '/erusev/parsedown'),
|
||||
'Less' => array($vendorDir . '/october/rain/src/Parse/Assetic/Less/lib'),
|
||||
'Doctrine\\DBAL\\' => array($vendorDir . '/doctrine/dbal/lib'),
|
||||
'Doctrine\\Common\\Collections\\' => array($vendorDir . '/doctrine/collections/lib'),
|
||||
'Assetic' => array($vendorDir . '/kriswallsmith/assetic/src'),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,19 +7,17 @@ $baseDir = dirname($vendorDir);
|
|||
|
||||
return array(
|
||||
'XdgBaseDir\\' => array($vendorDir . '/dnoegel/php-xdg-base-dir/src'),
|
||||
'Wikimedia\\Composer\\' => array($vendorDir . '/wikimedia/composer-merge-plugin/src'),
|
||||
'Twig\\' => array($vendorDir . '/twig/twig/src'),
|
||||
'TijsVerkoyen\\CssToInlineStyles\\' => array($vendorDir . '/tijsverkoyen/css-to-inline-styles/src'),
|
||||
'System\\' => array($baseDir . '/modules/system'),
|
||||
'Symfony\\Polyfill\\Util\\' => array($vendorDir . '/symfony/polyfill-util'),
|
||||
'Symfony\\Polyfill\\Php72\\' => array($vendorDir . '/symfony/polyfill-php72'),
|
||||
'Symfony\\Polyfill\\Php70\\' => array($vendorDir . '/symfony/polyfill-php70'),
|
||||
'Symfony\\Polyfill\\Php56\\' => array($vendorDir . '/symfony/polyfill-php56'),
|
||||
'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'),
|
||||
'Symfony\\Polyfill\\Intl\\Normalizer\\' => array($vendorDir . '/symfony/polyfill-intl-normalizer'),
|
||||
'Symfony\\Polyfill\\Intl\\Idn\\' => array($vendorDir . '/symfony/polyfill-intl-idn'),
|
||||
'Symfony\\Polyfill\\Iconv\\' => array($vendorDir . '/symfony/polyfill-iconv'),
|
||||
'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'),
|
||||
'Symfony\\Contracts\\Translation\\' => array($vendorDir . '/symfony/translation-contracts'),
|
||||
'Symfony\\Contracts\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher-contracts'),
|
||||
'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'),
|
||||
'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'),
|
||||
'Symfony\\Component\\Translation\\' => array($vendorDir . '/symfony/translation'),
|
||||
|
|
@ -39,8 +37,8 @@ return array(
|
|||
'Psr\\Container\\' => array($vendorDir . '/psr/container/src'),
|
||||
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
|
||||
'October\\Rain\\' => array($vendorDir . '/october/rain/src'),
|
||||
'October\\Demo\\' => array($baseDir . '/plugins/october/demo'),
|
||||
'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'),
|
||||
'League\\MimeTypeDetection\\' => array($vendorDir . '/league/mime-type-detection/src'),
|
||||
'League\\Flysystem\\' => array($vendorDir . '/league/flysystem/src'),
|
||||
'League\\Csv\\' => array($vendorDir . '/league/csv/src'),
|
||||
'Leafo\\ScssPhp\\' => array($vendorDir . '/leafo/scssphp/src'),
|
||||
|
|
@ -51,8 +49,10 @@ return array(
|
|||
'Illuminate\\' => array($vendorDir . '/laravel/framework/src/Illuminate'),
|
||||
'Egulias\\EmailValidator\\' => array($vendorDir . '/egulias/email-validator/src'),
|
||||
'Dotenv\\' => array($vendorDir . '/vlucas/phpdotenv/src'),
|
||||
'Doctrine\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib/Doctrine/Inflector'),
|
||||
'Doctrine\\Common\\Lexer\\' => array($vendorDir . '/doctrine/lexer/lib/Doctrine/Common/Lexer'),
|
||||
'Doctrine\\Common\\Inflector\\' => array($vendorDir . '/doctrine/inflector/lib/Doctrine/Common/Inflector'),
|
||||
'Doctrine\\Common\\Collections\\' => array($vendorDir . '/doctrine/collections/lib/Doctrine/Common/Collections'),
|
||||
'Doctrine\\Common\\Cache\\' => array($vendorDir . '/doctrine/cache/lib/Doctrine/Common/Cache'),
|
||||
'Doctrine\\Common\\Annotations\\' => array($vendorDir . '/doctrine/annotations/lib/Doctrine/Common/Annotations'),
|
||||
'Doctrine\\Common\\' => array($vendorDir . '/doctrine/common/lib/Doctrine/Common'),
|
||||
|
|
|
|||
|
|
@ -9,9 +9,7 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
public static $files = array (
|
||||
'0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'023d27dca8066ef29e6739335ea73bad' => __DIR__ . '/..' . '/symfony/polyfill-php70/bootstrap.php',
|
||||
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'bd9634f2d41831496de0d3dfe4c94881' => __DIR__ . '/..' . '/symfony/polyfill-php56/bootstrap.php',
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
|
||||
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||
|
|
@ -32,10 +30,6 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
array (
|
||||
'XdgBaseDir\\' => 11,
|
||||
),
|
||||
'W' =>
|
||||
array (
|
||||
'Wikimedia\\Composer\\' => 19,
|
||||
),
|
||||
'T' =>
|
||||
array (
|
||||
'Twig\\' => 5,
|
||||
|
|
@ -44,15 +38,14 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
'S' =>
|
||||
array (
|
||||
'System\\' => 7,
|
||||
'Symfony\\Polyfill\\Util\\' => 22,
|
||||
'Symfony\\Polyfill\\Php72\\' => 23,
|
||||
'Symfony\\Polyfill\\Php70\\' => 23,
|
||||
'Symfony\\Polyfill\\Php56\\' => 23,
|
||||
'Symfony\\Polyfill\\Mbstring\\' => 26,
|
||||
'Symfony\\Polyfill\\Intl\\Normalizer\\' => 33,
|
||||
'Symfony\\Polyfill\\Intl\\Idn\\' => 26,
|
||||
'Symfony\\Polyfill\\Iconv\\' => 23,
|
||||
'Symfony\\Polyfill\\Ctype\\' => 23,
|
||||
'Symfony\\Contracts\\Translation\\' => 30,
|
||||
'Symfony\\Contracts\\EventDispatcher\\' => 34,
|
||||
'Symfony\\Component\\Yaml\\' => 23,
|
||||
'Symfony\\Component\\VarDumper\\' => 28,
|
||||
'Symfony\\Component\\Translation\\' => 30,
|
||||
|
|
@ -81,7 +74,6 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
'O' =>
|
||||
array (
|
||||
'October\\Rain\\' => 13,
|
||||
'October\\Demo\\' => 13,
|
||||
),
|
||||
'M' =>
|
||||
array (
|
||||
|
|
@ -89,6 +81,7 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
),
|
||||
'L' =>
|
||||
array (
|
||||
'League\\MimeTypeDetection\\' => 25,
|
||||
'League\\Flysystem\\' => 17,
|
||||
'League\\Csv\\' => 11,
|
||||
'Leafo\\ScssPhp\\' => 14,
|
||||
|
|
@ -111,8 +104,10 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
'D' =>
|
||||
array (
|
||||
'Dotenv\\' => 7,
|
||||
'Doctrine\\Inflector\\' => 19,
|
||||
'Doctrine\\Common\\Lexer\\' => 22,
|
||||
'Doctrine\\Common\\Inflector\\' => 26,
|
||||
'Doctrine\\Common\\Collections\\' => 28,
|
||||
'Doctrine\\Common\\Cache\\' => 22,
|
||||
'Doctrine\\Common\\Annotations\\' => 28,
|
||||
'Doctrine\\Common\\' => 16,
|
||||
|
|
@ -134,10 +129,6 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
array (
|
||||
0 => __DIR__ . '/..' . '/dnoegel/php-xdg-base-dir/src',
|
||||
),
|
||||
'Wikimedia\\Composer\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/wikimedia/composer-merge-plugin/src',
|
||||
),
|
||||
'Twig\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/twig/twig/src',
|
||||
|
|
@ -150,22 +141,10 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
array (
|
||||
0 => __DIR__ . '/../..' . '/modules/system',
|
||||
),
|
||||
'Symfony\\Polyfill\\Util\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-util',
|
||||
),
|
||||
'Symfony\\Polyfill\\Php72\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php72',
|
||||
),
|
||||
'Symfony\\Polyfill\\Php70\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php70',
|
||||
),
|
||||
'Symfony\\Polyfill\\Php56\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-php56',
|
||||
),
|
||||
'Symfony\\Polyfill\\Mbstring\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
|
||||
|
|
@ -186,6 +165,14 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/polyfill-ctype',
|
||||
),
|
||||
'Symfony\\Contracts\\Translation\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/translation-contracts',
|
||||
),
|
||||
'Symfony\\Contracts\\EventDispatcher\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/event-dispatcher-contracts',
|
||||
),
|
||||
'Symfony\\Component\\Yaml\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/yaml',
|
||||
|
|
@ -262,14 +249,14 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
array (
|
||||
0 => __DIR__ . '/..' . '/october/rain/src',
|
||||
),
|
||||
'October\\Demo\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/../..' . '/plugins/october/demo',
|
||||
),
|
||||
'Monolog\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/monolog/monolog/src/Monolog',
|
||||
),
|
||||
'League\\MimeTypeDetection\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/league/mime-type-detection/src',
|
||||
),
|
||||
'League\\Flysystem\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/league/flysystem/src',
|
||||
|
|
@ -310,6 +297,10 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
array (
|
||||
0 => __DIR__ . '/..' . '/vlucas/phpdotenv/src',
|
||||
),
|
||||
'Doctrine\\Inflector\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/inflector/lib/Doctrine/Inflector',
|
||||
),
|
||||
'Doctrine\\Common\\Lexer\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/lexer/lib/Doctrine/Common/Lexer',
|
||||
|
|
@ -318,6 +309,10 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/inflector/lib/Doctrine/Common/Inflector',
|
||||
),
|
||||
'Doctrine\\Common\\Collections\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/collections/lib/Doctrine/Common/Collections',
|
||||
),
|
||||
'Doctrine\\Common\\Cache\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/cache/lib/Doctrine/Common/Cache',
|
||||
|
|
@ -391,10 +386,6 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/dbal/lib',
|
||||
),
|
||||
'Doctrine\\Common\\Collections\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/doctrine/collections/lib',
|
||||
),
|
||||
),
|
||||
'A' =>
|
||||
array (
|
||||
|
|
@ -406,16 +397,9 @@ class ComposerStaticInitce290a037d2cbd6fc6b8d537449d0ac2
|
|||
);
|
||||
|
||||
public static $classMap = array (
|
||||
'ArithmeticError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php',
|
||||
'AssertionError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php',
|
||||
'DivisionByZeroError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/DivisionByZeroError.php',
|
||||
'Error' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/Error.php',
|
||||
'JSMin' => __DIR__ . '/..' . '/linkorb/jsmin-php/src/jsmin-1.1.1.php',
|
||||
'JSMinException' => __DIR__ . '/..' . '/linkorb/jsmin-php/src/jsmin-1.1.1.php',
|
||||
'Normalizer' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/Resources/stubs/Normalizer.php',
|
||||
'ParseError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/ParseError.php',
|
||||
'SessionUpdateTimestampHandlerInterface' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/SessionUpdateTimestampHandlerInterface.php',
|
||||
'TypeError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/TypeError.php',
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -57,12 +57,14 @@
|
|||
"PPI",
|
||||
"Puppet",
|
||||
"Porto",
|
||||
"ProcessWire",
|
||||
"RadPHP",
|
||||
"ReIndex",
|
||||
"Roundcube",
|
||||
"shopware",
|
||||
"SilverStripe",
|
||||
"SMF",
|
||||
"Starbug",
|
||||
"SyDES",
|
||||
"Sylius",
|
||||
"symfony",
|
||||
|
|
@ -86,10 +88,13 @@
|
|||
"autoload": {
|
||||
"psr-4": { "Composer\\Installers\\": "src/Composer/Installers" }
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": { "Composer\\Installers\\Test\\": "tests/Composer/Installers/Test" }
|
||||
},
|
||||
"extra": {
|
||||
"class": "Composer\\Installers\\Plugin",
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0-dev"
|
||||
"dev-main": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"replace": {
|
||||
|
|
@ -100,13 +105,15 @@
|
|||
"composer-plugin-api": "^1.0 || ^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"composer/composer": "1.6.* || 2.0.*@dev",
|
||||
"composer/semver": "1.0.* || 2.0.*@dev",
|
||||
"phpunit/phpunit": "^4.8.36",
|
||||
"sebastian/comparator": "^1.2.4",
|
||||
"symfony/process": "^2.3"
|
||||
"composer/composer": "1.6.* || ^2.0",
|
||||
"composer/semver": "^1 || ^3",
|
||||
"symfony/phpunit-bridge": "^4.2 || ^5",
|
||||
"phpstan/phpstan": "^0.12.55",
|
||||
"symfony/process": "^2.3",
|
||||
"phpstan/phpstan-phpunit": "^0.12.16"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "phpunit"
|
||||
"test": "SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT=1 vendor/bin/simple-phpunit",
|
||||
"phpstan": "vendor/bin/phpstan analyse"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@ abstract class BaseInstaller
|
|||
/**
|
||||
* For an installer to override to modify the vars per installer.
|
||||
*
|
||||
* @param array $vars
|
||||
* @return array
|
||||
* @param array<string, string> $vars This will normally receive array{name: string, vendor: string, type: string}
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function inflectPackageVars($vars)
|
||||
{
|
||||
|
|
@ -85,7 +85,7 @@ abstract class BaseInstaller
|
|||
/**
|
||||
* Gets the installer's locations
|
||||
*
|
||||
* @return array
|
||||
* @return array<string, string> map of package types => install path
|
||||
*/
|
||||
public function getLocations()
|
||||
{
|
||||
|
|
@ -95,8 +95,8 @@ abstract class BaseInstaller
|
|||
/**
|
||||
* Replace vars in a path
|
||||
*
|
||||
* @param string $path
|
||||
* @param array $vars
|
||||
* @param string $path
|
||||
* @param array<string, string> $vars
|
||||
* @return string
|
||||
*/
|
||||
protected function templatePath($path, array $vars = array())
|
||||
|
|
@ -121,7 +121,7 @@ abstract class BaseInstaller
|
|||
* @param string $name
|
||||
* @param string $type
|
||||
* @param string $vendor = NULL
|
||||
* @return string
|
||||
* @return string|false
|
||||
*/
|
||||
protected function mapCustomInstallPaths(array $paths, $name, $type, $vendor = NULL)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
namespace Composer\Installers;
|
||||
|
||||
use Composer\DependencyResolver\Pool;
|
||||
use Composer\Semver\Constraint\Constraint;
|
||||
|
||||
class CakePHPInstaller extends BaseInstaller
|
||||
{
|
||||
|
|
@ -49,14 +50,6 @@ class CakePHPInstaller extends BaseInstaller
|
|||
*/
|
||||
protected function matchesCakeVersion($matcher, $version)
|
||||
{
|
||||
if (class_exists('Composer\Semver\Constraint\MultiConstraint')) {
|
||||
$multiClass = 'Composer\Semver\Constraint\MultiConstraint';
|
||||
$constraintClass = 'Composer\Semver\Constraint\Constraint';
|
||||
} else {
|
||||
$multiClass = 'Composer\Package\LinkConstraint\MultiConstraint';
|
||||
$constraintClass = 'Composer\Package\LinkConstraint\VersionConstraint';
|
||||
}
|
||||
|
||||
$repositoryManager = $this->composer->getRepositoryManager();
|
||||
if (! $repositoryManager) {
|
||||
return false;
|
||||
|
|
@ -67,6 +60,6 @@ class CakePHPInstaller extends BaseInstaller
|
|||
return false;
|
||||
}
|
||||
|
||||
return $repos->findPackage('cakephp/cakephp', new $constraintClass($matcher, $version)) !== null;
|
||||
return $repos->findPackage('cakephp/cakephp', new Constraint($matcher, $version)) !== null;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,9 +12,7 @@ class CockpitInstaller extends BaseInstaller
|
|||
*
|
||||
* Strip `module-` prefix from package name.
|
||||
*
|
||||
* @param array @vars
|
||||
*
|
||||
* @return array
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function inflectPackageVars($vars)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use Composer\IO\IOInterface;
|
|||
use Composer\Package\PackageInterface;
|
||||
use Composer\Repository\InstalledRepositoryInterface;
|
||||
use Composer\Util\Filesystem;
|
||||
use React\Promise\PromiseInterface;
|
||||
|
||||
class Installer extends LibraryInstaller
|
||||
{
|
||||
|
|
@ -87,6 +88,7 @@ class Installer extends LibraryInstaller
|
|||
'radphp' => 'RadPHPInstaller',
|
||||
'phifty' => 'PhiftyInstaller',
|
||||
'porto' => 'PortoInstaller',
|
||||
'processwire' => 'ProcessWireInstaller',
|
||||
'redaxo' => 'RedaxoInstaller',
|
||||
'redaxo5' => 'Redaxo5Installer',
|
||||
'reindex' => 'ReIndexInstaller',
|
||||
|
|
@ -95,6 +97,7 @@ class Installer extends LibraryInstaller
|
|||
'sitedirect' => 'SiteDirectInstaller',
|
||||
'silverstripe' => 'SilverStripeInstaller',
|
||||
'smf' => 'SMFInstaller',
|
||||
'starbug' => 'StarbugInstaller',
|
||||
'sydes' => 'SyDESInstaller',
|
||||
'sylius' => 'SyliusInstaller',
|
||||
'symfony1' => 'Symfony1Installer',
|
||||
|
|
@ -160,9 +163,23 @@ class Installer extends LibraryInstaller
|
|||
|
||||
public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package)
|
||||
{
|
||||
parent::uninstall($repo, $package);
|
||||
$installPath = $this->getPackageBasePath($package);
|
||||
$this->io->write(sprintf('Deleting %s - %s', $installPath, !file_exists($installPath) ? '<comment>deleted</comment>' : '<error>not deleted</error>'));
|
||||
$io = $this->io;
|
||||
$outputStatus = function () use ($io, $installPath) {
|
||||
$io->write(sprintf('Deleting %s - %s', $installPath, !file_exists($installPath) ? '<comment>deleted</comment>' : '<error>not deleted</error>'));
|
||||
};
|
||||
|
||||
$promise = parent::uninstall($repo, $package);
|
||||
|
||||
// Composer v2 might return a promise here
|
||||
if ($promise instanceof PromiseInterface) {
|
||||
return $promise->then($outputStatus);
|
||||
}
|
||||
|
||||
// If not, execute the code right away as parent::uninstall executed synchronously (composer v1, or v2 without async)
|
||||
$outputStatus();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -184,23 +201,20 @@ class Installer extends LibraryInstaller
|
|||
/**
|
||||
* Finds a supported framework type if it exists and returns it
|
||||
*
|
||||
* @param string $type
|
||||
* @return string
|
||||
* @param string $type
|
||||
* @return string|false
|
||||
*/
|
||||
protected function findFrameworkType($type)
|
||||
{
|
||||
$frameworkType = false;
|
||||
|
||||
krsort($this->supportedTypes);
|
||||
|
||||
foreach ($this->supportedTypes as $key => $val) {
|
||||
if ($key === substr($type, 0, strlen($key))) {
|
||||
$frameworkType = substr($type, 0, strlen($key));
|
||||
break;
|
||||
return substr($type, 0, strlen($key));
|
||||
}
|
||||
}
|
||||
|
||||
return $frameworkType;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ class MoodleInstaller extends BaseInstaller
|
|||
'cachestore' => 'cache/stores/{$name}/',
|
||||
'cachelock' => 'cache/locks/{$name}/',
|
||||
'calendartype' => 'calendar/type/{$name}/',
|
||||
'fileconverter' => 'files/converter/{$name}/',
|
||||
'format' => 'course/format/{$name}/',
|
||||
'coursereport' => 'course/report/{$name}/',
|
||||
'customcertelement' => 'mod/customcert/element/{$name}/',
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class OxidInstaller extends BaseInstaller
|
|||
*
|
||||
* @param PackageInterface $package
|
||||
* @param string $frameworkType
|
||||
* @return void
|
||||
* @return string
|
||||
*/
|
||||
public function getInstallPath(PackageInterface $package, $frameworkType = '')
|
||||
{
|
||||
|
|
|
|||
|
|
@ -13,9 +13,7 @@ class SyDESInstaller extends BaseInstaller
|
|||
*
|
||||
* Strip `sydes-` prefix and a trailing '-theme' or '-module' from package name if present.
|
||||
*
|
||||
* @param array @vars
|
||||
*
|
||||
* @return array
|
||||
* {@inerhitDoc}
|
||||
*/
|
||||
public function inflectPackageVars($vars)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,7 +6,25 @@ namespace Composer\Installers;
|
|||
*/
|
||||
class TaoInstaller extends BaseInstaller
|
||||
{
|
||||
const EXTRA_TAO_EXTENSION_NAME = 'tao-extension-name';
|
||||
|
||||
protected $locations = array(
|
||||
'extension' => '{$name}'
|
||||
);
|
||||
|
||||
public function inflectPackageVars($vars)
|
||||
{
|
||||
$extra = $this->package->getExtra();
|
||||
|
||||
if (array_key_exists(self::EXTRA_TAO_EXTENSION_NAME, $extra)) {
|
||||
$vars['name'] = $extra[self::EXTRA_TAO_EXTENSION_NAME];
|
||||
return $vars;
|
||||
}
|
||||
|
||||
$vars['name'] = str_replace('extension-', '', $vars['name']);
|
||||
$vars['name'] = str_replace('-', ' ', $vars['name']);
|
||||
$vars['name'] = lcfirst(str_replace(' ', '', ucwords($vars['name'])));
|
||||
|
||||
return $vars;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,52 @@
|
|||
## Changelog
|
||||
|
||||
### 1.6.1
|
||||
|
||||
This release fixes an issue in which annotations such as `@foo-bar`
|
||||
and `@foo-` were incorrectly recognised as valid, and both erroneously
|
||||
parsed as `@foo`.
|
||||
|
||||
Any annotation with `@name-*` format will now silently be ignored,
|
||||
allowing vendor-specific annotations to be prefixed with the tool
|
||||
name.
|
||||
|
||||
Total issues resolved: **3**
|
||||
|
||||
- [165: Update the composer branch alias](https://github.com/doctrine/annotations/pull/165) thanks to @mikeSimonson
|
||||
- [209: Change Annotation::value typehint to mixed](https://github.com/doctrine/annotations/pull/209) thanks to @malarzm
|
||||
- [257: Skip parsing annotations containing dashes, such as `@Foo-bar`, or `@Foo-`](https://github.com/doctrine/annotations/pull/257) thanks to @Ocramius
|
||||
|
||||
### 1.6.0
|
||||
|
||||
This release brings a new endpoint that make sure that you can't shoot yourself in the foot by calling ```registerLoader``` multiple times and a few tests improvements.
|
||||
|
||||
Total issues resolved: **7**
|
||||
|
||||
- [145: Memory leak in AnnotationRegistry::registerLoader() when called multiple times](https://github.com/doctrine/annotations/issues/145) thanks to @TriAnMan
|
||||
- [146: Import error on @experimental Annotation](https://github.com/doctrine/annotations/issues/146) thanks to @aturki
|
||||
- [147: Ignoring @experimental annotation used by Symfony 3.3 CacheAdapter](https://github.com/doctrine/annotations/pull/147) thanks to @aturki
|
||||
- [151: Remove duplicate code in `DCOM58Test`](https://github.com/doctrine/annotations/pull/151) thanks to @tuanphpvn
|
||||
- [161: Prevent loading class_exists multiple times](https://github.com/doctrine/annotations/pull/161) thanks to @jrjohnson
|
||||
- [162: Add registerUniqueLoader to AnnotationRegistry](https://github.com/doctrine/annotations/pull/162) thanks to @jrjohnson
|
||||
- [163: Use assertDirectoryExists and assertDirectoryNotExists](https://github.com/doctrine/annotations/pull/163) thanks to @carusogabriel
|
||||
|
||||
Thanks to everyone involved in this release.
|
||||
|
||||
### 1.5.0
|
||||
|
||||
This release increments the minimum supported PHP version to 7.1.0.
|
||||
|
||||
Also, HHVM official support has been dropped.
|
||||
|
||||
Some noticeable performance improvements to annotation autoloading
|
||||
have been applied, making failed annotation autoloading less heavy
|
||||
on the filesystem access.
|
||||
|
||||
- [133: Add @throws annotation in AnnotationReader#__construct()](https://github.com/doctrine/annotations/issues/133) thanks to @SenseException
|
||||
- [134: Require PHP 7.1, drop HHVM support](https://github.com/doctrine/annotations/issues/134) thanks to @lcobucci
|
||||
- [135: Prevent the same loader from being registered twice](https://github.com/doctrine/annotations/issues/135) thanks to @jrjohnson
|
||||
- [137: #135 optimise multiple class load attempts in AnnotationRegistry](https://github.com/doctrine/annotations/issues/137) thanks to @Ocramius
|
||||
|
||||
|
||||
### 1.4.0
|
||||
|
||||
|
|
|
|||
|
|
@ -1,16 +1,21 @@
|
|||
# Doctrine Annotations
|
||||
|
||||
[](https://travis-ci.org/doctrine/annotations)
|
||||
[](https://github.com/doctrine/persistence/actions)
|
||||
[](https://www.versioneye.com/package/php--doctrine--annotations)
|
||||
[](https://www.versioneye.com/php/doctrine:annotations/references)
|
||||
[](https://packagist.org/packages/doctrine/annotations)
|
||||
[](https://packagist.org/packages/doctrine/annotations)
|
||||
[](https://packagist.org/packages/doctrine/annotations)
|
||||
|
||||
Docblock Annotations Parser library (extracted from [Doctrine Common](https://github.com/doctrine/common)).
|
||||
|
||||
## Documentation
|
||||
|
||||
See the [doctrine-project website](http://docs.doctrine-project.org/projects/doctrine-common/en/latest/reference/annotations.html).
|
||||
See the [doctrine-project website](https://www.doctrine-project.org/projects/doctrine-annotations/en/latest/index.html).
|
||||
|
||||
## Contributing
|
||||
|
||||
When making a pull request, make sure your changes follow the
|
||||
[Coding Standard Guidelines](https://www.doctrine-project.org/projects/doctrine-coding-standard/en/current/reference/index.html#introduction).
|
||||
|
||||
## Changelog
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"type": "library",
|
||||
"description": "Docblock Annotations Parser",
|
||||
"keywords": ["annotations", "docblock", "parser"],
|
||||
"homepage": "http://www.doctrine-project.org",
|
||||
"homepage": "https://www.doctrine-project.org/projects/annotations.html",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{"name": "Guilherme Blanco", "email": "guilhermeblanco@gmail.com"},
|
||||
|
|
@ -13,22 +13,30 @@
|
|||
{"name": "Johannes Schmitt", "email": "schmittjoh@gmail.com"}
|
||||
],
|
||||
"require": {
|
||||
"php": "^5.6 || ^7.0",
|
||||
"php": "^7.1 || ^8.0",
|
||||
"ext-tokenizer": "*",
|
||||
"doctrine/lexer": "1.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/cache": "1.*",
|
||||
"phpunit/phpunit": "^5.7"
|
||||
"doctrine/coding-standard": "^6.0 || ^8.1",
|
||||
"phpstan/phpstan": "^0.12.20",
|
||||
"phpunit/phpunit": "^7.5 || ^9.1.5"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": { "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" }
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": { "Doctrine\\Tests\\Common\\Annotations\\": "tests/Doctrine/Tests/Common/Annotations" }
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.4.x-dev"
|
||||
}
|
||||
"psr-4": {
|
||||
"Doctrine\\Performance\\Common\\Annotations\\": "tests/Doctrine/Performance/Common/Annotations",
|
||||
"Doctrine\\Tests\\Common\\Annotations\\": "tests/Doctrine/Tests/Common/Annotations"
|
||||
},
|
||||
"files": [
|
||||
"tests/Doctrine/Tests/Common/Annotations/Fixtures/functions.php",
|
||||
"tests/Doctrine/Tests/Common/Annotations/Fixtures/SingleClassLOC1000.php"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,271 @@
|
|||
Handling Annotations
|
||||
====================
|
||||
|
||||
There are several different approaches to handling annotations in PHP.
|
||||
Doctrine Annotations maps docblock annotations to PHP classes. Because
|
||||
not all docblock annotations are used for metadata purposes a filter is
|
||||
applied to ignore or skip classes that are not Doctrine annotations.
|
||||
|
||||
Take a look at the following code snippet:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
namespace MyProject\Entities;
|
||||
|
||||
use Doctrine\ORM\Mapping AS ORM;
|
||||
use Symfony\Component\Validator\Constraints AS Assert;
|
||||
|
||||
/**
|
||||
* @author Benjamin Eberlei
|
||||
* @ORM\Entity
|
||||
* @MyProject\Annotations\Foobarable
|
||||
*/
|
||||
class User
|
||||
{
|
||||
/**
|
||||
* @ORM\Id @ORM\Column @ORM\GeneratedValue
|
||||
* @dummy
|
||||
* @var int
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @ORM\Column(type="string")
|
||||
* @Assert\NotEmpty
|
||||
* @Assert\Email
|
||||
* @var string
|
||||
*/
|
||||
private $email;
|
||||
}
|
||||
|
||||
In this snippet you can see a variety of different docblock annotations:
|
||||
|
||||
- Documentation annotations such as ``@var`` and ``@author``. These
|
||||
annotations are ignored and never considered for throwing an
|
||||
exception due to wrongly used annotations.
|
||||
- Annotations imported through use statements. The statement ``use
|
||||
Doctrine\ORM\Mapping AS ORM`` makes all classes under that namespace
|
||||
available as ``@ORM\ClassName``. Same goes for the import of
|
||||
``@Assert``.
|
||||
- The ``@dummy`` annotation. It is not a documentation annotation and
|
||||
not ignored. For Doctrine Annotations it is not entirely clear how
|
||||
to handle this annotation. Depending on the configuration an exception
|
||||
(unknown annotation) will be thrown when parsing this annotation.
|
||||
- The fully qualified annotation ``@MyProject\Annotations\Foobarable``.
|
||||
This is transformed directly into the given class name.
|
||||
|
||||
How are these annotations loaded? From looking at the code you could
|
||||
guess that the ORM Mapping, Assert Validation and the fully qualified
|
||||
annotation can just be loaded using
|
||||
the defined PHP autoloaders. This is not the case however: For error
|
||||
handling reasons every check for class existence inside the
|
||||
``AnnotationReader`` sets the second parameter $autoload
|
||||
of ``class_exists($name, $autoload)`` to false. To work flawlessly the
|
||||
``AnnotationReader`` requires silent autoloaders which many autoloaders are
|
||||
not. Silent autoloading is NOT part of the `PSR-0 specification
|
||||
<https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md>`_
|
||||
for autoloading.
|
||||
|
||||
This is why Doctrine Annotations uses its own autoloading mechanism
|
||||
through a global registry. If you are wondering about the annotation
|
||||
registry being global, there is no other way to solve the architectural
|
||||
problems of autoloading annotation classes in a straightforward fashion.
|
||||
Additionally if you think about PHP autoloading then you recognize it is
|
||||
a global as well.
|
||||
|
||||
To anticipate the configuration section, making the above PHP class work
|
||||
with Doctrine Annotations requires this setup:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use Doctrine\Common\Annotations\AnnotationRegistry;
|
||||
|
||||
AnnotationRegistry::registerFile("/path/to/doctrine/lib/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php");
|
||||
AnnotationRegistry::registerAutoloadNamespace("Symfony\Component\Validator\Constraint", "/path/to/symfony/src");
|
||||
AnnotationRegistry::registerAutoloadNamespace("MyProject\Annotations", "/path/to/myproject/src");
|
||||
|
||||
$reader = new AnnotationReader();
|
||||
AnnotationReader::addGlobalIgnoredName('dummy');
|
||||
|
||||
The second block with the annotation registry calls registers all the
|
||||
three different annotation namespaces that are used.
|
||||
Doctrine Annotations saves all its annotations in a single file, that is
|
||||
why ``AnnotationRegistry#registerFile`` is used in contrast to
|
||||
``AnnotationRegistry#registerAutoloadNamespace`` which creates a PSR-0
|
||||
compatible loading mechanism for class to file names.
|
||||
|
||||
In the third block, we create the actual ``AnnotationReader`` instance.
|
||||
Note that we also add ``dummy`` to the global list of ignored
|
||||
annotations for which we do not throw exceptions. Setting this is
|
||||
necessary in our example case, otherwise ``@dummy`` would trigger an
|
||||
exception to be thrown during the parsing of the docblock of
|
||||
``MyProject\Entities\User#id``.
|
||||
|
||||
Setup and Configuration
|
||||
-----------------------
|
||||
|
||||
To use the annotations library is simple, you just need to create a new
|
||||
``AnnotationReader`` instance:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
|
||||
|
||||
This creates a simple annotation reader with no caching other than in
|
||||
memory (in php arrays). Since parsing docblocks can be expensive you
|
||||
should cache this process by using a caching reader.
|
||||
|
||||
You can use a file caching reader, but please note it is deprecated to
|
||||
do so:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use Doctrine\Common\Annotations\FileCacheReader;
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
|
||||
$reader = new FileCacheReader(
|
||||
new AnnotationReader(),
|
||||
"/path/to/cache",
|
||||
$debug = true
|
||||
);
|
||||
|
||||
If you set the ``debug`` flag to ``true`` the cache reader will check
|
||||
for changes in the original files, which is very important during
|
||||
development. If you don't set it to ``true`` you have to delete the
|
||||
directory to clear the cache. This gives faster performance, however
|
||||
should only be used in production, because of its inconvenience during
|
||||
development.
|
||||
|
||||
You can also use one of the ``Doctrine\Common\Cache\Cache`` cache
|
||||
implementations to cache the annotations:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use Doctrine\Common\Annotations\CachedReader;
|
||||
use Doctrine\Common\Cache\ApcCache;
|
||||
|
||||
$reader = new CachedReader(
|
||||
new AnnotationReader(),
|
||||
new ApcCache(),
|
||||
$debug = true
|
||||
);
|
||||
|
||||
The ``debug`` flag is used here as well to invalidate the cache files
|
||||
when the PHP class with annotations changed and should be used during
|
||||
development.
|
||||
|
||||
.. warning ::
|
||||
|
||||
The ``AnnotationReader`` works and caches under the
|
||||
assumption that all annotations of a doc-block are processed at
|
||||
once. That means that annotation classes that do not exist and
|
||||
aren't loaded and cannot be autoloaded (using the
|
||||
AnnotationRegistry) would never be visible and not accessible if a
|
||||
cache is used unless the cache is cleared and the annotations
|
||||
requested again, this time with all annotations defined.
|
||||
|
||||
By default the annotation reader returns a list of annotations with
|
||||
numeric indexes. If you want your annotations to be indexed by their
|
||||
class name you can wrap the reader in an ``IndexedReader``:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use Doctrine\Common\Annotations\IndexedReader;
|
||||
|
||||
$reader = new IndexedReader(new AnnotationReader());
|
||||
|
||||
.. warning::
|
||||
|
||||
You should never wrap the indexed reader inside a cached reader,
|
||||
only the other way around. This way you can re-use the cache with
|
||||
indexed or numeric keys, otherwise your code may experience failures
|
||||
due to caching in a numerical or indexed format.
|
||||
|
||||
Registering Annotations
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
As explained in the introduction, Doctrine Annotations uses its own
|
||||
autoloading mechanism to determine if a given annotation has a
|
||||
corresponding PHP class that can be autoloaded. For annotation
|
||||
autoloading you have to configure the
|
||||
``Doctrine\Common\Annotations\AnnotationRegistry``. There are three
|
||||
different mechanisms to configure annotation autoloading:
|
||||
|
||||
- Calling ``AnnotationRegistry#registerFile($file)`` to register a file
|
||||
that contains one or more annotation classes.
|
||||
- Calling ``AnnotationRegistry#registerNamespace($namespace, $dirs =
|
||||
null)`` to register that the given namespace contains annotations and
|
||||
that their base directory is located at the given $dirs or in the
|
||||
include path if ``NULL`` is passed. The given directories should *NOT*
|
||||
be the directory where classes of the namespace are in, but the base
|
||||
directory of the root namespace. The AnnotationRegistry uses a
|
||||
namespace to directory separator approach to resolve the correct path.
|
||||
- Calling ``AnnotationRegistry#registerLoader($callable)`` to register
|
||||
an autoloader callback. The callback accepts the class as first and
|
||||
only parameter and has to return ``true`` if the corresponding file
|
||||
was found and included.
|
||||
|
||||
.. note::
|
||||
|
||||
Loaders have to fail silently, if a class is not found even if it
|
||||
matches for example the namespace prefix of that loader. Never is a
|
||||
loader to throw a warning or exception if the loading failed
|
||||
otherwise parsing doc block annotations will become a huge pain.
|
||||
|
||||
A sample loader callback could look like:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationRegistry;
|
||||
use Symfony\Component\ClassLoader\UniversalClassLoader;
|
||||
|
||||
AnnotationRegistry::registerLoader(function($class) {
|
||||
$file = str_replace("\\", DIRECTORY_SEPARATOR, $class) . ".php";
|
||||
|
||||
if (file_exists("/my/base/path/" . $file)) {
|
||||
// file_exists() makes sure that the loader fails silently
|
||||
require "/my/base/path/" . $file;
|
||||
}
|
||||
});
|
||||
|
||||
$loader = new UniversalClassLoader();
|
||||
AnnotationRegistry::registerLoader(array($loader, "loadClass"));
|
||||
|
||||
|
||||
Ignoring missing exceptions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
By default an exception is thrown from the ``AnnotationReader`` if an
|
||||
annotation was found that:
|
||||
|
||||
- is not part of the list of ignored "documentation annotations";
|
||||
- was not imported through a use statement;
|
||||
- is not a fully qualified class that exists.
|
||||
|
||||
You can disable this behavior for specific names if your docblocks do
|
||||
not follow strict requirements:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
|
||||
AnnotationReader::addGlobalIgnoredName('foo');
|
||||
|
||||
PHP Imports
|
||||
~~~~~~~~~~~
|
||||
|
||||
By default the annotation reader parses the use-statement of a php file
|
||||
to gain access to the import rules and register them for the annotation
|
||||
processing. Only if you are using PHP Imports can you validate the
|
||||
correct usage of annotations and throw exceptions if you misspelled an
|
||||
annotation. This mechanism is enabled by default.
|
||||
|
||||
To ease the upgrade path, we still allow you to disable this mechanism.
|
||||
Note however that we will remove this in future versions:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$reader = new \Doctrine\Common\Annotations\AnnotationReader();
|
||||
$reader->setEnabledPhpImports(false);
|
||||
|
|
@ -0,0 +1,443 @@
|
|||
Custom Annotation Classes
|
||||
=========================
|
||||
|
||||
If you want to define your own annotations, you just have to group them
|
||||
in a namespace and register this namespace in the ``AnnotationRegistry``.
|
||||
Annotation classes have to contain a class-level docblock with the text
|
||||
``@Annotation``:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
namespace MyCompany\Annotations;
|
||||
|
||||
/** @Annotation */
|
||||
class Bar
|
||||
{
|
||||
// some code
|
||||
}
|
||||
|
||||
Inject annotation values
|
||||
------------------------
|
||||
|
||||
The annotation parser checks if the annotation constructor has arguments,
|
||||
if so then it will pass the value array, otherwise it will try to inject
|
||||
values into public properties directly:
|
||||
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
namespace MyCompany\Annotations;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*
|
||||
* Some Annotation using a constructor
|
||||
*/
|
||||
class Bar
|
||||
{
|
||||
private $foo;
|
||||
|
||||
public function __construct(array $values)
|
||||
{
|
||||
$this->foo = $values['foo'];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*
|
||||
* Some Annotation without a constructor
|
||||
*/
|
||||
class Foo
|
||||
{
|
||||
public $bar;
|
||||
}
|
||||
|
||||
Optional: Constructors with Named Parameters
|
||||
--------------------------------------------
|
||||
|
||||
Starting with Annotations v1.11 a new annotation instantiation strategy
|
||||
is available that aims at compatibility of Annotation classes with the PHP 8
|
||||
attribute feature. You need to declare a constructor with regular parameter
|
||||
names that match the named arguments in the annotation syntax.
|
||||
|
||||
To enable this feature, you can tag your annotation class with
|
||||
``@NamedArgumentConstructor`` (available from v1.12) or implement the
|
||||
``Doctrine\Common\Annotations\NamedArgumentConstructorAnnotation`` interface
|
||||
(available from v1.11 and deprecated as of v1.12).
|
||||
When using the ``@NamedArgumentConstructor`` tag, the first argument of the
|
||||
constructor is considered as the default one.
|
||||
|
||||
|
||||
Usage with the ``@NamedArgumentContrustor`` tag
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
namespace MyCompany\Annotations;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor
|
||||
*/
|
||||
class Bar implements NamedArgumentConstructorAnnotation
|
||||
{
|
||||
private $foo;
|
||||
|
||||
public function __construct(string $foo)
|
||||
{
|
||||
$this->foo = $foo;
|
||||
}
|
||||
}
|
||||
|
||||
/** Usable with @Bar(foo="baz") */
|
||||
/** Usable with @Bar("baz") */
|
||||
|
||||
In combination with PHP 8's constructor property promotion feature
|
||||
you can simplify this to:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
namespace MyCompany\Annotations;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @NamedArgumentConstructor
|
||||
*/
|
||||
class Bar implements NamedArgumentConstructorAnnotation
|
||||
{
|
||||
public function __construct(private string $foo) {}
|
||||
}
|
||||
|
||||
|
||||
Usage with the
|
||||
``Doctrine\Common\Annotations\NamedArgumentConstructorAnnotation``
|
||||
interface (v1.11, deprecated as of v1.12):
|
||||
.. code-block:: php
|
||||
|
||||
namespace MyCompany\Annotations;
|
||||
|
||||
use Doctrine\Common\Annotations\NamedArgumentConstructorAnnotation;
|
||||
|
||||
/** @Annotation */
|
||||
class Bar implements NamedArgumentConstructorAnnotation
|
||||
{
|
||||
private $foo;
|
||||
|
||||
public function __construct(private string $foo) {}
|
||||
}
|
||||
|
||||
/** Usable with @Bar(foo="baz") */
|
||||
|
||||
Annotation Target
|
||||
-----------------
|
||||
|
||||
``@Target`` indicates the kinds of class elements to which an annotation
|
||||
type is applicable. Then you could define one or more targets:
|
||||
|
||||
- ``CLASS`` Allowed in class docblocks
|
||||
- ``PROPERTY`` Allowed in property docblocks
|
||||
- ``METHOD`` Allowed in the method docblocks
|
||||
- ``FUNCTION`` Allowed in function dockblocks
|
||||
- ``ALL`` Allowed in class, property, method and function docblocks
|
||||
- ``ANNOTATION`` Allowed inside other annotations
|
||||
|
||||
If the annotations is not allowed in the current context, an
|
||||
``AnnotationException`` is thrown.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
namespace MyCompany\Annotations;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target({"METHOD","PROPERTY"})
|
||||
*/
|
||||
class Bar
|
||||
{
|
||||
// some code
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
class Foo
|
||||
{
|
||||
// some code
|
||||
}
|
||||
|
||||
Attribute types
|
||||
---------------
|
||||
|
||||
The annotation parser checks the given parameters using the phpdoc
|
||||
annotation ``@var``, The data type could be validated using the ``@var``
|
||||
annotation on the annotation properties or using the ``@Attributes`` and
|
||||
``@Attribute`` annotations.
|
||||
|
||||
If the data type does not match you get an ``AnnotationException``
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
namespace MyCompany\Annotations;
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target({"METHOD","PROPERTY"})
|
||||
*/
|
||||
class Bar
|
||||
{
|
||||
/** @var mixed */
|
||||
public $mixed;
|
||||
|
||||
/** @var boolean */
|
||||
public $boolean;
|
||||
|
||||
/** @var bool */
|
||||
public $bool;
|
||||
|
||||
/** @var float */
|
||||
public $float;
|
||||
|
||||
/** @var string */
|
||||
public $string;
|
||||
|
||||
/** @var integer */
|
||||
public $integer;
|
||||
|
||||
/** @var array */
|
||||
public $array;
|
||||
|
||||
/** @var SomeAnnotationClass */
|
||||
public $annotation;
|
||||
|
||||
/** @var array<integer> */
|
||||
public $arrayOfIntegers;
|
||||
|
||||
/** @var array<SomeAnnotationClass> */
|
||||
public $arrayOfAnnotations;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target({"METHOD","PROPERTY"})
|
||||
* @Attributes({
|
||||
* @Attribute("stringProperty", type = "string"),
|
||||
* @Attribute("annotProperty", type = "SomeAnnotationClass"),
|
||||
* })
|
||||
*/
|
||||
class Foo
|
||||
{
|
||||
public function __construct(array $values)
|
||||
{
|
||||
$this->stringProperty = $values['stringProperty'];
|
||||
$this->annotProperty = $values['annotProperty'];
|
||||
}
|
||||
|
||||
// some code
|
||||
}
|
||||
|
||||
Annotation Required
|
||||
-------------------
|
||||
|
||||
``@Required`` indicates that the field must be specified when the
|
||||
annotation is used. If it is not used you get an ``AnnotationException``
|
||||
stating that this value can not be null.
|
||||
|
||||
Declaring a required field:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("ALL")
|
||||
*/
|
||||
class Foo
|
||||
{
|
||||
/** @Required */
|
||||
public $requiredField;
|
||||
}
|
||||
|
||||
Usage:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
/** @Foo(requiredField="value") */
|
||||
public $direction; // Valid
|
||||
|
||||
/** @Foo */
|
||||
public $direction; // Required field missing, throws an AnnotationException
|
||||
|
||||
|
||||
Enumerated values
|
||||
-----------------
|
||||
|
||||
- An annotation property marked with ``@Enum`` is a field that accepts a
|
||||
fixed set of scalar values.
|
||||
- You should use ``@Enum`` fields any time you need to represent fixed
|
||||
values.
|
||||
- The annotation parser checks the given value and throws an
|
||||
``AnnotationException`` if the value does not match.
|
||||
|
||||
|
||||
Declaring an enumerated property:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
* @Target("ALL")
|
||||
*/
|
||||
class Direction
|
||||
{
|
||||
/**
|
||||
* @Enum({"NORTH", "SOUTH", "EAST", "WEST"})
|
||||
*/
|
||||
public $value;
|
||||
}
|
||||
|
||||
Annotation usage:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
/** @Direction("NORTH") */
|
||||
public $direction; // Valid value
|
||||
|
||||
/** @Direction("NORTHEAST") */
|
||||
public $direction; // Invalid value, throws an AnnotationException
|
||||
|
||||
|
||||
Constants
|
||||
---------
|
||||
|
||||
The use of constants and class constants is available on the annotations
|
||||
parser.
|
||||
|
||||
The following usages are allowed:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
namespace MyCompany\Entity;
|
||||
|
||||
use MyCompany\Annotations\Foo;
|
||||
use MyCompany\Annotations\Bar;
|
||||
use MyCompany\Entity\SomeClass;
|
||||
|
||||
/**
|
||||
* @Foo(PHP_EOL)
|
||||
* @Bar(Bar::FOO)
|
||||
* @Foo({SomeClass::FOO, SomeClass::BAR})
|
||||
* @Bar({SomeClass::FOO_KEY = SomeClass::BAR_VALUE})
|
||||
*/
|
||||
class User
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Be careful with constants and the cache !
|
||||
|
||||
.. note::
|
||||
|
||||
The cached reader will not re-evaluate each time an annotation is
|
||||
loaded from cache. When a constant is changed the cache must be
|
||||
cleaned.
|
||||
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
Using the library API is simple. Using the annotations described in the
|
||||
previous section, you can now annotate other classes with your
|
||||
annotations:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
namespace MyCompany\Entity;
|
||||
|
||||
use MyCompany\Annotations\Foo;
|
||||
use MyCompany\Annotations\Bar;
|
||||
|
||||
/**
|
||||
* @Foo(bar="foo")
|
||||
* @Bar(foo="bar")
|
||||
*/
|
||||
class User
|
||||
{
|
||||
}
|
||||
|
||||
Now we can write a script to get the annotations above:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$reflClass = new ReflectionClass('MyCompany\Entity\User');
|
||||
$classAnnotations = $reader->getClassAnnotations($reflClass);
|
||||
|
||||
foreach ($classAnnotations AS $annot) {
|
||||
if ($annot instanceof \MyCompany\Annotations\Foo) {
|
||||
echo $annot->bar; // prints "foo";
|
||||
} else if ($annot instanceof \MyCompany\Annotations\Bar) {
|
||||
echo $annot->foo; // prints "bar";
|
||||
}
|
||||
}
|
||||
|
||||
You have a complete API for retrieving annotation class instances from a
|
||||
class, property or method docblock:
|
||||
|
||||
|
||||
Reader API
|
||||
~~~~~~~~~~
|
||||
|
||||
Access all annotations of a class
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
public function getClassAnnotations(\ReflectionClass $class);
|
||||
|
||||
Access one annotation of a class
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
public function getClassAnnotation(\ReflectionClass $class, $annotationName);
|
||||
|
||||
Access all annotations of a method
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
public function getMethodAnnotations(\ReflectionMethod $method);
|
||||
|
||||
Access one annotation of a method
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
public function getMethodAnnotation(\ReflectionMethod $method, $annotationName);
|
||||
|
||||
Access all annotations of a property
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
public function getPropertyAnnotations(\ReflectionProperty $property);
|
||||
|
||||
Access one annotation of a property
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName);
|
||||
|
||||
Access all annotations of a function
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
public function getFunctionAnnotations(\ReflectionFunction $property);
|
||||
|
||||
Access one annotation of a function
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
public function getFunctionAnnotation(\ReflectionFunction $property, $annotationName);
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
Introduction
|
||||
============
|
||||
|
||||
Doctrine Annotations allows to implement custom annotation
|
||||
functionality for PHP classes and functions.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
class Foo
|
||||
{
|
||||
/**
|
||||
* @MyAnnotation(myProperty="value")
|
||||
*/
|
||||
private $bar;
|
||||
}
|
||||
|
||||
Annotations aren't implemented in PHP itself which is why this component
|
||||
offers a way to use the PHP doc-blocks as a place for the well known
|
||||
annotation syntax using the ``@`` char.
|
||||
|
||||
Annotations in Doctrine are used for the ORM configuration to build the
|
||||
class mapping, but it can be used in other projects for other purposes
|
||||
too.
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
You can install the Annotation component with composer:
|
||||
|
||||
.. code-block::
|
||||
|
||||
$ composer require doctrine/annotations
|
||||
|
||||
Create an annotation class
|
||||
==========================
|
||||
|
||||
An annotation class is a representation of the later used annotation
|
||||
configuration in classes. The annotation class of the previous example
|
||||
looks like this:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
/**
|
||||
* @Annotation
|
||||
*/
|
||||
final class MyAnnotation
|
||||
{
|
||||
public $myProperty;
|
||||
}
|
||||
|
||||
The annotation class is declared as an annotation by ``@Annotation``.
|
||||
|
||||
:ref:`Read more about custom annotations. <custom>`
|
||||
|
||||
Reading annotations
|
||||
===================
|
||||
|
||||
The access to the annotations happens by reflection of the class or function
|
||||
containing them. There are multiple reader-classes implementing the
|
||||
``Doctrine\Common\Annotations\Reader`` interface, that can access the
|
||||
annotations of a class. A common one is
|
||||
``Doctrine\Common\Annotations\AnnotationReader``:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
use Doctrine\Common\Annotations\AnnotationReader;
|
||||
use Doctrine\Common\Annotations\AnnotationRegistry;
|
||||
|
||||
// Deprecated and will be removed in 2.0 but currently needed
|
||||
AnnotationRegistry::registerLoader('class_exists');
|
||||
|
||||
$reflectionClass = new ReflectionClass(Foo::class);
|
||||
$property = $reflectionClass->getProperty('bar');
|
||||
|
||||
$reader = new AnnotationReader();
|
||||
$myAnnotation = $reader->getPropertyAnnotation(
|
||||
$property,
|
||||
MyAnnotation::class
|
||||
);
|
||||
|
||||
echo $myAnnotation->myProperty; // result: "value"
|
||||
|
||||
Note that ``AnnotationRegistry::registerLoader('class_exists')`` only works
|
||||
if you already have an autoloader configured (i.e. composer autoloader).
|
||||
Otherwise, :ref:`please take a look to the other annotation autoload mechanisms <annotations>`.
|
||||
|
||||
A reader has multiple methods to access the annotations of a class or
|
||||
function.
|
||||
|
||||
:ref:`Read more about handling annotations. <annotations>`
|
||||
|
||||
IDE Support
|
||||
-----------
|
||||
|
||||
Some IDEs already provide support for annotations:
|
||||
|
||||
- Eclipse via the `Symfony2 Plugin <http://symfony.dubture.com/>`_
|
||||
- PhpStorm via the `PHP Annotations Plugin <https://plugins.jetbrains.com/plugin/7320-php-annotations>`_ or the `Symfony Plugin <https://plugins.jetbrains.com/plugin/7219-symfony-support>`_
|
||||
|
||||
.. _Read more about handling annotations.: annotations
|
||||
.. _Read more about custom annotations.: custom
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
.. toctree::
|
||||
:depth: 3
|
||||
|
||||
index
|
||||
annotations
|
||||
custom
|
||||
|
|
@ -1,47 +1,27 @@
|
|||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Annotations;
|
||||
|
||||
use BadMethodCallException;
|
||||
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Annotations class.
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class Annotation
|
||||
{
|
||||
/**
|
||||
* Value property. Common among all derived classes.
|
||||
*
|
||||
* @var string
|
||||
* @var mixed
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $data Key-value for properties to be defined in this class.
|
||||
* @param array<string, mixed> $data Key-value for properties to be defined in this class.
|
||||
*/
|
||||
public final function __construct(array $data)
|
||||
final public function __construct(array $data)
|
||||
{
|
||||
foreach ($data as $key => $value) {
|
||||
$this->$key = $value;
|
||||
|
|
@ -53,12 +33,12 @@ class Annotation
|
|||
*
|
||||
* @param string $name Unknown property name.
|
||||
*
|
||||
* @throws \BadMethodCallException
|
||||
* @throws BadMethodCallException
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
throw new \BadMethodCallException(
|
||||
sprintf("Unknown property '%s' on annotation '%s'.", $name, get_class($this))
|
||||
throw new BadMethodCallException(
|
||||
sprintf("Unknown property '%s' on annotation '%s'.", $name, static::class)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -68,12 +48,12 @@ class Annotation
|
|||
* @param string $name Unknown property name.
|
||||
* @param mixed $value Property value.
|
||||
*
|
||||
* @throws \BadMethodCallException
|
||||
* @throws BadMethodCallException
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
throw new \BadMethodCallException(
|
||||
sprintf("Unknown property '%s' on annotation '%s'.", $name, get_class($this))
|
||||
throw new BadMethodCallException(
|
||||
sprintf("Unknown property '%s' on annotation '%s'.", $name, static::class)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,47 +1,21 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
/**
|
||||
* Annotation that can be used to signal to the parser
|
||||
* to check the attribute type during the parsing process.
|
||||
*
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
final class Attribute
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
/** @var string */
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
/** @var string */
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
/** @var bool */
|
||||
public $required = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,37 +1,15 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
/**
|
||||
* Annotation that can be used to signal to the parser
|
||||
* to check the types of all declared attributes during the parsing process.
|
||||
*
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
final class Attributes
|
||||
{
|
||||
/**
|
||||
* @var array<Doctrine\Common\Annotations\Annotation\Attribute>
|
||||
*/
|
||||
/** @var array<Attribute> */
|
||||
public $value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,20 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
use function get_class;
|
||||
use function gettype;
|
||||
use function in_array;
|
||||
use function is_object;
|
||||
use function is_scalar;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Annotation that can be used to signal to the parser
|
||||
* to check the available values during the parsing process.
|
||||
*
|
||||
* @since 2.4
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*
|
||||
* @Annotation
|
||||
* @Attributes({
|
||||
* @Attribute("value", required = true, type = "array"),
|
||||
|
|
@ -35,34 +23,30 @@ namespace Doctrine\Common\Annotations\Annotation;
|
|||
*/
|
||||
final class Enum
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
/** @phpstan-var list<scalar> */
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* Literal target declaration.
|
||||
*
|
||||
* @var array
|
||||
* @var mixed[]
|
||||
*/
|
||||
public $literal;
|
||||
|
||||
/**
|
||||
* Annotation constructor.
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @param array $values
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @phpstan-param array{literal?: mixed[], value: list<scalar>} $values
|
||||
*/
|
||||
public function __construct(array $values)
|
||||
{
|
||||
if ( ! isset($values['literal'])) {
|
||||
$values['literal'] = array();
|
||||
if (! isset($values['literal'])) {
|
||||
$values['literal'] = [];
|
||||
}
|
||||
|
||||
foreach ($values['value'] as $var) {
|
||||
if( ! is_scalar($var)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
if (! is_scalar($var)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'@Enum supports only scalar values "%s" given.',
|
||||
is_object($var) ? get_class($var) : gettype($var)
|
||||
));
|
||||
|
|
@ -70,15 +54,16 @@ final class Enum
|
|||
}
|
||||
|
||||
foreach ($values['literal'] as $key => $var) {
|
||||
if( ! in_array($key, $values['value'])) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
if (! in_array($key, $values['value'])) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'Undefined enumerator value "%s" for literal "%s".',
|
||||
$key , $var
|
||||
$key,
|
||||
$var
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
$this->value = $values['value'];
|
||||
$this->literal = $values['literal'];
|
||||
$this->value = $values['value'];
|
||||
$this->literal = $values['literal'];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,52 +1,41 @@
|
|||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
use function is_array;
|
||||
use function is_string;
|
||||
use function json_encode;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Annotation that can be used to signal to the parser to ignore specific
|
||||
* annotations during the parsing process.
|
||||
*
|
||||
* @Annotation
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
final class IgnoreAnnotation
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
/** @phpstan-var list<string> */
|
||||
public $names;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @throws RuntimeException
|
||||
*
|
||||
* @param array $values
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
* @phpstan-param array{value: string|list<string>} $values
|
||||
*/
|
||||
public function __construct(array $values)
|
||||
{
|
||||
if (is_string($values['value'])) {
|
||||
$values['value'] = array($values['value']);
|
||||
$values['value'] = [$values['value']];
|
||||
}
|
||||
if (!is_array($values['value'])) {
|
||||
throw new \RuntimeException(sprintf('@IgnoreAnnotation expects either a string name, or an array of strings, but got %s.', json_encode($values['value'])));
|
||||
|
||||
if (! is_array($values['value'])) {
|
||||
throw new RuntimeException(sprintf(
|
||||
'@IgnoreAnnotation expects either a string name, or an array of strings, but got %s.',
|
||||
json_encode($values['value'])
|
||||
));
|
||||
}
|
||||
|
||||
$this->names = $values['value'];
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
/**
|
||||
* Annotation that indicates that the annotated class should be constructed with a named argument call.
|
||||
*
|
||||
* @Annotation
|
||||
* @Target("CLASS")
|
||||
*/
|
||||
final class NamedArgumentConstructor
|
||||
{
|
||||
}
|
||||
|
|
@ -1,31 +1,11 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
/**
|
||||
* Annotation that can be used to signal to the parser
|
||||
* to check if that attribute is required during the parsing process.
|
||||
*
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
final class Required
|
||||
|
|
|
|||
|
|
@ -1,89 +1,79 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Annotations\Annotation;
|
||||
|
||||
use InvalidArgumentException;
|
||||
|
||||
use function array_keys;
|
||||
use function get_class;
|
||||
use function gettype;
|
||||
use function implode;
|
||||
use function is_array;
|
||||
use function is_object;
|
||||
use function is_string;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Annotation that can be used to signal to the parser
|
||||
* to check the annotation target during the parsing process.
|
||||
*
|
||||
* @author Fabio B. Silva <fabio.bat.silva@gmail.com>
|
||||
*
|
||||
* @Annotation
|
||||
*/
|
||||
final class Target
|
||||
{
|
||||
const TARGET_CLASS = 1;
|
||||
const TARGET_METHOD = 2;
|
||||
const TARGET_PROPERTY = 4;
|
||||
const TARGET_ANNOTATION = 8;
|
||||
const TARGET_ALL = 15;
|
||||
public const TARGET_CLASS = 1;
|
||||
public const TARGET_METHOD = 2;
|
||||
public const TARGET_PROPERTY = 4;
|
||||
public const TARGET_ANNOTATION = 8;
|
||||
public const TARGET_FUNCTION = 16;
|
||||
public const TARGET_ALL = 31;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $map = array(
|
||||
/** @var array<string, int> */
|
||||
private static $map = [
|
||||
'ALL' => self::TARGET_ALL,
|
||||
'CLASS' => self::TARGET_CLASS,
|
||||
'METHOD' => self::TARGET_METHOD,
|
||||
'PROPERTY' => self::TARGET_PROPERTY,
|
||||
'FUNCTION' => self::TARGET_FUNCTION,
|
||||
'ANNOTATION' => self::TARGET_ANNOTATION,
|
||||
);
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
/** @phpstan-var list<string> */
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* Targets as bitmask.
|
||||
*
|
||||
* @var integer
|
||||
* @var int
|
||||
*/
|
||||
public $targets;
|
||||
|
||||
/**
|
||||
* Literal target declaration.
|
||||
*
|
||||
* @var integer
|
||||
* @var string
|
||||
*/
|
||||
public $literal;
|
||||
|
||||
/**
|
||||
* Annotation constructor.
|
||||
* @throws InvalidArgumentException
|
||||
*
|
||||
* @param array $values
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @phpstan-param array{value?: string|list<string>} $values
|
||||
*/
|
||||
public function __construct(array $values)
|
||||
{
|
||||
if (!isset($values['value'])){
|
||||
if (! isset($values['value'])) {
|
||||
$values['value'] = null;
|
||||
}
|
||||
if (is_string($values['value'])){
|
||||
$values['value'] = array($values['value']);
|
||||
|
||||
if (is_string($values['value'])) {
|
||||
$values['value'] = [$values['value']];
|
||||
}
|
||||
if (!is_array($values['value'])){
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf('@Target expects either a string value, or an array of strings, "%s" given.',
|
||||
|
||||
if (! is_array($values['value'])) {
|
||||
throw new InvalidArgumentException(
|
||||
sprintf(
|
||||
'@Target expects either a string value, or an array of strings, "%s" given.',
|
||||
is_object($values['value']) ? get_class($values['value']) : gettype($values['value'])
|
||||
)
|
||||
);
|
||||
|
|
@ -91,17 +81,21 @@ final class Target
|
|||
|
||||
$bitmask = 0;
|
||||
foreach ($values['value'] as $literal) {
|
||||
if(!isset(self::$map[$literal])){
|
||||
throw new \InvalidArgumentException(
|
||||
sprintf('Invalid Target "%s". Available targets: [%s]',
|
||||
$literal, implode(', ', array_keys(self::$map)))
|
||||
if (! isset(self::$map[$literal])) {
|
||||
throw new InvalidArgumentException(
|
||||
sprintf(
|
||||
'Invalid Target "%s". Available targets: [%s]',
|
||||
$literal,
|
||||
implode(', ', array_keys(self::$map))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$bitmask |= self::$map[$literal];
|
||||
}
|
||||
|
||||
$this->targets = $bitmask;
|
||||
$this->value = $values['value'];
|
||||
$this->literal = implode(', ', $this->value);
|
||||
$this->targets = $bitmask;
|
||||
$this->value = $values['value'];
|
||||
$this->literal = implode(', ', $this->value);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,19 @@
|
|||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Annotations;
|
||||
|
||||
use Exception;
|
||||
|
||||
use function get_class;
|
||||
use function gettype;
|
||||
use function implode;
|
||||
use function is_object;
|
||||
use function sprintf;
|
||||
|
||||
/**
|
||||
* Description of AnnotationException
|
||||
*
|
||||
* @since 2.0
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class AnnotationException extends \Exception
|
||||
class AnnotationException extends Exception
|
||||
{
|
||||
/**
|
||||
* Creates a new AnnotationException describing a Syntax error.
|
||||
|
|
@ -58,8 +43,6 @@ class AnnotationException extends \Exception
|
|||
* Creates a new AnnotationException describing an error which occurred during
|
||||
* the creation of the annotation.
|
||||
*
|
||||
* @since 2.2
|
||||
*
|
||||
* @param string $message
|
||||
*
|
||||
* @return AnnotationException
|
||||
|
|
@ -72,8 +55,6 @@ class AnnotationException extends \Exception
|
|||
/**
|
||||
* Creates a new AnnotationException describing a type error.
|
||||
*
|
||||
* @since 1.1
|
||||
*
|
||||
* @param string $message
|
||||
*
|
||||
* @return AnnotationException
|
||||
|
|
@ -86,8 +67,6 @@ class AnnotationException extends \Exception
|
|||
/**
|
||||
* Creates a new AnnotationException describing a constant semantical error.
|
||||
*
|
||||
* @since 2.3
|
||||
*
|
||||
* @param string $identifier
|
||||
* @param string $context
|
||||
*
|
||||
|
|
@ -105,8 +84,6 @@ class AnnotationException extends \Exception
|
|||
/**
|
||||
* Creates a new AnnotationException describing an type error of an attribute.
|
||||
*
|
||||
* @since 2.2
|
||||
*
|
||||
* @param string $attributeName
|
||||
* @param string $annotationName
|
||||
* @param string $context
|
||||
|
|
@ -130,8 +107,6 @@ class AnnotationException extends \Exception
|
|||
/**
|
||||
* Creates a new AnnotationException describing an required error of an attribute.
|
||||
*
|
||||
* @since 2.2
|
||||
*
|
||||
* @param string $attributeName
|
||||
* @param string $annotationName
|
||||
* @param string $context
|
||||
|
|
@ -153,21 +128,20 @@ class AnnotationException extends \Exception
|
|||
/**
|
||||
* Creates a new AnnotationException describing a invalid enummerator.
|
||||
*
|
||||
* @since 2.4
|
||||
*
|
||||
* @param string $attributeName
|
||||
* @param string $annotationName
|
||||
* @param string $context
|
||||
* @param array $available
|
||||
* @param mixed $given
|
||||
*
|
||||
* @return AnnotationException
|
||||
*
|
||||
* @phpstan-param list<string> $available
|
||||
*/
|
||||
public static function enumeratorError($attributeName, $annotationName, $context, $available, $given)
|
||||
{
|
||||
return new self(sprintf(
|
||||
'[Enum Error] Attribute "%s" of @%s declared on %s accept only [%s], but got %s.',
|
||||
$attributeName,
|
||||
'[Enum Error] Attribute "%s" of @%s declared on %s accepts only [%s], but got %s.',
|
||||
$attributeName,
|
||||
$annotationName,
|
||||
$context,
|
||||
implode(', ', $available),
|
||||
|
|
@ -181,7 +155,7 @@ class AnnotationException extends \Exception
|
|||
public static function optimizerPlusSaveComments()
|
||||
{
|
||||
return new self(
|
||||
"You have to enable opcache.save_comments=1 or zend_optimizerplus.save_comments=1."
|
||||
'You have to enable opcache.save_comments=1 or zend_optimizerplus.save_comments=1.'
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -191,7 +165,7 @@ class AnnotationException extends \Exception
|
|||
public static function optimizerPlusLoadComments()
|
||||
{
|
||||
return new self(
|
||||
"You have to enable opcache.load_comments=1 or zend_optimizerplus.load_comments=1."
|
||||
'You have to enable opcache.load_comments=1 or zend_optimizerplus.load_comments=1.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,122 +1,57 @@
|
|||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Annotations;
|
||||
|
||||
use Doctrine\Common\Annotations\Annotation\IgnoreAnnotation;
|
||||
use Doctrine\Common\Annotations\Annotation\Target;
|
||||
use ReflectionClass;
|
||||
use ReflectionFunction;
|
||||
use ReflectionMethod;
|
||||
use ReflectionProperty;
|
||||
|
||||
use function array_merge;
|
||||
use function class_exists;
|
||||
use function extension_loaded;
|
||||
use function ini_get;
|
||||
|
||||
/**
|
||||
* A reader for docblock annotations.
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class AnnotationReader implements Reader
|
||||
{
|
||||
/**
|
||||
* Global map for imports.
|
||||
*
|
||||
* @var array
|
||||
* @var array<string, class-string>
|
||||
*/
|
||||
private static $globalImports = array(
|
||||
'ignoreannotation' => 'Doctrine\Common\Annotations\Annotation\IgnoreAnnotation',
|
||||
);
|
||||
private static $globalImports = [
|
||||
'ignoreannotation' => Annotation\IgnoreAnnotation::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* A list with annotations that are not causing exceptions when not resolved to an annotation class.
|
||||
*
|
||||
* The names are case sensitive.
|
||||
*
|
||||
* @var array
|
||||
* @var array<string, true>
|
||||
*/
|
||||
private static $globalIgnoredNames = array(
|
||||
// Annotation tags
|
||||
'Annotation' => true, 'Attribute' => true, 'Attributes' => true,
|
||||
/* Can we enable this? 'Enum' => true, */
|
||||
'Required' => true,
|
||||
'Target' => true,
|
||||
// Widely used tags (but not existent in phpdoc)
|
||||
'fix' => true , 'fixme' => true,
|
||||
'override' => true,
|
||||
// PHPDocumentor 1 tags
|
||||
'abstract'=> true, 'access'=> true,
|
||||
'code' => true,
|
||||
'deprec'=> true,
|
||||
'endcode' => true, 'exception'=> true,
|
||||
'final'=> true,
|
||||
'ingroup' => true, 'inheritdoc'=> true, 'inheritDoc'=> true,
|
||||
'magic' => true,
|
||||
'name'=> true,
|
||||
'toc' => true, 'tutorial'=> true,
|
||||
'private' => true,
|
||||
'static'=> true, 'staticvar'=> true, 'staticVar'=> true,
|
||||
'throw' => true,
|
||||
// PHPDocumentor 2 tags.
|
||||
'api' => true, 'author'=> true,
|
||||
'category'=> true, 'copyright'=> true,
|
||||
'deprecated'=> true,
|
||||
'example'=> true,
|
||||
'filesource'=> true,
|
||||
'global'=> true,
|
||||
'ignore'=> true, /* Can we enable this? 'index' => true, */ 'internal'=> true,
|
||||
'license'=> true, 'link'=> true,
|
||||
'method' => true,
|
||||
'package'=> true, 'param'=> true, 'property' => true, 'property-read' => true, 'property-write' => true,
|
||||
'return'=> true,
|
||||
'see'=> true, 'since'=> true, 'source' => true, 'subpackage'=> true,
|
||||
'throws'=> true, 'todo'=> true, 'TODO'=> true,
|
||||
'usedby'=> true, 'uses' => true,
|
||||
'var'=> true, 'version'=> true,
|
||||
// PHPUnit tags
|
||||
'codeCoverageIgnore' => true, 'codeCoverageIgnoreStart' => true, 'codeCoverageIgnoreEnd' => true,
|
||||
// PHPCheckStyle
|
||||
'SuppressWarnings' => true,
|
||||
// PHPStorm
|
||||
'noinspection' => true,
|
||||
// PEAR
|
||||
'package_version' => true,
|
||||
// PlantUML
|
||||
'startuml' => true, 'enduml' => true,
|
||||
);
|
||||
private static $globalIgnoredNames = ImplicitlyIgnoredAnnotationNames::LIST;
|
||||
|
||||
/**
|
||||
* A list with annotations that are not causing exceptions when not resolved to an annotation class.
|
||||
*
|
||||
* The names are case sensitive.
|
||||
*
|
||||
* @var array
|
||||
* @var array<string, true>
|
||||
*/
|
||||
private static $globalIgnoredNamespaces = array();
|
||||
private static $globalIgnoredNamespaces = [];
|
||||
|
||||
/**
|
||||
* Add a new annotation to the globally ignored annotation names with regard to exception handling.
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
static public function addGlobalIgnoredName($name)
|
||||
public static function addGlobalIgnoredName($name)
|
||||
{
|
||||
self::$globalIgnoredNames[$name] = true;
|
||||
}
|
||||
|
|
@ -126,7 +61,7 @@ class AnnotationReader implements Reader
|
|||
*
|
||||
* @param string $namespace
|
||||
*/
|
||||
static public function addGlobalIgnoredNamespace($namespace)
|
||||
public static function addGlobalIgnoredNamespace($namespace)
|
||||
{
|
||||
self::$globalIgnoredNamespaces[$namespace] = true;
|
||||
}
|
||||
|
|
@ -134,75 +69,68 @@ class AnnotationReader implements Reader
|
|||
/**
|
||||
* Annotations parser.
|
||||
*
|
||||
* @var \Doctrine\Common\Annotations\DocParser
|
||||
* @var DocParser
|
||||
*/
|
||||
private $parser;
|
||||
|
||||
/**
|
||||
* Annotations parser used to collect parsing metadata.
|
||||
*
|
||||
* @var \Doctrine\Common\Annotations\DocParser
|
||||
* @var DocParser
|
||||
*/
|
||||
private $preParser;
|
||||
|
||||
/**
|
||||
* PHP parser used to collect imports.
|
||||
*
|
||||
* @var \Doctrine\Common\Annotations\PhpParser
|
||||
* @var PhpParser
|
||||
*/
|
||||
private $phpParser;
|
||||
|
||||
/**
|
||||
* In-memory cache mechanism to store imported annotations per class.
|
||||
*
|
||||
* @var array
|
||||
* @psalm-var array<'class'|'function', array<string, array<string, class-string>>>
|
||||
*/
|
||||
private $imports = array();
|
||||
private $imports = [];
|
||||
|
||||
/**
|
||||
* In-memory cache mechanism to store ignored annotations per class.
|
||||
*
|
||||
* @var array
|
||||
* @psalm-var array<'class'|'function', array<string, array<string, true>>>
|
||||
*/
|
||||
private $ignoredAnnotationNames = array();
|
||||
private $ignoredAnnotationNames = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Initializes a new AnnotationReader.
|
||||
*
|
||||
* @param DocParser $parser
|
||||
* @throws AnnotationException
|
||||
*/
|
||||
public function __construct(DocParser $parser = null)
|
||||
public function __construct(?DocParser $parser = null)
|
||||
{
|
||||
if (extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.save_comments') === "0" || ini_get('opcache.save_comments') === "0")) {
|
||||
if (
|
||||
extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.save_comments') === '0' ||
|
||||
ini_get('opcache.save_comments') === '0')
|
||||
) {
|
||||
throw AnnotationException::optimizerPlusSaveComments();
|
||||
}
|
||||
|
||||
if (extension_loaded('Zend OPcache') && ini_get('opcache.save_comments') == 0) {
|
||||
if (extension_loaded('Zend OPcache') && ini_get('opcache.save_comments') === 0) {
|
||||
throw AnnotationException::optimizerPlusSaveComments();
|
||||
}
|
||||
|
||||
if (PHP_VERSION_ID < 70000) {
|
||||
if (extension_loaded('Zend Optimizer+') && (ini_get('zend_optimizerplus.load_comments') === "0" || ini_get('opcache.load_comments') === "0")) {
|
||||
throw AnnotationException::optimizerPlusLoadComments();
|
||||
}
|
||||
|
||||
if (extension_loaded('Zend OPcache') && ini_get('opcache.load_comments') == 0) {
|
||||
throw AnnotationException::optimizerPlusLoadComments();
|
||||
}
|
||||
}
|
||||
|
||||
AnnotationRegistry::registerFile(__DIR__ . '/Annotation/IgnoreAnnotation.php');
|
||||
// Make sure that the IgnoreAnnotation annotation is loaded
|
||||
class_exists(IgnoreAnnotation::class);
|
||||
|
||||
$this->parser = $parser ?: new DocParser();
|
||||
|
||||
$this->preParser = new DocParser;
|
||||
$this->preParser = new DocParser();
|
||||
|
||||
$this->preParser->setImports(self::$globalImports);
|
||||
$this->preParser->setIgnoreNotImportedAnnotations(true);
|
||||
$this->preParser->setIgnoredAnnotationNames(self::$globalIgnoredNames);
|
||||
|
||||
$this->phpParser = new PhpParser;
|
||||
$this->phpParser = new PhpParser();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -211,7 +139,7 @@ class AnnotationReader implements Reader
|
|||
public function getClassAnnotations(ReflectionClass $class)
|
||||
{
|
||||
$this->parser->setTarget(Target::TARGET_CLASS);
|
||||
$this->parser->setImports($this->getClassImports($class));
|
||||
$this->parser->setImports($this->getImports($class));
|
||||
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class));
|
||||
$this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
|
||||
|
||||
|
|
@ -240,7 +168,7 @@ class AnnotationReader implements Reader
|
|||
public function getPropertyAnnotations(ReflectionProperty $property)
|
||||
{
|
||||
$class = $property->getDeclaringClass();
|
||||
$context = 'property ' . $class->getName() . "::\$" . $property->getName();
|
||||
$context = 'property ' . $class->getName() . '::$' . $property->getName();
|
||||
|
||||
$this->parser->setTarget(Target::TARGET_PROPERTY);
|
||||
$this->parser->setImports($this->getPropertyImports($property));
|
||||
|
|
@ -299,66 +227,103 @@ class AnnotationReader implements Reader
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the ignored annotations for the given class.
|
||||
* Gets the annotations applied to a function.
|
||||
*
|
||||
* @param \ReflectionClass $class
|
||||
*
|
||||
* @return array
|
||||
* @phpstan-return list<object> An array of Annotations.
|
||||
*/
|
||||
private function getIgnoredAnnotationNames(ReflectionClass $class)
|
||||
public function getFunctionAnnotations(ReflectionFunction $function): array
|
||||
{
|
||||
$name = $class->getName();
|
||||
if (isset($this->ignoredAnnotationNames[$name])) {
|
||||
return $this->ignoredAnnotationNames[$name];
|
||||
}
|
||||
$context = 'function ' . $function->getName();
|
||||
|
||||
$this->collectParsingMetadata($class);
|
||||
$this->parser->setTarget(Target::TARGET_FUNCTION);
|
||||
$this->parser->setImports($this->getImports($function));
|
||||
$this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($function));
|
||||
$this->parser->setIgnoredAnnotationNamespaces(self::$globalIgnoredNamespaces);
|
||||
|
||||
return $this->ignoredAnnotationNames[$name];
|
||||
return $this->parser->parse($function->getDocComment(), $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves imports.
|
||||
* Gets a function annotation.
|
||||
*
|
||||
* @param \ReflectionClass $class
|
||||
*
|
||||
* @return array
|
||||
* @return object|null The Annotation or NULL, if the requested annotation does not exist.
|
||||
*/
|
||||
private function getClassImports(ReflectionClass $class)
|
||||
public function getFunctionAnnotation(ReflectionFunction $function, string $annotationName)
|
||||
{
|
||||
$name = $class->getName();
|
||||
if (isset($this->imports[$name])) {
|
||||
return $this->imports[$name];
|
||||
$annotations = $this->getFunctionAnnotations($function);
|
||||
|
||||
foreach ($annotations as $annotation) {
|
||||
if ($annotation instanceof $annotationName) {
|
||||
return $annotation;
|
||||
}
|
||||
}
|
||||
|
||||
$this->collectParsingMetadata($class);
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->imports[$name];
|
||||
/**
|
||||
* Returns the ignored annotations for the given class or function.
|
||||
*
|
||||
* @param ReflectionClass|ReflectionFunction $reflection
|
||||
*
|
||||
* @return array<string, true>
|
||||
*/
|
||||
private function getIgnoredAnnotationNames($reflection): array
|
||||
{
|
||||
$type = $reflection instanceof ReflectionClass ? 'class' : 'function';
|
||||
$name = $reflection->getName();
|
||||
|
||||
if (isset($this->ignoredAnnotationNames[$type][$name])) {
|
||||
return $this->ignoredAnnotationNames[$type][$name];
|
||||
}
|
||||
|
||||
$this->collectParsingMetadata($reflection);
|
||||
|
||||
return $this->ignoredAnnotationNames[$type][$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves imports for a class or a function.
|
||||
*
|
||||
* @param ReflectionClass|ReflectionFunction $reflection
|
||||
*
|
||||
* @return array<string, class-string>
|
||||
*/
|
||||
private function getImports($reflection): array
|
||||
{
|
||||
$type = $reflection instanceof ReflectionClass ? 'class' : 'function';
|
||||
$name = $reflection->getName();
|
||||
|
||||
if (isset($this->imports[$type][$name])) {
|
||||
return $this->imports[$type][$name];
|
||||
}
|
||||
|
||||
$this->collectParsingMetadata($reflection);
|
||||
|
||||
return $this->imports[$type][$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves imports for methods.
|
||||
*
|
||||
* @param \ReflectionMethod $method
|
||||
*
|
||||
* @return array
|
||||
* @return array<string, class-string>
|
||||
*/
|
||||
private function getMethodImports(ReflectionMethod $method)
|
||||
{
|
||||
$class = $method->getDeclaringClass();
|
||||
$classImports = $this->getClassImports($class);
|
||||
if (!method_exists($class, 'getTraits')) {
|
||||
return $classImports;
|
||||
}
|
||||
$class = $method->getDeclaringClass();
|
||||
$classImports = $this->getImports($class);
|
||||
|
||||
$traitImports = array();
|
||||
$traitImports = [];
|
||||
|
||||
foreach ($class->getTraits() as $trait) {
|
||||
if ($trait->hasMethod($method->getName())
|
||||
&& $trait->getFileName() === $method->getFileName()
|
||||
if (
|
||||
! $trait->hasMethod($method->getName())
|
||||
|| $trait->getFileName() !== $method->getFileName()
|
||||
) {
|
||||
$traitImports = array_merge($traitImports, $this->phpParser->parseClass($trait));
|
||||
continue;
|
||||
}
|
||||
|
||||
$traitImports = array_merge($traitImports, $this->phpParser->parseUseStatements($trait));
|
||||
}
|
||||
|
||||
return array_merge($classImports, $traitImports);
|
||||
|
|
@ -367,55 +332,58 @@ class AnnotationReader implements Reader
|
|||
/**
|
||||
* Retrieves imports for properties.
|
||||
*
|
||||
* @param \ReflectionProperty $property
|
||||
*
|
||||
* @return array
|
||||
* @return array<string, class-string>
|
||||
*/
|
||||
private function getPropertyImports(ReflectionProperty $property)
|
||||
{
|
||||
$class = $property->getDeclaringClass();
|
||||
$classImports = $this->getClassImports($class);
|
||||
if (!method_exists($class, 'getTraits')) {
|
||||
return $classImports;
|
||||
}
|
||||
$class = $property->getDeclaringClass();
|
||||
$classImports = $this->getImports($class);
|
||||
|
||||
$traitImports = array();
|
||||
$traitImports = [];
|
||||
|
||||
foreach ($class->getTraits() as $trait) {
|
||||
if ($trait->hasProperty($property->getName())) {
|
||||
$traitImports = array_merge($traitImports, $this->phpParser->parseClass($trait));
|
||||
if (! $trait->hasProperty($property->getName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$traitImports = array_merge($traitImports, $this->phpParser->parseUseStatements($trait));
|
||||
}
|
||||
|
||||
return array_merge($classImports, $traitImports);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects parsing metadata for a given class.
|
||||
* Collects parsing metadata for a given class or function.
|
||||
*
|
||||
* @param \ReflectionClass $class
|
||||
* @param ReflectionClass|ReflectionFunction $reflection
|
||||
*/
|
||||
private function collectParsingMetadata(ReflectionClass $class)
|
||||
private function collectParsingMetadata($reflection): void
|
||||
{
|
||||
$type = $reflection instanceof ReflectionClass ? 'class' : 'function';
|
||||
$name = $reflection->getName();
|
||||
|
||||
$ignoredAnnotationNames = self::$globalIgnoredNames;
|
||||
$annotations = $this->preParser->parse($class->getDocComment(), 'class ' . $class->name);
|
||||
$annotations = $this->preParser->parse($reflection->getDocComment(), $type . ' ' . $name);
|
||||
|
||||
foreach ($annotations as $annotation) {
|
||||
if ($annotation instanceof IgnoreAnnotation) {
|
||||
foreach ($annotation->names AS $annot) {
|
||||
$ignoredAnnotationNames[$annot] = true;
|
||||
}
|
||||
if (! ($annotation instanceof IgnoreAnnotation)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($annotation->names as $annot) {
|
||||
$ignoredAnnotationNames[$annot] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$name = $class->getName();
|
||||
|
||||
$this->imports[$name] = array_merge(
|
||||
$this->imports[$type][$name] = array_merge(
|
||||
self::$globalImports,
|
||||
$this->phpParser->parseClass($class),
|
||||
array('__NAMESPACE__' => $class->getNamespaceName())
|
||||
$this->phpParser->parseUseStatements($reflection),
|
||||
[
|
||||
'__NAMESPACE__' => $reflection->getNamespaceName(),
|
||||
'self' => $name,
|
||||
]
|
||||
);
|
||||
|
||||
$this->ignoredAnnotationNames[$name] = $ignoredAnnotationNames;
|
||||
$this->ignoredAnnotationNames[$type][$name] = $ignoredAnnotationNames;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,18 @@
|
|||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Annotations;
|
||||
|
||||
/**
|
||||
* AnnotationRegistry.
|
||||
*/
|
||||
use function array_key_exists;
|
||||
use function array_merge;
|
||||
use function class_exists;
|
||||
use function in_array;
|
||||
use function is_file;
|
||||
use function str_replace;
|
||||
use function stream_resolve_include_path;
|
||||
use function strpos;
|
||||
|
||||
use const DIRECTORY_SEPARATOR;
|
||||
|
||||
final class AnnotationRegistry
|
||||
{
|
||||
/**
|
||||
|
|
@ -32,35 +23,49 @@ final class AnnotationRegistry
|
|||
*
|
||||
* This autoloading mechanism does not utilize the PHP autoloading but implements autoloading on its own.
|
||||
*
|
||||
* @var array
|
||||
* @var string[][]|string[]|null[]
|
||||
*/
|
||||
static private $autoloadNamespaces = array();
|
||||
private static $autoloadNamespaces = [];
|
||||
|
||||
/**
|
||||
* A map of autoloader callables.
|
||||
*
|
||||
* @var array
|
||||
* @var callable[]
|
||||
*/
|
||||
static private $loaders = array();
|
||||
private static $loaders = [];
|
||||
|
||||
/**
|
||||
* @return void
|
||||
* An array of classes which cannot be found
|
||||
*
|
||||
* @var null[] indexed by class name
|
||||
*/
|
||||
static public function reset()
|
||||
private static $failedToAutoload = [];
|
||||
|
||||
/**
|
||||
* Whenever registerFile() was used. Disables use of standard autoloader.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
private static $registerFileUsed = false;
|
||||
|
||||
public static function reset(): void
|
||||
{
|
||||
self::$autoloadNamespaces = array();
|
||||
self::$loaders = array();
|
||||
self::$autoloadNamespaces = [];
|
||||
self::$loaders = [];
|
||||
self::$failedToAutoload = [];
|
||||
self::$registerFileUsed = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers file.
|
||||
*
|
||||
* @param string $file
|
||||
*
|
||||
* @return void
|
||||
* @deprecated This method is deprecated and will be removed in
|
||||
* doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
|
||||
*/
|
||||
static public function registerFile($file)
|
||||
public static function registerFile(string $file): void
|
||||
{
|
||||
self::$registerFileUsed = true;
|
||||
|
||||
require_once $file;
|
||||
}
|
||||
|
||||
|
|
@ -69,12 +74,12 @@ final class AnnotationRegistry
|
|||
*
|
||||
* Loading of this namespaces will be done with a PSR-0 namespace loading algorithm.
|
||||
*
|
||||
* @param string $namespace
|
||||
* @param string|array|null $dirs
|
||||
* @deprecated This method is deprecated and will be removed in
|
||||
* doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
|
||||
*
|
||||
* @return void
|
||||
* @phpstan-param string|list<string>|null $dirs
|
||||
*/
|
||||
static public function registerAutoloadNamespace($namespace, $dirs = null)
|
||||
public static function registerAutoloadNamespace(string $namespace, $dirs = null): void
|
||||
{
|
||||
self::$autoloadNamespaces[$namespace] = $dirs;
|
||||
}
|
||||
|
|
@ -84,11 +89,12 @@ final class AnnotationRegistry
|
|||
*
|
||||
* Loading of this namespaces will be done with a PSR-0 namespace loading algorithm.
|
||||
*
|
||||
* @param array $namespaces
|
||||
* @deprecated This method is deprecated and will be removed in
|
||||
* doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
|
||||
*
|
||||
* @return void
|
||||
* @param string[][]|string[]|null[] $namespaces indexed by namespace name
|
||||
*/
|
||||
static public function registerAutoloadNamespaces(array $namespaces)
|
||||
public static function registerAutoloadNamespaces(array $namespaces): void
|
||||
{
|
||||
self::$autoloadNamespaces = array_merge(self::$autoloadNamespaces, $namespaces);
|
||||
}
|
||||
|
|
@ -99,53 +105,86 @@ final class AnnotationRegistry
|
|||
* NOTE: These class loaders HAVE to be silent when a class was not found!
|
||||
* IMPORTANT: Loaders have to return true if they loaded a class that could contain the searched annotation class.
|
||||
*
|
||||
* @param callable $callable
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @deprecated This method is deprecated and will be removed in
|
||||
* doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
|
||||
*/
|
||||
static public function registerLoader($callable)
|
||||
public static function registerLoader(callable $callable): void
|
||||
{
|
||||
if (!is_callable($callable)) {
|
||||
throw new \InvalidArgumentException("A callable is expected in AnnotationRegistry::registerLoader().");
|
||||
// Reset our static cache now that we have a new loader to work with
|
||||
self::$failedToAutoload = [];
|
||||
self::$loaders[] = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an autoloading callable for annotations, if it is not already registered
|
||||
*
|
||||
* @deprecated This method is deprecated and will be removed in
|
||||
* doctrine/annotations 2.0. Annotations will be autoloaded in 2.0.
|
||||
*/
|
||||
public static function registerUniqueLoader(callable $callable): void
|
||||
{
|
||||
if (in_array($callable, self::$loaders, true)) {
|
||||
return;
|
||||
}
|
||||
self::$loaders[] = $callable;
|
||||
|
||||
self::registerLoader($callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Autoloads an annotation class silently.
|
||||
*
|
||||
* @param string $class
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
static public function loadAnnotationClass($class)
|
||||
public static function loadAnnotationClass(string $class): bool
|
||||
{
|
||||
foreach (self::$autoloadNamespaces AS $namespace => $dirs) {
|
||||
if (strpos($class, $namespace) === 0) {
|
||||
$file = str_replace("\\", DIRECTORY_SEPARATOR, $class) . ".php";
|
||||
if ($dirs === null) {
|
||||
if ($path = stream_resolve_include_path($file)) {
|
||||
require $path;
|
||||
if (class_exists($class, false)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (array_key_exists($class, self::$failedToAutoload)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (self::$autoloadNamespaces as $namespace => $dirs) {
|
||||
if (strpos($class, $namespace) !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$file = str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
|
||||
|
||||
if ($dirs === null) {
|
||||
$path = stream_resolve_include_path($file);
|
||||
if ($path) {
|
||||
require $path;
|
||||
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
foreach ((array) $dirs as $dir) {
|
||||
if (is_file($dir . DIRECTORY_SEPARATOR . $file)) {
|
||||
require $dir . DIRECTORY_SEPARATOR . $file;
|
||||
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
foreach((array)$dirs AS $dir) {
|
||||
if (is_file($dir . DIRECTORY_SEPARATOR . $file)) {
|
||||
require $dir . DIRECTORY_SEPARATOR . $file;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (self::$loaders AS $loader) {
|
||||
if (call_user_func($loader, $class) === true) {
|
||||
foreach (self::$loaders as $loader) {
|
||||
if ($loader($class) === true) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
self::$loaders === [] &&
|
||||
self::$autoloadNamespaces === [] &&
|
||||
self::$registerFileUsed === false &&
|
||||
class_exists($class)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
self::$failedToAutoload[$class] = null;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,67 +1,47 @@
|
|||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Annotations;
|
||||
|
||||
use Doctrine\Common\Cache\Cache;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use ReflectionProperty;
|
||||
|
||||
use function array_map;
|
||||
use function array_merge;
|
||||
use function assert;
|
||||
use function filemtime;
|
||||
use function max;
|
||||
use function time;
|
||||
|
||||
/**
|
||||
* A cache aware annotation reader.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*/
|
||||
final class CachedReader implements Reader
|
||||
{
|
||||
/**
|
||||
* @var Reader
|
||||
*/
|
||||
/** @var Reader */
|
||||
private $delegate;
|
||||
|
||||
/**
|
||||
* @var Cache
|
||||
*/
|
||||
/** @var Cache */
|
||||
private $cache;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
/** @var bool */
|
||||
private $debug;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $loadedAnnotations = array();
|
||||
/** @var array<string, array<object>> */
|
||||
private $loadedAnnotations = [];
|
||||
|
||||
/** @var int[] */
|
||||
private $loadedFilemtimes = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Reader $reader
|
||||
* @param Cache $cache
|
||||
* @param bool $debug
|
||||
* @param bool $debug
|
||||
*/
|
||||
public function __construct(Reader $reader, Cache $cache, $debug = false)
|
||||
{
|
||||
$this->delegate = $reader;
|
||||
$this->cache = $cache;
|
||||
$this->debug = (boolean) $debug;
|
||||
$this->cache = $cache;
|
||||
$this->debug = (bool) $debug;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -75,7 +55,8 @@ final class CachedReader implements Reader
|
|||
return $this->loadedAnnotations[$cacheKey];
|
||||
}
|
||||
|
||||
if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) {
|
||||
$annots = $this->fetchFromCache($cacheKey, $class);
|
||||
if ($annots === false) {
|
||||
$annots = $this->delegate->getClassAnnotations($class);
|
||||
$this->saveToCache($cacheKey, $annots);
|
||||
}
|
||||
|
|
@ -100,16 +81,17 @@ final class CachedReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getPropertyAnnotations(\ReflectionProperty $property)
|
||||
public function getPropertyAnnotations(ReflectionProperty $property)
|
||||
{
|
||||
$class = $property->getDeclaringClass();
|
||||
$cacheKey = $class->getName().'$'.$property->getName();
|
||||
$class = $property->getDeclaringClass();
|
||||
$cacheKey = $class->getName() . '$' . $property->getName();
|
||||
|
||||
if (isset($this->loadedAnnotations[$cacheKey])) {
|
||||
return $this->loadedAnnotations[$cacheKey];
|
||||
}
|
||||
|
||||
if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) {
|
||||
$annots = $this->fetchFromCache($cacheKey, $class);
|
||||
if ($annots === false) {
|
||||
$annots = $this->delegate->getPropertyAnnotations($property);
|
||||
$this->saveToCache($cacheKey, $annots);
|
||||
}
|
||||
|
|
@ -120,7 +102,7 @@ final class CachedReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName)
|
||||
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
|
||||
{
|
||||
foreach ($this->getPropertyAnnotations($property) as $annot) {
|
||||
if ($annot instanceof $annotationName) {
|
||||
|
|
@ -134,16 +116,17 @@ final class CachedReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getMethodAnnotations(\ReflectionMethod $method)
|
||||
public function getMethodAnnotations(ReflectionMethod $method)
|
||||
{
|
||||
$class = $method->getDeclaringClass();
|
||||
$cacheKey = $class->getName().'#'.$method->getName();
|
||||
$class = $method->getDeclaringClass();
|
||||
$cacheKey = $class->getName() . '#' . $method->getName();
|
||||
|
||||
if (isset($this->loadedAnnotations[$cacheKey])) {
|
||||
return $this->loadedAnnotations[$cacheKey];
|
||||
}
|
||||
|
||||
if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) {
|
||||
$annots = $this->fetchFromCache($cacheKey, $class);
|
||||
if ($annots === false) {
|
||||
$annots = $this->delegate->getMethodAnnotations($method);
|
||||
$this->saveToCache($cacheKey, $annots);
|
||||
}
|
||||
|
|
@ -154,7 +137,7 @@ final class CachedReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getMethodAnnotation(\ReflectionMethod $method, $annotationName)
|
||||
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
|
||||
{
|
||||
foreach ($this->getMethodAnnotations($method) as $annot) {
|
||||
if ($annot instanceof $annotationName) {
|
||||
|
|
@ -172,21 +155,22 @@ final class CachedReader implements Reader
|
|||
*/
|
||||
public function clearLoadedAnnotations()
|
||||
{
|
||||
$this->loadedAnnotations = array();
|
||||
$this->loadedAnnotations = [];
|
||||
$this->loadedFilemtimes = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a value from the cache.
|
||||
*
|
||||
* @param string $cacheKey The cache key.
|
||||
* @param ReflectionClass $class The related class.
|
||||
* @param string $cacheKey The cache key.
|
||||
*
|
||||
* @return mixed The cached value or false when the value is not in cache.
|
||||
*/
|
||||
private function fetchFromCache($cacheKey, ReflectionClass $class)
|
||||
{
|
||||
if (($data = $this->cache->fetch($cacheKey)) !== false) {
|
||||
if (!$this->debug || $this->isCacheFresh($cacheKey, $class)) {
|
||||
$data = $this->cache->fetch($cacheKey);
|
||||
if ($data !== false) {
|
||||
if (! $this->debug || $this->isCacheFresh($cacheKey, $class)) {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
|
@ -205,58 +189,76 @@ final class CachedReader implements Reader
|
|||
private function saveToCache($cacheKey, $value)
|
||||
{
|
||||
$this->cache->save($cacheKey, $value);
|
||||
if ($this->debug) {
|
||||
$this->cache->save('[C]'.$cacheKey, time());
|
||||
if (! $this->debug) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->cache->save('[C]' . $cacheKey, time());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the cache is fresh.
|
||||
*
|
||||
* @param string $cacheKey
|
||||
* @param ReflectionClass $class
|
||||
* @param string $cacheKey
|
||||
*
|
||||
* @return boolean
|
||||
* @return bool
|
||||
*/
|
||||
private function isCacheFresh($cacheKey, ReflectionClass $class)
|
||||
{
|
||||
if (null === $lastModification = $this->getLastModification($class)) {
|
||||
$lastModification = $this->getLastModification($class);
|
||||
if ($lastModification === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->cache->fetch('[C]'.$cacheKey) >= $lastModification;
|
||||
return $this->cache->fetch('[C]' . $cacheKey) >= $lastModification;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time the class was last modified, testing traits and parents
|
||||
*
|
||||
* @param ReflectionClass $class
|
||||
* @return int
|
||||
*/
|
||||
private function getLastModification(ReflectionClass $class)
|
||||
private function getLastModification(ReflectionClass $class): int
|
||||
{
|
||||
$filename = $class->getFileName();
|
||||
$parent = $class->getParentClass();
|
||||
|
||||
return max(array_merge(
|
||||
if (isset($this->loadedFilemtimes[$filename])) {
|
||||
return $this->loadedFilemtimes[$filename];
|
||||
}
|
||||
|
||||
$parent = $class->getParentClass();
|
||||
|
||||
$lastModification = max(array_merge(
|
||||
[$filename ? filemtime($filename) : 0],
|
||||
array_map([$this, 'getTraitLastModificationTime'], $class->getTraits()),
|
||||
array_map([$this, 'getLastModification'], $class->getInterfaces()),
|
||||
array_map(function (ReflectionClass $reflectionTrait): int {
|
||||
return $this->getTraitLastModificationTime($reflectionTrait);
|
||||
}, $class->getTraits()),
|
||||
array_map(function (ReflectionClass $class): int {
|
||||
return $this->getLastModification($class);
|
||||
}, $class->getInterfaces()),
|
||||
$parent ? [$this->getLastModification($parent)] : []
|
||||
));
|
||||
|
||||
assert($lastModification !== false);
|
||||
|
||||
return $this->loadedFilemtimes[$filename] = $lastModification;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ReflectionClass $reflectionTrait
|
||||
* @return int
|
||||
*/
|
||||
private function getTraitLastModificationTime(ReflectionClass $reflectionTrait)
|
||||
private function getTraitLastModificationTime(ReflectionClass $reflectionTrait): int
|
||||
{
|
||||
$fileName = $reflectionTrait->getFileName();
|
||||
|
||||
return max(array_merge(
|
||||
if (isset($this->loadedFilemtimes[$fileName])) {
|
||||
return $this->loadedFilemtimes[$fileName];
|
||||
}
|
||||
|
||||
$lastModificationTime = max(array_merge(
|
||||
[$fileName ? filemtime($fileName) : 0],
|
||||
array_map([$this, 'getTraitLastModificationTime'], $reflectionTrait->getTraits())
|
||||
array_map(function (ReflectionClass $reflectionTrait): int {
|
||||
return $this->getTraitLastModificationTime($reflectionTrait);
|
||||
}, $reflectionTrait->getTraits())
|
||||
));
|
||||
|
||||
assert($lastModificationTime !== false);
|
||||
|
||||
return $this->loadedFilemtimes[$fileName] = $lastModificationTime;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,61 +1,46 @@
|
|||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Annotations;
|
||||
|
||||
use Doctrine\Common\Lexer\AbstractLexer;
|
||||
|
||||
use function ctype_alpha;
|
||||
use function is_numeric;
|
||||
use function str_replace;
|
||||
use function stripos;
|
||||
use function strlen;
|
||||
use function strpos;
|
||||
use function strtolower;
|
||||
use function substr;
|
||||
|
||||
/**
|
||||
* Simple lexer for docblock annotations.
|
||||
*
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
||||
* @author Jonathan Wage <jonwage@gmail.com>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
final class DocLexer extends AbstractLexer
|
||||
{
|
||||
const T_NONE = 1;
|
||||
const T_INTEGER = 2;
|
||||
const T_STRING = 3;
|
||||
const T_FLOAT = 4;
|
||||
public const T_NONE = 1;
|
||||
public const T_INTEGER = 2;
|
||||
public const T_STRING = 3;
|
||||
public const T_FLOAT = 4;
|
||||
|
||||
// All tokens that are also identifiers should be >= 100
|
||||
const T_IDENTIFIER = 100;
|
||||
const T_AT = 101;
|
||||
const T_CLOSE_CURLY_BRACES = 102;
|
||||
const T_CLOSE_PARENTHESIS = 103;
|
||||
const T_COMMA = 104;
|
||||
const T_EQUALS = 105;
|
||||
const T_FALSE = 106;
|
||||
const T_NAMESPACE_SEPARATOR = 107;
|
||||
const T_OPEN_CURLY_BRACES = 108;
|
||||
const T_OPEN_PARENTHESIS = 109;
|
||||
const T_TRUE = 110;
|
||||
const T_NULL = 111;
|
||||
const T_COLON = 112;
|
||||
public const T_IDENTIFIER = 100;
|
||||
public const T_AT = 101;
|
||||
public const T_CLOSE_CURLY_BRACES = 102;
|
||||
public const T_CLOSE_PARENTHESIS = 103;
|
||||
public const T_COMMA = 104;
|
||||
public const T_EQUALS = 105;
|
||||
public const T_FALSE = 106;
|
||||
public const T_NAMESPACE_SEPARATOR = 107;
|
||||
public const T_OPEN_CURLY_BRACES = 108;
|
||||
public const T_OPEN_PARENTHESIS = 109;
|
||||
public const T_TRUE = 110;
|
||||
public const T_NULL = 111;
|
||||
public const T_COLON = 112;
|
||||
public const T_MINUS = 113;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $noCase = array(
|
||||
/** @var array<string, int> */
|
||||
protected $noCase = [
|
||||
'@' => self::T_AT,
|
||||
',' => self::T_COMMA,
|
||||
'(' => self::T_OPEN_PARENTHESIS,
|
||||
|
|
@ -64,28 +49,38 @@ final class DocLexer extends AbstractLexer
|
|||
'}' => self::T_CLOSE_CURLY_BRACES,
|
||||
'=' => self::T_EQUALS,
|
||||
':' => self::T_COLON,
|
||||
'\\' => self::T_NAMESPACE_SEPARATOR
|
||||
);
|
||||
'-' => self::T_MINUS,
|
||||
'\\' => self::T_NAMESPACE_SEPARATOR,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $withCase = array(
|
||||
/** @var array<string, int> */
|
||||
protected $withCase = [
|
||||
'true' => self::T_TRUE,
|
||||
'false' => self::T_FALSE,
|
||||
'null' => self::T_NULL
|
||||
);
|
||||
'null' => self::T_NULL,
|
||||
];
|
||||
|
||||
/**
|
||||
* Whether the next token starts immediately, or if there were
|
||||
* non-captured symbols before that
|
||||
*/
|
||||
public function nextTokenIsAdjacent(): bool
|
||||
{
|
||||
return $this->token === null
|
||||
|| ($this->lookahead !== null
|
||||
&& ($this->lookahead['position'] - $this->token['position']) === strlen($this->token['value']));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function getCatchablePatterns()
|
||||
{
|
||||
return array(
|
||||
return [
|
||||
'[a-z_\\\][a-z0-9_\:\\\]*[a-z_][a-z0-9_]*',
|
||||
'(?:[+-]?[0-9]+(?:[\.][0-9]+)*)(?:[eE][+-]?[0-9]+)?',
|
||||
'"(?:""|[^"])*+"',
|
||||
);
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -93,7 +88,7 @@ final class DocLexer extends AbstractLexer
|
|||
*/
|
||||
protected function getNonCatchablePatterns()
|
||||
{
|
||||
return array('\s+', '\*+', '(.)');
|
||||
return ['\s+', '\*+', '(.)'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -125,7 +120,7 @@ final class DocLexer extends AbstractLexer
|
|||
|
||||
// Checking numeric value
|
||||
if (is_numeric($value)) {
|
||||
return (strpos($value, '.') !== false || stripos($value, 'e') !== false)
|
||||
return strpos($value, '.') !== false || stripos($value, 'e') !== false
|
||||
? self::T_FLOAT : self::T_INTEGER;
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,89 +1,84 @@
|
|||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Annotations;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use ReflectionProperty;
|
||||
use RuntimeException;
|
||||
|
||||
use function chmod;
|
||||
use function file_put_contents;
|
||||
use function filemtime;
|
||||
use function gettype;
|
||||
use function is_dir;
|
||||
use function is_file;
|
||||
use function is_int;
|
||||
use function is_writable;
|
||||
use function mkdir;
|
||||
use function rename;
|
||||
use function rtrim;
|
||||
use function serialize;
|
||||
use function sha1;
|
||||
use function sprintf;
|
||||
use function strtr;
|
||||
use function tempnam;
|
||||
use function uniqid;
|
||||
use function unlink;
|
||||
use function var_export;
|
||||
|
||||
/**
|
||||
* File cache reader for annotations.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
* @author Benjamin Eberlei <kontakt@beberlei.de>
|
||||
*
|
||||
* @deprecated the FileCacheReader is deprecated and will be removed
|
||||
* in version 2.0.0 of doctrine/annotations. Please use the
|
||||
* {@see \Doctrine\Common\Annotations\CachedReader} instead.
|
||||
*/
|
||||
class FileCacheReader implements Reader
|
||||
{
|
||||
/**
|
||||
* @var Reader
|
||||
*/
|
||||
/** @var Reader */
|
||||
private $reader;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
/** @var string */
|
||||
private $dir;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
/** @var bool */
|
||||
private $debug;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $loadedAnnotations = array();
|
||||
/** @phpstan-var array<string, list<object>> */
|
||||
private $loadedAnnotations = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $classNameHashes = array();
|
||||
/** @var array<string, string> */
|
||||
private $classNameHashes = [];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
/** @var int */
|
||||
private $umask;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param string $cacheDir
|
||||
* @param bool $debug
|
||||
* @param int $umask
|
||||
*
|
||||
* @param Reader $reader
|
||||
* @param string $cacheDir
|
||||
* @param boolean $debug
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
* @throws InvalidArgumentException
|
||||
*/
|
||||
public function __construct(Reader $reader, $cacheDir, $debug = false, $umask = 0002)
|
||||
{
|
||||
if ( ! is_int($umask)) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
if (! is_int($umask)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'The parameter umask must be an integer, was: %s',
|
||||
gettype($umask)
|
||||
));
|
||||
}
|
||||
|
||||
$this->reader = $reader;
|
||||
$this->umask = $umask;
|
||||
$this->umask = $umask;
|
||||
|
||||
if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777 & (~$this->umask), true)) {
|
||||
throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist and could not be created.', $cacheDir));
|
||||
if (! is_dir($cacheDir) && ! @mkdir($cacheDir, 0777 & (~$this->umask), true)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
'The directory "%s" does not exist and could not be created.',
|
||||
$cacheDir
|
||||
));
|
||||
}
|
||||
|
||||
$this->dir = rtrim($cacheDir, '\\/');
|
||||
|
|
@ -93,31 +88,37 @@ class FileCacheReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getClassAnnotations(\ReflectionClass $class)
|
||||
public function getClassAnnotations(ReflectionClass $class)
|
||||
{
|
||||
if ( ! isset($this->classNameHashes[$class->name])) {
|
||||
if (! isset($this->classNameHashes[$class->name])) {
|
||||
$this->classNameHashes[$class->name] = sha1($class->name);
|
||||
}
|
||||
|
||||
$key = $this->classNameHashes[$class->name];
|
||||
|
||||
if (isset($this->loadedAnnotations[$key])) {
|
||||
return $this->loadedAnnotations[$key];
|
||||
}
|
||||
|
||||
$path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php';
|
||||
if (!is_file($path)) {
|
||||
$path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php';
|
||||
if (! is_file($path)) {
|
||||
$annot = $this->reader->getClassAnnotations($class);
|
||||
$this->saveCacheFile($path, $annot);
|
||||
|
||||
return $this->loadedAnnotations[$key] = $annot;
|
||||
}
|
||||
|
||||
if ($this->debug
|
||||
&& (false !== $filename = $class->getFileName())
|
||||
&& filemtime($path) < filemtime($filename)) {
|
||||
$filename = $class->getFilename();
|
||||
if (
|
||||
$this->debug
|
||||
&& $filename !== false
|
||||
&& filemtime($path) < filemtime($filename)
|
||||
) {
|
||||
@unlink($path);
|
||||
|
||||
$annot = $this->reader->getClassAnnotations($class);
|
||||
$this->saveCacheFile($path, $annot);
|
||||
|
||||
return $this->loadedAnnotations[$key] = $annot;
|
||||
}
|
||||
|
||||
|
|
@ -127,32 +128,38 @@ class FileCacheReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getPropertyAnnotations(\ReflectionProperty $property)
|
||||
public function getPropertyAnnotations(ReflectionProperty $property)
|
||||
{
|
||||
$class = $property->getDeclaringClass();
|
||||
if ( ! isset($this->classNameHashes[$class->name])) {
|
||||
if (! isset($this->classNameHashes[$class->name])) {
|
||||
$this->classNameHashes[$class->name] = sha1($class->name);
|
||||
}
|
||||
$key = $this->classNameHashes[$class->name].'$'.$property->getName();
|
||||
|
||||
$key = $this->classNameHashes[$class->name] . '$' . $property->getName();
|
||||
|
||||
if (isset($this->loadedAnnotations[$key])) {
|
||||
return $this->loadedAnnotations[$key];
|
||||
}
|
||||
|
||||
$path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php';
|
||||
if (!is_file($path)) {
|
||||
$path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php';
|
||||
if (! is_file($path)) {
|
||||
$annot = $this->reader->getPropertyAnnotations($property);
|
||||
$this->saveCacheFile($path, $annot);
|
||||
|
||||
return $this->loadedAnnotations[$key] = $annot;
|
||||
}
|
||||
|
||||
if ($this->debug
|
||||
&& (false !== $filename = $class->getFilename())
|
||||
&& filemtime($path) < filemtime($filename)) {
|
||||
$filename = $class->getFilename();
|
||||
if (
|
||||
$this->debug
|
||||
&& $filename !== false
|
||||
&& filemtime($path) < filemtime($filename)
|
||||
) {
|
||||
@unlink($path);
|
||||
|
||||
$annot = $this->reader->getPropertyAnnotations($property);
|
||||
$this->saveCacheFile($path, $annot);
|
||||
|
||||
return $this->loadedAnnotations[$key] = $annot;
|
||||
}
|
||||
|
||||
|
|
@ -162,32 +169,38 @@ class FileCacheReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getMethodAnnotations(\ReflectionMethod $method)
|
||||
public function getMethodAnnotations(ReflectionMethod $method)
|
||||
{
|
||||
$class = $method->getDeclaringClass();
|
||||
if ( ! isset($this->classNameHashes[$class->name])) {
|
||||
if (! isset($this->classNameHashes[$class->name])) {
|
||||
$this->classNameHashes[$class->name] = sha1($class->name);
|
||||
}
|
||||
$key = $this->classNameHashes[$class->name].'#'.$method->getName();
|
||||
|
||||
$key = $this->classNameHashes[$class->name] . '#' . $method->getName();
|
||||
|
||||
if (isset($this->loadedAnnotations[$key])) {
|
||||
return $this->loadedAnnotations[$key];
|
||||
}
|
||||
|
||||
$path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php';
|
||||
if (!is_file($path)) {
|
||||
$path = $this->dir . '/' . strtr($key, '\\', '-') . '.cache.php';
|
||||
if (! is_file($path)) {
|
||||
$annot = $this->reader->getMethodAnnotations($method);
|
||||
$this->saveCacheFile($path, $annot);
|
||||
|
||||
return $this->loadedAnnotations[$key] = $annot;
|
||||
}
|
||||
|
||||
if ($this->debug
|
||||
&& (false !== $filename = $class->getFilename())
|
||||
&& filemtime($path) < filemtime($filename)) {
|
||||
$filename = $class->getFilename();
|
||||
if (
|
||||
$this->debug
|
||||
&& $filename !== false
|
||||
&& filemtime($path) < filemtime($filename)
|
||||
) {
|
||||
@unlink($path);
|
||||
|
||||
$annot = $this->reader->getMethodAnnotations($method);
|
||||
$this->saveCacheFile($path, $annot);
|
||||
|
||||
return $this->loadedAnnotations[$key] = $annot;
|
||||
}
|
||||
|
||||
|
|
@ -204,36 +217,48 @@ class FileCacheReader implements Reader
|
|||
*/
|
||||
private function saveCacheFile($path, $data)
|
||||
{
|
||||
if (!is_writable($this->dir)) {
|
||||
throw new \InvalidArgumentException(sprintf('The directory "%s" is not writable. Both, the webserver and the console user need access. You can manage access rights for multiple users with "chmod +a". If your system does not support this, check out the acl package.', $this->dir));
|
||||
if (! is_writable($this->dir)) {
|
||||
throw new InvalidArgumentException(sprintf(
|
||||
<<<'EXCEPTION'
|
||||
The directory "%s" is not writable. Both the webserver and the console user need access.
|
||||
You can manage access rights for multiple users with "chmod +a".
|
||||
If your system does not support this, check out the acl package.,
|
||||
EXCEPTION
|
||||
,
|
||||
$this->dir
|
||||
));
|
||||
}
|
||||
|
||||
$tempfile = tempnam($this->dir, uniqid('', true));
|
||||
|
||||
if (false === $tempfile) {
|
||||
throw new \RuntimeException(sprintf('Unable to create tempfile in directory: %s', $this->dir));
|
||||
if ($tempfile === false) {
|
||||
throw new RuntimeException(sprintf('Unable to create tempfile in directory: %s', $this->dir));
|
||||
}
|
||||
|
||||
@chmod($tempfile, 0666 & (~$this->umask));
|
||||
|
||||
$written = file_put_contents($tempfile, '<?php return unserialize('.var_export(serialize($data), true).');');
|
||||
$written = file_put_contents(
|
||||
$tempfile,
|
||||
'<?php return unserialize(' . var_export(serialize($data), true) . ');'
|
||||
);
|
||||
|
||||
if (false === $written) {
|
||||
throw new \RuntimeException(sprintf('Unable to write cached file to: %s', $tempfile));
|
||||
if ($written === false) {
|
||||
throw new RuntimeException(sprintf('Unable to write cached file to: %s', $tempfile));
|
||||
}
|
||||
|
||||
@chmod($tempfile, 0666 & (~$this->umask));
|
||||
|
||||
if (false === rename($tempfile, $path)) {
|
||||
if (rename($tempfile, $path) === false) {
|
||||
@unlink($tempfile);
|
||||
throw new \RuntimeException(sprintf('Unable to rename %s to %s', $tempfile, $path));
|
||||
|
||||
throw new RuntimeException(sprintf('Unable to rename %s to %s', $tempfile, $path));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getClassAnnotation(\ReflectionClass $class, $annotationName)
|
||||
public function getClassAnnotation(ReflectionClass $class, $annotationName)
|
||||
{
|
||||
$annotations = $this->getClassAnnotations($class);
|
||||
|
||||
|
|
@ -249,7 +274,7 @@ class FileCacheReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getMethodAnnotation(\ReflectionMethod $method, $annotationName)
|
||||
public function getMethodAnnotation(ReflectionMethod $method, $annotationName)
|
||||
{
|
||||
$annotations = $this->getMethodAnnotations($method);
|
||||
|
||||
|
|
@ -265,7 +290,7 @@ class FileCacheReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName)
|
||||
public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
|
||||
{
|
||||
$annotations = $this->getPropertyAnnotations($property);
|
||||
|
||||
|
|
@ -285,6 +310,6 @@ class FileCacheReader implements Reader
|
|||
*/
|
||||
public function clearLoadedAnnotations()
|
||||
{
|
||||
$this->loadedAnnotations = array();
|
||||
$this->loadedAnnotations = [];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
171
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/ImplicitlyIgnoredAnnotationNames.php
vendored
Normal file
171
vendor/doctrine/annotations/lib/Doctrine/Common/Annotations/ImplicitlyIgnoredAnnotationNames.php
vendored
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Doctrine\Common\Annotations;
|
||||
|
||||
/**
|
||||
* A list of annotations that are implicitly ignored during the parsing process.
|
||||
*
|
||||
* All names are case sensitive.
|
||||
*/
|
||||
final class ImplicitlyIgnoredAnnotationNames
|
||||
{
|
||||
private const Reserved = [
|
||||
'Annotation' => true,
|
||||
'Attribute' => true,
|
||||
'Attributes' => true,
|
||||
/* Can we enable this? 'Enum' => true, */
|
||||
'Required' => true,
|
||||
'Target' => true,
|
||||
];
|
||||
|
||||
private const WidelyUsedNonStandard = [
|
||||
'fix' => true,
|
||||
'fixme' => true,
|
||||
'override' => true,
|
||||
];
|
||||
|
||||
private const PhpDocumentor1 = [
|
||||
'abstract' => true,
|
||||
'access' => true,
|
||||
'code' => true,
|
||||
'deprec' => true,
|
||||
'endcode' => true,
|
||||
'exception' => true,
|
||||
'final' => true,
|
||||
'ingroup' => true,
|
||||
'inheritdoc' => true,
|
||||
'inheritDoc' => true,
|
||||
'magic' => true,
|
||||
'name' => true,
|
||||
'private' => true,
|
||||
'static' => true,
|
||||
'staticvar' => true,
|
||||
'staticVar' => true,
|
||||
'toc' => true,
|
||||
'tutorial' => true,
|
||||
'throw' => true,
|
||||
];
|
||||
|
||||
private const PhpDocumentor2 = [
|
||||
'api' => true,
|
||||
'author' => true,
|
||||
'category' => true,
|
||||
'copyright' => true,
|
||||
'deprecated' => true,
|
||||
'example' => true,
|
||||
'filesource' => true,
|
||||
'global' => true,
|
||||
'ignore' => true,
|
||||
/* Can we enable this? 'index' => true, */
|
||||
'internal' => true,
|
||||
'license' => true,
|
||||
'link' => true,
|
||||
'method' => true,
|
||||
'package' => true,
|
||||
'param' => true,
|
||||
'property' => true,
|
||||
'property-read' => true,
|
||||
'property-write' => true,
|
||||
'return' => true,
|
||||
'see' => true,
|
||||
'since' => true,
|
||||
'source' => true,
|
||||
'subpackage' => true,
|
||||
'throws' => true,
|
||||
'todo' => true,
|
||||
'TODO' => true,
|
||||
'usedby' => true,
|
||||
'uses' => true,
|
||||
'var' => true,
|
||||
'version' => true,
|
||||
];
|
||||
|
||||
private const PHPUnit = [
|
||||
'author' => true,
|
||||
'after' => true,
|
||||
'afterClass' => true,
|
||||
'backupGlobals' => true,
|
||||
'backupStaticAttributes' => true,
|
||||
'before' => true,
|
||||
'beforeClass' => true,
|
||||
'codeCoverageIgnore' => true,
|
||||
'codeCoverageIgnoreStart' => true,
|
||||
'codeCoverageIgnoreEnd' => true,
|
||||
'covers' => true,
|
||||
'coversDefaultClass' => true,
|
||||
'coversNothing' => true,
|
||||
'dataProvider' => true,
|
||||
'depends' => true,
|
||||
'doesNotPerformAssertions' => true,
|
||||
'expectedException' => true,
|
||||
'expectedExceptionCode' => true,
|
||||
'expectedExceptionMessage' => true,
|
||||
'expectedExceptionMessageRegExp' => true,
|
||||
'group' => true,
|
||||
'large' => true,
|
||||
'medium' => true,
|
||||
'preserveGlobalState' => true,
|
||||
'requires' => true,
|
||||
'runTestsInSeparateProcesses' => true,
|
||||
'runInSeparateProcess' => true,
|
||||
'small' => true,
|
||||
'test' => true,
|
||||
'testdox' => true,
|
||||
'testWith' => true,
|
||||
'ticket' => true,
|
||||
'uses' => true,
|
||||
];
|
||||
|
||||
private const PhpCheckStyle = ['SuppressWarnings' => true];
|
||||
|
||||
private const PhpStorm = ['noinspection' => true];
|
||||
|
||||
private const PEAR = ['package_version' => true];
|
||||
|
||||
private const PlainUML = [
|
||||
'startuml' => true,
|
||||
'enduml' => true,
|
||||
];
|
||||
|
||||
private const Symfony = ['experimental' => true];
|
||||
|
||||
private const PhpCodeSniffer = [
|
||||
'codingStandardsIgnoreStart' => true,
|
||||
'codingStandardsIgnoreEnd' => true,
|
||||
];
|
||||
|
||||
private const SlevomatCodingStandard = ['phpcsSuppress' => true];
|
||||
|
||||
private const PhpStan = [
|
||||
'extends' => true,
|
||||
'implements' => true,
|
||||
'template' => true,
|
||||
'use' => true,
|
||||
];
|
||||
|
||||
private const Phan = ['suppress' => true];
|
||||
|
||||
private const Rector = ['noRector' => true];
|
||||
|
||||
public const LIST = self::Reserved
|
||||
+ self::WidelyUsedNonStandard
|
||||
+ self::PhpDocumentor1
|
||||
+ self::PhpDocumentor2
|
||||
+ self::PHPUnit
|
||||
+ self::PhpCheckStyle
|
||||
+ self::PhpStorm
|
||||
+ self::PEAR
|
||||
+ self::PlainUML
|
||||
+ self::Symfony
|
||||
+ self::SlevomatCodingStandard
|
||||
+ self::PhpCodeSniffer
|
||||
+ self::PhpStan
|
||||
+ self::Phan
|
||||
+ self::Rector;
|
||||
|
||||
private function __construct()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,41 +1,22 @@
|
|||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Annotations;
|
||||
|
||||
use ReflectionClass;
|
||||
use ReflectionMethod;
|
||||
use ReflectionProperty;
|
||||
|
||||
use function call_user_func_array;
|
||||
use function get_class;
|
||||
|
||||
/**
|
||||
* Allows the reader to be used in-place of Doctrine's reader.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*/
|
||||
class IndexedReader implements Reader
|
||||
{
|
||||
/**
|
||||
* @var Reader
|
||||
*/
|
||||
/** @var Reader */
|
||||
private $delegate;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Reader $reader
|
||||
*/
|
||||
public function __construct(Reader $reader)
|
||||
{
|
||||
$this->delegate = $reader;
|
||||
|
|
@ -44,9 +25,9 @@ class IndexedReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getClassAnnotations(\ReflectionClass $class)
|
||||
public function getClassAnnotations(ReflectionClass $class)
|
||||
{
|
||||
$annotations = array();
|
||||
$annotations = [];
|
||||
foreach ($this->delegate->getClassAnnotations($class) as $annot) {
|
||||
$annotations[get_class($annot)] = $annot;
|
||||
}
|
||||
|
|
@ -57,7 +38,7 @@ class IndexedReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getClassAnnotation(\ReflectionClass $class, $annotation)
|
||||
public function getClassAnnotation(ReflectionClass $class, $annotation)
|
||||
{
|
||||
return $this->delegate->getClassAnnotation($class, $annotation);
|
||||
}
|
||||
|
|
@ -65,9 +46,9 @@ class IndexedReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getMethodAnnotations(\ReflectionMethod $method)
|
||||
public function getMethodAnnotations(ReflectionMethod $method)
|
||||
{
|
||||
$annotations = array();
|
||||
$annotations = [];
|
||||
foreach ($this->delegate->getMethodAnnotations($method) as $annot) {
|
||||
$annotations[get_class($annot)] = $annot;
|
||||
}
|
||||
|
|
@ -78,7 +59,7 @@ class IndexedReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getMethodAnnotation(\ReflectionMethod $method, $annotation)
|
||||
public function getMethodAnnotation(ReflectionMethod $method, $annotation)
|
||||
{
|
||||
return $this->delegate->getMethodAnnotation($method, $annotation);
|
||||
}
|
||||
|
|
@ -86,9 +67,9 @@ class IndexedReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getPropertyAnnotations(\ReflectionProperty $property)
|
||||
public function getPropertyAnnotations(ReflectionProperty $property)
|
||||
{
|
||||
$annotations = array();
|
||||
$annotations = [];
|
||||
foreach ($this->delegate->getPropertyAnnotations($property) as $annot) {
|
||||
$annotations[get_class($annot)] = $annot;
|
||||
}
|
||||
|
|
@ -99,7 +80,7 @@ class IndexedReader implements Reader
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getPropertyAnnotation(\ReflectionProperty $property, $annotation)
|
||||
public function getPropertyAnnotation(ReflectionProperty $property, $annotation)
|
||||
{
|
||||
return $this->delegate->getPropertyAnnotation($property, $annotation);
|
||||
}
|
||||
|
|
@ -107,13 +88,13 @@ class IndexedReader implements Reader
|
|||
/**
|
||||
* Proxies all methods to the delegate.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $args
|
||||
* @param string $method
|
||||
* @param mixed[] $args
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
return call_user_func_array(array($this->delegate, $method), $args);
|
||||
return call_user_func_array([$this->delegate, $method], $args);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace Doctrine\Common\Annotations;
|
||||
|
||||
/**
|
||||
* Marker interface for PHP7/PHP8 compatible support
|
||||
* for named arguments (and constructor property promotion).
|
||||
*
|
||||
* @deprecated Implementing this interface is deprecated
|
||||
* Use the Annotation @NamedArgumentConstructor instead
|
||||
*/
|
||||
interface NamedArgumentConstructorAnnotation
|
||||
{
|
||||
}
|
||||
|
|
@ -1,85 +1,86 @@
|
|||
<?php
|
||||
/*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the MIT license. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\Common\Annotations;
|
||||
|
||||
use ReflectionClass;
|
||||
use ReflectionFunction;
|
||||
use SplFileObject;
|
||||
|
||||
use function is_file;
|
||||
use function method_exists;
|
||||
use function preg_quote;
|
||||
use function preg_replace;
|
||||
|
||||
/**
|
||||
* Parses a file for namespaces/use/class declarations.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Christian Kaps <christian.kaps@mohiva.com>
|
||||
*/
|
||||
final class PhpParser
|
||||
{
|
||||
/**
|
||||
* Parses a class.
|
||||
*
|
||||
* @param \ReflectionClass $class A <code>ReflectionClass</code> object.
|
||||
* @deprecated use parseUseStatements instead
|
||||
*
|
||||
* @return array A list with use statements in the form (Alias => FQN).
|
||||
* @param ReflectionClass $class A <code>ReflectionClass</code> object.
|
||||
*
|
||||
* @return array<string, class-string> A list with use statements in the form (Alias => FQN).
|
||||
*/
|
||||
public function parseClass(\ReflectionClass $class)
|
||||
public function parseClass(ReflectionClass $class)
|
||||
{
|
||||
if (method_exists($class, 'getUseStatements')) {
|
||||
return $class->getUseStatements();
|
||||
return $this->parseUseStatements($class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a class or function for use statements.
|
||||
*
|
||||
* @param ReflectionClass|ReflectionFunction $reflection
|
||||
*
|
||||
* @psalm-return array<string, string> a list with use statements in the form (Alias => FQN).
|
||||
*/
|
||||
public function parseUseStatements($reflection): array
|
||||
{
|
||||
if (method_exists($reflection, 'getUseStatements')) {
|
||||
return $reflection->getUseStatements();
|
||||
}
|
||||
|
||||
if (false === $filename = $class->getFileName()) {
|
||||
return array();
|
||||
$filename = $reflection->getFileName();
|
||||
|
||||
if ($filename === false) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$content = $this->getFileContent($filename, $class->getStartLine());
|
||||
$content = $this->getFileContent($filename, $reflection->getStartLine());
|
||||
|
||||
if (null === $content) {
|
||||
return array();
|
||||
if ($content === null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$namespace = preg_quote($class->getNamespaceName());
|
||||
$content = preg_replace('/^.*?(\bnamespace\s+' . $namespace . '\s*[;{].*)$/s', '\\1', $content);
|
||||
$namespace = preg_quote($reflection->getNamespaceName());
|
||||
$content = preg_replace('/^.*?(\bnamespace\s+' . $namespace . '\s*[;{].*)$/s', '\\1', $content);
|
||||
$tokenizer = new TokenParser('<?php ' . $content);
|
||||
|
||||
$statements = $tokenizer->parseUseStatements($class->getNamespaceName());
|
||||
|
||||
return $statements;
|
||||
return $tokenizer->parseUseStatements($reflection->getNamespaceName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the content of the file right up to the given line number.
|
||||
*
|
||||
* @param string $filename The name of the file to load.
|
||||
* @param integer $lineNumber The number of lines to read from file.
|
||||
* @param string $filename The name of the file to load.
|
||||
* @param int $lineNumber The number of lines to read from file.
|
||||
*
|
||||
* @return string|null The content of the file or null if the file does not exist.
|
||||
*/
|
||||
private function getFileContent($filename, $lineNumber)
|
||||
{
|
||||
if ( ! is_file($filename)) {
|
||||
if (! is_file($filename)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$content = '';
|
||||
$lineCnt = 0;
|
||||
$file = new SplFileObject($filename);
|
||||
while (!$file->eof()) {
|
||||
if ($lineCnt++ == $lineNumber) {
|
||||
$file = new SplFileObject($filename);
|
||||
while (! $file->eof()) {
|
||||
if ($lineCnt++ === $lineNumber) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue