├── CHANGELOG.md ├── LICENSE ├── README.md ├── blueprints.yaml ├── classes └── shortcodes │ └── PageInjectShortcode.php ├── composer.json ├── composer.lock ├── nextgen-editor └── plugins │ └── page-inject │ ├── page-inject.css │ └── page-inject.js ├── page-inject.php ├── page-inject.yaml └── vendor ├── autoload.php └── composer ├── ClassLoader.php ├── InstalledVersions.php ├── LICENSE ├── autoload_classmap.php ├── autoload_namespaces.php ├── autoload_psr4.php ├── autoload_real.php ├── autoload_static.php ├── installed.json ├── installed.php └── platform_check.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v2.1.6 2 | ## 03/22/2023 3 | 4 | 1. [](#bugfix) 5 | * Fixed NextGen integration where cog icon settings would not work properly 6 | 7 | # v2.1.5 8 | ## 03/28/2022 9 | 10 | 2. [](#bugfix) 11 | * Fixed an issue with relative paths for page-inject when processing 12 | 13 | # v2.1.4 14 | ## 02/08/2022 15 | 16 | 2. [](#bugfix) 17 | * Fixed bad configuration reference for `processed_content` 18 | * Set `processed_content` to false by default - so `content-inject` doesn't try to render with twig 19 | 20 | # v2.1.3 21 | ## 02/08/2022 22 | 23 | 1. [](#improved) 24 | * Shortcodes should use `rawHanders()` to support shortcodes/markdown 25 | 26 | # v2.1.2 27 | ## 02/08/2022 28 | 29 | 1. [](#improved) 30 | * PHPStan fixes 31 | * Support `./` in page paths 32 | 33 | # v2.1.1 34 | ## 12/09/2021 35 | 36 | 1. [](#improved) 37 | * Added a modular example in the README.md 38 | 2. [](#bugfix) 39 | * Allow null returned by `getInjectedPageContent()` 40 | 41 | # v2.1.0 42 | ## 12/06/2021 43 | 44 | 1. [](#new) 45 | * Added `[page-inject]` and `[content-inject]` shortcodes as an alternative syntax 46 | * Added support for remote `page-inject` and `content-inject` variations 47 | 2. [](#improved) 48 | * Refactored code to work with both markdown and shortcode syntax 49 | * Use composer-based autoloader 50 | 51 | # v2.0.0 52 | ## 12/03/2021 53 | 54 | 1. [](#new) 55 | * Added support for new remote injects. 56 | 57 | # v1.4.5 58 | ## 04/27/2021 59 | 60 | 1. [](#improved) 61 | * NextGen Editor: Added toolbar icon 62 | * NextGen Editor: Added support for multiple editor instances 63 | 1. [](#bugfix) 64 | * Fixed permissions to only require `pages.read` for `taskPageInject` [premium-issues#43](https://github.com/getgrav/grav-premium-issues/issues/43) 65 | 66 | # v1.4.4 67 | ## 01/29/2021 68 | 69 | 1. [](#bugfix) 70 | * NextGen Editor: Fixed Page Inject UI links missing the base_root [getgrav/grav-premium-issues#30](https://github.com/getgrav/grav-premium-issues/issues/30) 71 | * NextGen Editor: Moved list of available templates to input text to support partials and any twig template [getgrav/grav-premium-issues#24](https://github.com/getgrav/grav-premium-issues/issues/24) 72 | 73 | # v1.4.3 74 | ## 01/15/2021 75 | 76 | 1. [](#improved) 77 | * NextGen Editor: Updated upcast/downcast syntax to support latest version 78 | 79 | # v1.4.2 80 | ## 12/20/2020 81 | 82 | 1. [](#bugfix) 83 | * Fixed `undefined` value when inserting a new Page-Inject shortcode, preventing Page picker to load 84 | 85 | # v1.4.1 86 | ## 12/18/2020 87 | 88 | 1. [](#improved) 89 | * NextGen Editor: Properly restore the initial stored path when loading the Page Picker 90 | 91 | # v1.4.0 92 | ## 12/02/2020 93 | 94 | 1. [](#new) 95 | * NEW support for NextGen Editor 96 | * Added a new `taskPageInjectData` to be used by NextGen Editor integration 97 | 1. [](#bugfix) 98 | * Added missing admin nonce 99 | 100 | # v1.3.1 101 | ## 04/15/2019 102 | 103 | 1. [](#bugfix) 104 | * Fixed issue with Feed plugin and Page-Inject by forcing template to `html` [feed#42](https://github.com/getgrav/grav-plugin-feed/issues/42) 105 | 106 | # v1.3.0 107 | ## 12/08/2017 108 | 109 | 1. [](#new) 110 | * Added multi-lang support to Page Inject plugin [#10](https://github.com/getgrav/grav-plugin-page-inject/issues/10) 111 | 112 | # v1.2.0 113 | ## 10/11/2016 114 | 115 | 1. [](#improved) 116 | * Support Grav-style link route resolution (e.g. `../your-route`) [#5](https://github.com/getgrav/grav-plugin-page-inject/issues/5) 117 | 1. [](#bugfix) 118 | * Fixed issue with `page-inject` processing Twig twice [#7](https://github.com/getgrav/grav-plugin-page-inject/issues/7) 119 | 120 | # v1.1.1 121 | ## 10/21/2015 122 | 123 | 1. [](#new) 124 | * Added `active` config option to enable/disable site-wide 125 | 1. [](#bugfix) 126 | * Fixed issue with plugin not processing reliably with cache-enabled 127 | 128 | # v1.1.0 129 | ## 08/25/2015 130 | 131 | 1. [](#improved) 132 | * Added blueprints for Grav Admin plugin 133 | 134 | # v1.0.0 135 | ## 06/18/2015 136 | 137 | 1. [](#new) 138 | * ChangeLog started... 139 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Grav 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Grav Page Inject Plugin 2 | 3 | `Page Inject` is a powerful [Grav][grav] Plugin that lets you inject entire pages or page content into other pages using simple markdown-style syntax or alternatively a shortcode syntax (Shortcode Core plugin required). 4 | 5 | # Installation 6 | 7 | Installing the Page Inject plugin can be done in one of two ways. Our GPM (Grav Package Manager) installation method enables you to quickly and easily install the plugin with a simple terminal command, while the manual method enables you to do so via a zip file. 8 | 9 | ## GPM Installation (Preferred) 10 | 11 | The simplest way to install this plugin is via the [Grav Package Manager (GPM)](http://learn.getgrav.org/advanced/grav-gpm) through your system's Terminal (also called the command line). From the root of your Grav install type: 12 | 13 | bin/gpm install page-inject 14 | 15 | This will install the Page Inject plugin into your `/user/plugins` directory within Grav. Its files can be found under `/your/site/grav/user/plugins/page-inject`. 16 | 17 | ## Manual Installation 18 | 19 | To install this plugin, just download the zip version of this repository and unzip it under `/your/site/grav/user/plugins`. Then, rename the folder to `page-inject`. You can find these files either on [GitHub](https://github.com/getgrav/grav-plugin-page-inject) or via [GetGrav.org](http://getgrav.org/downloads/plugins#extras). 20 | 21 | You should now have all the plugin files under 22 | 23 | /your/site/grav/user/plugins/page-inject 24 | 25 | # Config Defaults 26 | 27 | ``` 28 | enabled: true 29 | active: true 30 | processed_content: true 31 | remote_injections: 32 | ``` 33 | 34 | If you need to change any value, then the best process is to copy the [page-inject.yaml](page-inject.yaml) file into your `users/config/plugins/` folder (create it if it doesn't exist), and then modify there. This will override the default settings. 35 | 36 | The `active` option allows you to enable/disable the plugin site-wide, and then enable it on page via Page Config overrides. This is useful to optimize performance. 37 | 38 | the `processed_content` option means the page is pre-rendered before being injected. This is the default behavior and means that relative image links and other path-sensitive content works correctly. You can however set this to `false` and then the raw markdown is inject and processed along with the rest of the current page. This is relevant for `content-inject` links **only**. 39 | 40 | ### Page Config 41 | 42 | You can override the plugin options by adding overrides in the page header frontmatter: 43 | 44 | ``` 45 | page-inject: 46 | active: true 47 | processed_content: true 48 | ``` 49 | 50 | ## Markdown-Style Usage (Legacy) 51 | 52 | There are two ways to use this plugin in your markdown content: 53 | 54 | 1. **Page Injection** 55 | 56 | ``` 57 | [plugin:page-inject](/route/to/page) 58 | ``` 59 | 60 | This approach includes an entire page rendered with the associated template. This works best for modular page content or content that uses a specific template that provides appropriate styling that is intended to be part of other pages. You can also pass an optional template name and use that template to render the page (as long as you also provide the template in your theme): 61 | 62 | ``` 63 | [plugin:page-inject](/route/to/page?template=custom-template) 64 | ``` 65 | 66 | 2. **Content Injection** 67 | 68 | ``` 69 | [plugin:content-inject](/route/to/page) 70 | ``` 71 | 72 | Sometimes you just want the content of another page injected directly into your current page. Use `content-inject` for this purpose. The content is not rendered with the associated twig template, merely injected into the current page. 73 | 74 | ## Shortcode Usage (New) 75 | 76 | The shortcode syntax follows the markdown-style syntax, and supports both `page-inject` and `content-inject` approaches. 77 | 78 | > NOTE: Shortcode functionality requires the `shortcode-core` plugin to be installed and enabled. 79 | 80 | For example the Page Injection example from above would look like: 81 | 82 | ```markdown 83 | [page-inject=/route/to/page /] 84 | ``` 85 | 86 | and the content inject version: 87 | 88 | ```markdown 89 | [content-inject=/route/to/page /] 90 | ``` 91 | 92 | Alternatively, you can supply the path explicitly: 93 | 94 | ```markdown 95 | [content-inject path="/route/to/page" /] 96 | ``` 97 | 98 | And for page-injection, you can specify a custom Twig template to render with: 99 | 100 | ```markdown 101 | [page-inject path="/route/to/page" template="foo" /] 102 | ``` 103 | 104 | ## Modular Pages 105 | 106 | One of the most useful scenarios for using Page Inject plugin is for pulling modular pages into your content. This is because it allows you to sprinkle structure, pre-rendered HTML into the middle of your content. This allows you to create complex content layouts with a combination of straight markdown content, with blocks of more structured output. For example imagine being able to display a "Contact Us" form in the middle of a case study. Or perhaps a customer quotes module in the middle of a long article about your customer success stories. A quick example of this might be: 107 | 108 | ```markdown 109 | [plugin:page-inject](/modular/_callout) 110 | ``` 111 | 112 | The path will be the **Page route** for the page, and modular pages are typically distinguished by the `_` prefix. You would typically want to use a **page-inject** for this as you want the modular page pre-rendered with the associated Twig template. You could still just display the content with **content-inject**. 113 | 114 | ## Remote Injects 115 | 116 | It is now possible to retrieve remote content from another Grav instance as long as both of the sites are running the latest version of the `page-inject` plugin. First in the **client** Grav instance you need to define a remote connection to another Grav **server** in the plugin configuration. For example: 117 | 118 | ```yaml 119 | remote_injections: 120 | dev: https://dev.somehost.com/ 121 | foo: https://foo.com/bar 122 | ``` 123 | 124 | This will then allow you to inject page content from one Grav instance to another using this syntax: 125 | 126 | ```markdown 127 | [plugin:page-inject](remote://dev/home/modular/_callout) 128 | ``` 129 | 130 | and for the shortcode version: 131 | 132 | ```markdown 133 | [page-inject path="remote://dev/home/modular/_callout" /] 134 | ``` 135 | 136 | Where the `remote://dev` protocol tells the plugin to retrieve the requested page from the `dev` injection configuration via the path `/home/modular/_callout`. 137 | 138 | This is particularly useful for modular content that is already a snippet of content that is being reused on the **server**. This will retrieve the content, and because a modular page's content is pre-rendered with the appropriate Twig template, it will include all the HTML of the modular page. If you request a regular page (non-modular), there will be no Twig and just plain HTML content will be sent. 139 | 140 | [grav]: http://github.com/getgrav/grav 141 | -------------------------------------------------------------------------------- /blueprints.yaml: -------------------------------------------------------------------------------- 1 | name: Page Inject 2 | type: plugin 3 | slug: page-inject 4 | version: 2.1.6 5 | description: "**Page Inject** is a powerful plugin that lets you inject entire pages or page content into other pages using simple markdown-style or shortcode syntax" 6 | icon: trello 7 | author: 8 | name: Team Grav 9 | email: devs@getgrav.org 10 | url: http://getgrav.org 11 | homepage: https://github.com/getgrav/grav-plugin-page-inject 12 | keywords: inject, embed, markdown 13 | bugs: https://github.com/getgrav/grav-plugin-page-inject/issues 14 | license: MIT 15 | dependencies: 16 | - { name: grav, version: '>=1.7.25' } 17 | 18 | form: 19 | validation: strict 20 | fields: 21 | enabled: 22 | type: toggle 23 | label: Plugin status 24 | highlight: 1 25 | default: 0 26 | options: 27 | 1: Enabled 28 | 0: Disabled 29 | validate: 30 | type: bool 31 | 32 | active: 33 | type: toggle 34 | label: Activate Site-Wide 35 | highlight: 1 36 | default: 1 37 | options: 38 | 1: Enabled 39 | 0: Disabled 40 | validate: 41 | type: bool 42 | help: This will ensure the plugin is activated site wide. You can override with page header options. 43 | 44 | processed_content: 45 | type: toggle 46 | label: Processed Content 47 | highlight: 0 48 | default: 0 49 | options: 50 | 1: Enabled 51 | 0: Disabled 52 | validate: 53 | type: bool 54 | help: If enabled the page is pre-rendered before being injected, so relative paths work correctly 55 | 56 | remote_injections: 57 | type: array 58 | label: Remote Injections 59 | help: Key should be a short slug, e.g. "dev", and the full URL should be "https://foo.com/path" 60 | placeholder_key: Short slug 61 | placeholder_value: Full Url 62 | 63 | -------------------------------------------------------------------------------- /classes/shortcodes/PageInjectShortcode.php: -------------------------------------------------------------------------------- 1 | shortcode->getRawHandlers()->add('page-inject', function(ShortcodeInterface $sc) { 13 | $path = $sc->getParameter('path') ?? $sc->getBbCode(); 14 | return PageInjectPlugin::getInjectedPageContent('page-inject', $path, $this->shortcode->getPage()); 15 | }); 16 | 17 | $this->shortcode->getRawHandlers()->add('content-inject', function(ShortcodeInterface $sc) { 18 | $path = $sc->getParameter('path') ?? $sc->getBbCode(); 19 | return PageInjectPlugin::getInjectedPageContent('content-inject', $path, $this->shortcode->getPage()); 20 | }); 21 | } 22 | } -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "getgrav/page-inject", 3 | "type": "grav-plugin", 4 | "description": "Page & Content Injection for Grav", 5 | "keywords": ["shortcode"], 6 | "homepage": "https://github.com/getgrav/grav-plugin-content-inject/", 7 | "license": "MIT", 8 | "authors": [ 9 | { 10 | "name": "Team Grav", 11 | "email": "devs@getgrav.org", 12 | "homepage": "http://getgrav.org", 13 | "role": "Developer" 14 | } 15 | ], 16 | "require": { 17 | "php": ">=7.1.3" 18 | }, 19 | "autoload": { 20 | "psr-4": { 21 | "Grav\\Plugin\\PageInject\\": "classes" 22 | }, 23 | "classmap": ["page-inject.php"] 24 | }, 25 | "config": { 26 | "platform": { 27 | "php": "7.1.3" 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "8561525a75814aec966c4ad64c77c613", 8 | "packages": [], 9 | "packages-dev": [], 10 | "aliases": [], 11 | "minimum-stability": "stable", 12 | "stability-flags": [], 13 | "prefer-stable": false, 14 | "prefer-lowest": false, 15 | "platform": { 16 | "php": ">=7.1.3" 17 | }, 18 | "platform-dev": [], 19 | "platform-overrides": { 20 | "php": "7.1.3" 21 | }, 22 | "plugin-api-version": "2.1.0" 23 | } 24 | -------------------------------------------------------------------------------- /nextgen-editor/plugins/page-inject/page-inject.css: -------------------------------------------------------------------------------- 1 | page-inject { 2 | border: 1px solid #ccc; 3 | display: inline-flex; 4 | } 5 | 6 | page-inject .pi-wrapper { 7 | align-items: center; 8 | display: inline-flex; 9 | margin: 8px 16px; 10 | } 11 | 12 | page-inject .pi-wrapper > * { 13 | margin-left: 16px; 14 | } 15 | 16 | page-inject .pi-wrapper > :first-child { 17 | margin-left: 0; 18 | } 19 | 20 | page-inject .pi-wrapper > *:before { 21 | margin-right: 16px; 22 | content: '|'; 23 | } 24 | 25 | page-inject .pi-wrapper > *:first-child:before { 26 | display: none; 27 | } 28 | 29 | page-inject .pi-type { 30 | font-weight: 700; 31 | } 32 | 33 | page-inject .pi-route-settings { 34 | cursor: pointer; 35 | height: 24px; 36 | margin-left: 4px; 37 | margin-top: -2px; 38 | transition: opacity .2s ease, color .2s ease; 39 | width: 24px; 40 | } 41 | 42 | page-inject:hover .pi-route-settings { 43 | color: #ffc83d; 44 | } 45 | 46 | page-inject .pi-route-settings:hover, 47 | page-inject.ck-widget_selected .pi-route-settings { 48 | color: #1f89e5; 49 | } 50 | 51 | page-inject.ck-widget_selected .pi-route-settings:hover { 52 | opacity: .5; 53 | } 54 | 55 | page-inject .pi-settings { 56 | align-items: center; 57 | border-left: 1px solid #ccc; 58 | display: inline-flex; 59 | padding: 0 8px; 60 | } 61 | 62 | page-inject .pi-settings svg { 63 | cursor: pointer; 64 | height: 24px; 65 | margin-left: auto; 66 | transition: opacity .2s ease, color .2s ease; 67 | width: 24px; 68 | } 69 | 70 | page-inject:hover .pi-settings svg { 71 | color: #ffc83d; 72 | } 73 | 74 | page-inject .pi-settings svg:hover, 75 | page-inject.ck-widget_selected .pi-settings svg { 76 | color: #1f89e5; 77 | } 78 | 79 | page-inject.ck-widget_selected .pi-settings svg:hover { 80 | opacity: .5; 81 | } 82 | -------------------------------------------------------------------------------- /nextgen-editor/plugins/page-inject/page-inject.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | let availableTemplates = {}; 4 | 5 | const Widget = window.nextgenEditor.classes.widget.class; 6 | const Command = window.nextgenEditor.classes.core.command.class; 7 | const { toWidget } = window.nextgenEditor.classes.widget.utils; 8 | const { showPagePicker, showSettingsPopup } = window.nextgenEditor.exports; 9 | 10 | const itemTypes = { 11 | page: 'Page Injection', 12 | content: 'Content Injection', 13 | }; 14 | 15 | window.nextgenEditor.addHook('hookInit', () => { 16 | window.nextgenEditor.addButtonGroup('page-inject', { 17 | label: 'Page Inject', 18 | icon: `` 19 | }); 20 | 21 | window.nextgenEditor.addButton('page-inject-page', { 22 | group: 'page-inject', 23 | command: { name: 'page-inject', params: { type: 'page', value: '' } }, 24 | label: 'Page Injection', 25 | }); 26 | 27 | window.nextgenEditor.addButton('page-inject-content', { 28 | group: 'page-inject', 29 | command: { name: 'page-inject', params: { type: 'content', value: '' } }, 30 | label: 'Content Injection', 31 | }); 32 | }); 33 | 34 | function uncollapse(input) { 35 | const domOutput = new DOMParser().parseFromString(input, 'text/html'); 36 | 37 | let innerHTML = ''; 38 | 39 | const attributes = [...domOutput.body.firstChild.attributes] 40 | .reduce((acc, attribute) => ({ ...acc, [attribute.name]: attribute.value }), {}); 41 | 42 | /* eslint-disable indent, no-multi-spaces */ 43 | innerHTML += ''; 44 | innerHTML += `${itemTypes[attributes.type] || ''}`; 45 | innerHTML += `${attributes.title || ''}`; 46 | innerHTML += `${attributes.route || ''}`; 47 | innerHTML += ''; 48 | innerHTML += ''; 49 | innerHTML += ''; 50 | 51 | if (attributes.type === 'page') { 52 | const templateValue = attributes.template 53 | ? availableTemplates[attributes.template] 54 | ? `${availableTemplates[attributes.template]} template` 55 | : `${attributes.template} template` 56 | : 'No template selected'; 57 | 58 | innerHTML += `${templateValue}`; 59 | } 60 | 61 | innerHTML += ''; 62 | innerHTML += ''; 63 | innerHTML += ''; 64 | innerHTML += ''; 65 | innerHTML += ''; 66 | innerHTML += ''; 67 | /* eslint-enable indent, no-multi-spaces */ 68 | 69 | domOutput.body.firstChild.innerHTML = innerHTML; 70 | 71 | return domOutput.body.innerHTML; 72 | } 73 | 74 | window.nextgenEditor.addHook('hookMarkdowntoHTML', { 75 | weight: -50, 76 | async handler(options, input) { 77 | let output = input; 78 | 79 | const items = [...output.matchAll(/\[plugin:(?page|content)-inject\]\((?[^?)]+)(?\?[^)]*)?\)/g)]; 80 | 81 | const body = new FormData(); 82 | const reqUrl = `${window.GravAdmin.config.base_url_relative}/task:pageInjectData`; 83 | 84 | body.append('admin-nonce', window.GravAdmin.config.admin_nonce); 85 | items.forEach((matches) => { 86 | body.append('routes[]', matches.groups.route); 87 | }); 88 | 89 | if (!items.length) { 90 | body.append('routes[]', 'not_exists_route'); 91 | } 92 | 93 | const resp = await fetch(reqUrl, { body, method: 'POST' }) 94 | .then((resp) => (resp.ok ? resp.json() : null)) 95 | .then((resp) => (resp && resp.status !== 'error' ? resp : {})); 96 | 97 | availableTemplates = resp.available_templates; 98 | 99 | const pages = resp.data 100 | .filter((page) => page.status === 'success') 101 | .reduce((acc, page) => ({ ...acc, [page.data.route]: page.data }), {}); 102 | 103 | items.forEach((matches) => { 104 | const { type, route, query } = matches.groups; 105 | const template = new URLSearchParams(query).get('template') || ''; 106 | const title = (pages[route] && pages[route].title) || ''; 107 | 108 | output = output.replace(matches[0], `[[page-inject type="${type}" title="${title}" route="${route}" template="${template}"]]`); 109 | }); 110 | 111 | return output; 112 | }, 113 | }); 114 | 115 | window.nextgenEditor.addHook('hookMarkdowntoHTML', { 116 | weight: 50, 117 | handler(options, input) { 118 | let output = input; 119 | 120 | output = output.replace(/(

)?(\[\[page-inject(?[^\]]*)\]\])(<\/p>)?/g, (...matches) => { 121 | const { attributes } = matches.pop(); 122 | return uncollapse(``); 123 | }); 124 | 125 | return output; 126 | }, 127 | }); 128 | 129 | window.nextgenEditor.addHook('hookHTMLtoMarkdown', { 130 | weight: -50, 131 | handler(options, editor, input) { 132 | let output = input; 133 | 134 | output = output.replace(/]*>(((?!(<\/page-inject>)).)|\n)*<\/page-inject>/g, (...matches) => { 135 | const domPageInject = new DOMParser().parseFromString(matches[0], 'text/html').body.firstChild; 136 | 137 | const type = domPageInject.getAttribute('type'); 138 | const route = domPageInject.getAttribute('route'); 139 | const template = domPageInject.getAttribute('template'); 140 | 141 | const queryString = template 142 | ? `?template=${template}` 143 | : ''; 144 | 145 | return `[plugin:${type}-inject](${route}${queryString})`; 146 | }); 147 | 148 | return output; 149 | }, 150 | }); 151 | 152 | class GravPageInjectCommand extends Command { 153 | execute(params) { 154 | showPagePicker(params.value, (page) => { 155 | const dataPageInject = uncollapse(``); 156 | const viewPageInject = this.editor.data.processor.toView(dataPageInject).getChild(0); 157 | const modelPageInject = this.editor.data.toModel(viewPageInject).getChild(0); 158 | 159 | const selectedBlocks = [...this.editor.model.document.selection.getSelectedBlocks()]; 160 | const lastBlock = selectedBlocks[selectedBlocks.length - 1]; 161 | 162 | this.editor.model.change((modelWriter) => { 163 | let insertPosition = modelWriter.createPositionAfter(lastBlock); 164 | 165 | if (lastBlock && lastBlock.name === 'paragraph' && lastBlock.childCount === 0) { 166 | insertPosition = modelWriter.createPositionBefore(lastBlock); 167 | modelWriter.remove(lastBlock); 168 | } 169 | 170 | modelWriter.insert(modelPageInject, insertPosition); 171 | modelWriter.setSelection(modelPageInject, 'on'); 172 | }); 173 | }); 174 | } 175 | } 176 | 177 | window.nextgenEditor.addPlugin('GravPageInject', { 178 | requires: [Widget], 179 | init() { 180 | this.editor.commands.add('page-inject', new GravPageInjectCommand(this.editor)); 181 | 182 | this.editor.model.schema.register('page-inject', { 183 | isObject: true, 184 | isInline: true, 185 | allowWhere: '$text', 186 | allowContentOf: '$block', 187 | allowAttributes: [ 188 | 'type', 189 | 'title', 190 | 'route', 191 | 'template', 192 | ], 193 | }); 194 | 195 | this.editor.conversion.for('upcast').elementToElement({ 196 | view: 'page-inject', 197 | model(viewElement, { writer }) { 198 | return writer.createElement('page-inject', viewElement.getAttributes()); 199 | }, 200 | }); 201 | 202 | this.editor.conversion.for('dataDowncast').elementToElement({ 203 | model: 'page-inject', 204 | view(modelElement, { writer }) { 205 | return writer.createContainerElement('page-inject', modelElement.getAttributes()); 206 | }, 207 | }); 208 | 209 | this.editor.conversion.for('editingDowncast').elementToElement({ 210 | model: 'page-inject', 211 | view(modelElement, { writer }) { 212 | const container = writer.createContainerElement('page-inject', modelElement.getAttributes()); 213 | return toWidget(container, writer); 214 | }, 215 | }); 216 | }, 217 | }); 218 | 219 | window.pageInjectRouteSettings = function pageInjectRouteSettings() { 220 | const { editors } = window.nextgenEditor; 221 | 222 | const domPageInject = this.closest('page-inject'); 223 | const editor = (editors.filter((instance) => instance.ui.view.element.contains(domPageInject)) || []).shift(); 224 | 225 | if (editor) { 226 | const viewPageInject = editor.editing.view.domConverter.mapDomToView(domPageInject); 227 | const modelPageInject = editor.editing.mapper.toModelElement(viewPageInject); 228 | const route = modelPageInject.getAttribute('route'); 229 | 230 | showPagePicker(route, (page) => { 231 | if (page.value === route) { 232 | return; 233 | } 234 | 235 | editor.model.change((modelWriter) => { 236 | const attributes = [...modelPageInject.getAttributes()] 237 | .reduce((acc, pair) => ({ ...acc, [pair.shift()]: pair.pop() }), {}); 238 | 239 | const dataNewPageInject = uncollapse(``); 240 | const viewNewPageInject = editor.data.processor.toView(dataNewPageInject).getChild(0); 241 | const modelNewPageInject = editor.data.toModel(viewNewPageInject, '$block').getChild(0); 242 | const insertPosition = modelWriter.createPositionBefore(modelPageInject); 243 | 244 | modelWriter.remove(modelPageInject); 245 | modelWriter.insert(modelNewPageInject, insertPosition); 246 | modelWriter.setSelection(modelNewPageInject, 'on'); 247 | }); 248 | }); 249 | } 250 | }; 251 | 252 | window.pageInjectSettings = function pageInjectSettings() { 253 | const { editors } = window.nextgenEditor; 254 | 255 | const domPageInject = this.closest('page-inject'); 256 | const editor = (editors.filter((instance) => instance.ui.view.element.contains(domPageInject)) || []).shift(); 257 | 258 | if (editor) { 259 | const viewPageInject = editor.editing.view.domConverter.mapDomToView(domPageInject); 260 | let modelPageInject = editor.editing.mapper.toModelElement(viewPageInject); 261 | 262 | const currentAttributes = [...modelPageInject.getAttributes()] 263 | .reduce((acc, pair) => ({...acc, [pair.shift()]: pair.pop()}), {}); 264 | 265 | const attributes = { 266 | type: { 267 | title: 'Type', 268 | widget: { 269 | type: 'select', 270 | values: Object.keys(itemTypes).map((value) => ({value, label: itemTypes[value]})), 271 | }, 272 | }, 273 | template: { 274 | title: 'Template', 275 | widget: { 276 | type: 'input-text', 277 | // type: 'select', 278 | // values: [ 279 | // { value: '', label: '' }, 280 | // ...Object.keys(availableTemplates).map((value) => ({ value, label: availableTemplates[value] })), 281 | // ], 282 | visible: ({attributes}) => attributes.type === 'page', 283 | }, 284 | }, 285 | }; 286 | 287 | const argsForPopup = { 288 | title: 'Page Inject', 289 | domDisplayPoint: this, 290 | debounceDelay: 1000, 291 | attributes, 292 | currentAttributes, 293 | }; 294 | 295 | argsForPopup.deleteItem = () => { 296 | editor.model.change((modelWriter) => modelWriter.remove(modelPageInject)); 297 | }; 298 | 299 | argsForPopup.changeAttributes = (changeCallback) => { 300 | editor.model.change((modelWriter) => { 301 | const dataNewPageInject = uncollapse(``); 302 | const viewNewPageInject = editor.data.processor.toView(dataNewPageInject).getChild(0); 303 | const modelNewPageInject = editor.data.toModel(viewNewPageInject, '$block').getChild(0); 304 | const insertPosition = modelWriter.createPositionBefore(modelPageInject); 305 | 306 | modelWriter.remove(modelPageInject); 307 | modelWriter.insert(modelNewPageInject, insertPosition); 308 | modelWriter.setSelection(modelNewPageInject, 'on'); 309 | 310 | modelPageInject = modelNewPageInject; 311 | }); 312 | 313 | if (currentAttributes.type !== 'page' && currentAttributes.template) { 314 | currentAttributes.template = ''; 315 | changeCallback(); 316 | } 317 | }; 318 | 319 | showSettingsPopup(argsForPopup); 320 | } 321 | }; 322 | 323 | document.addEventListener('click', (event) => { 324 | const target = event.target; 325 | 326 | if (target.classList.contains('pi-route-settings') || target.closest('.pi-route-settings')) { 327 | event.preventDefault(); 328 | window.pageInjectRouteSettings.call(target); 329 | } 330 | 331 | if (target.classList.contains('pi-settings') || target.closest('.pi-settings')) { 332 | event.preventDefault(); 333 | window.pageInjectSettings.call(target) 334 | } 335 | }); 336 | 337 | })(); 338 | -------------------------------------------------------------------------------- /page-inject.php: -------------------------------------------------------------------------------- 1 | ['method_name', priority]. 31 | */ 32 | public static function getSubscribedEvents() 33 | { 34 | return [ 35 | 'onPluginsInitialized' => ['onPluginsInitialized', 0], 36 | 'registerNextGenEditorPlugin' => ['registerNextGenEditorPlugin', 0], 37 | ]; 38 | } 39 | 40 | /** 41 | * Initialize configuration. 42 | */ 43 | public function onPluginsInitialized() 44 | { 45 | if ($this->isAdmin()) { 46 | $this->enable([ 47 | 'onAdminTaskExecute' => ['onAdminTaskExecute', 0], 48 | ]); 49 | return; 50 | } 51 | 52 | $this->enable([ 53 | 'onPageContentRaw' => ['onPageContentRaw', 0], 54 | 'onPagesInitialized' => ['onPagesInitialized', 0], 55 | 'onShortcodeHandlers' => ['onShortcodeHandlers', 0], 56 | ]); 57 | } 58 | 59 | /** 60 | * 61 | * @param Event $e 62 | */ 63 | public function onAdminTaskExecute(Event $e): void 64 | { 65 | if ($e['method'] === 'taskPageInjectData') { 66 | header('Content-type: application/json'); 67 | header('Cache-Control: no-cache, no-store, must-revalidate'); 68 | $controller = $e['controller']; 69 | 70 | if (!$controller->authorizeTask('pageInject', ['admin.pages.read', 'admin.super'])) { 71 | http_response_code(401); 72 | $json_response = [ 73 | 'status' => 'error', 74 | 'message' => ' Unable to get PageInject data', 75 | 'details' => 'Insufficient permissions for this user.' 76 | ]; 77 | echo json_encode($json_response); 78 | exit; 79 | } 80 | 81 | error_reporting(1); 82 | set_time_limit(0); 83 | 84 | $json_response = $this->getPageInjectData(); 85 | 86 | echo json_encode($json_response); 87 | exit; 88 | } 89 | } 90 | 91 | public function onShortcodeHandlers() 92 | { 93 | $this->grav['shortcode']->registerAllShortcodes(__DIR__ . '/classes/shortcodes'); 94 | } 95 | 96 | /** 97 | * Add content after page content was read into the system. 98 | * 99 | * @param Event $event An event object, when `onPageContentRaw` is fired. 100 | */ 101 | public function onPageContentRaw(Event $event) 102 | { 103 | /** @var Page $page */ 104 | $page = $event['page']; 105 | 106 | /** @var Config $config */ 107 | $config = $this->mergeConfig($page); 108 | 109 | if ($config->get('enabled') && $config->get('active')) { 110 | // Get raw content and substitute all formulas by a unique token 111 | $raw = $page->getRawContent(); 112 | 113 | // build an anonymous function to pass to `parseLinks()` 114 | $function = function ($matches) use (&$page, &$config) { 115 | 116 | $search = $matches[0]; 117 | $type = $matches[1]; 118 | $page_path = $matches[2]; 119 | 120 | preg_match('#remote://(.*?)/(.*)#', $page_path, $remote_matches); 121 | 122 | if (isset($remote_matches[1]) && $remote_matches[2]) { 123 | $remote_injections = $config->get('remote_injections', []); 124 | $remote_url = $remote_injections[$remote_matches[1]] ?? null; 125 | if ($remote_url) { 126 | $url = $remote_url . '/?action=' . $type . '&path=/' . urlencode($remote_matches[2]); 127 | return \Grav\Common\HTTP\Response::get($url); 128 | } 129 | } else { 130 | $replace = $this->getInjectedPageContent($type, $page_path, $page, $config->get('processed_content')); 131 | if ($replace) { 132 | return str_replace($search, $replace, $search); 133 | } 134 | } 135 | return $search; 136 | }; 137 | 138 | // set the parsed content back into as raw content 139 | $processed_content = $this->parseInjectLinks($raw, $function); 140 | $page->setRawContent($processed_content); 141 | } 142 | } 143 | 144 | public function onPagesInitialized() 145 | { 146 | $uri = $this->grav['uri']; 147 | $type = $uri->query('action'); 148 | $path = $uri->query('path'); 149 | // Handle remote calls 150 | if (in_array($type, ['page-inject','content-inject']) && isset($path)) { 151 | echo $this->getInjectedPageContent($type, $path); 152 | exit; 153 | 154 | } 155 | } 156 | 157 | public function registerNextGenEditorPlugin($event) { 158 | $plugins = $event['plugins']; 159 | 160 | // page-inject 161 | $plugins['css'][] = 'plugin://page-inject/nextgen-editor/plugins/page-inject/page-inject.css'; 162 | $plugins['js'][] = 'plugin://page-inject/nextgen-editor/plugins/page-inject/page-inject.js'; 163 | 164 | $event['plugins'] = $plugins; 165 | return $event; 166 | } 167 | 168 | public static function getInjectedPageContent($type, $path, $page = null, $processed_content = null): ?string 169 | { 170 | $pages = Grav::instance()['pages']; 171 | $page = $page ?? Grav::instance()['page']; 172 | 173 | if (is_null($processed_content)) { 174 | $header = new Data((array) $page->header()); 175 | $processed_content = $header->get('page-inject.processed_content') ?? Grav::instance()['config']->get('plugins.page-inject.processed_content', true); 176 | } 177 | preg_match('/(.*)\?template=(.*)|(.*)/i', $path, $template_matches); 178 | 179 | $path = $template_matches[1] && $template_matches[2] ? $template_matches[1] : $path; 180 | $template = $template_matches[2]; 181 | $replace = null; 182 | $page_path = Uri::convertUrl($page, $path, 'link', false, true); 183 | // Cleanup any current path (`./`) references 184 | $page_path = str_replace('/./', '/', $page_path); 185 | 186 | $inject = $pages->find($page_path); 187 | if ($inject instanceof PageInterface && $inject->published()) { 188 | // Force HTML to avoid issues with News Feeds 189 | $inject->templateFormat('html'); 190 | if ($type == 'page-inject') { 191 | if ($template) { 192 | $inject->template($template); 193 | } 194 | $inject->modularTwig(true); 195 | $replace = $inject->content(); 196 | 197 | } else { 198 | if ($processed_content) { 199 | $replace = $inject->content(); 200 | } else { 201 | $replace = $inject->rawMarkdown(); 202 | } 203 | } 204 | } 205 | 206 | return $replace; 207 | } 208 | 209 | protected function getPageInjectData() 210 | { 211 | $request = $this->grav['request']; 212 | $data = $request->getParsedBody(); 213 | $page_routes = $data['routes'] ?? []; 214 | $json = []; 215 | 216 | /** @var Pages $pages */ 217 | $pages = Admin::enablePages(); 218 | 219 | foreach ($page_routes as $route) { 220 | /** @var PageInterface */ 221 | $page = $pages->find($route); 222 | 223 | if (!$page) { 224 | $data = [ 225 | 'status' => 'Error', 226 | 'message' => 'Page not found', 227 | 'data' => [] 228 | ]; 229 | } else { 230 | $data = [ 231 | 'status' => 'success', 232 | 'message' => 'Page found', 233 | 'data' => [ 234 | 'title' => $page->title(), 235 | 'route' => $page->route(), 236 | 'modified' => $page->modified(), 237 | 'template' => $page->template(), 238 | ] 239 | ]; 240 | } 241 | 242 | $json['data'][] = $data; 243 | $json['available_templates'] = $pages->pageTypes(); 244 | } 245 | 246 | return $json; 247 | } 248 | 249 | protected function parseInjectLinks($content, $function) 250 | { 251 | $regex = '/\[plugin:(content-inject|page-inject)\]\((.*)\)/i'; 252 | return preg_replace_callback($regex, $function, $content); 253 | } 254 | 255 | } 256 | -------------------------------------------------------------------------------- /page-inject.yaml: -------------------------------------------------------------------------------- 1 | enabled: true 2 | active: true 3 | processed_content: false 4 | remote_injections: -------------------------------------------------------------------------------- /vendor/autoload.php: -------------------------------------------------------------------------------- 1 | 7 | * Jordi Boggiano 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Composer\Autoload; 14 | 15 | /** 16 | * ClassLoader implements a PSR-0, PSR-4 and classmap class loader. 17 | * 18 | * $loader = new \Composer\Autoload\ClassLoader(); 19 | * 20 | * // register classes with namespaces 21 | * $loader->add('Symfony\Component', __DIR__.'/component'); 22 | * $loader->add('Symfony', __DIR__.'/framework'); 23 | * 24 | * // activate the autoloader 25 | * $loader->register(); 26 | * 27 | * // to enable searching the include path (eg. for PEAR packages) 28 | * $loader->setUseIncludePath(true); 29 | * 30 | * In this example, if you try to use a class in the Symfony\Component 31 | * namespace or one of its children (Symfony\Component\Console for instance), 32 | * the autoloader will first look for the class under the component/ 33 | * directory, and it will then fallback to the framework/ directory if not 34 | * found before giving up. 35 | * 36 | * This class is loosely based on the Symfony UniversalClassLoader. 37 | * 38 | * @author Fabien Potencier 39 | * @author Jordi Boggiano 40 | * @see https://www.php-fig.org/psr/psr-0/ 41 | * @see https://www.php-fig.org/psr/psr-4/ 42 | */ 43 | class ClassLoader 44 | { 45 | /** @var ?string */ 46 | private $vendorDir; 47 | 48 | // PSR-4 49 | /** 50 | * @var array[] 51 | * @psalm-var array> 52 | */ 53 | private $prefixLengthsPsr4 = array(); 54 | /** 55 | * @var array[] 56 | * @psalm-var array> 57 | */ 58 | private $prefixDirsPsr4 = array(); 59 | /** 60 | * @var array[] 61 | * @psalm-var array 62 | */ 63 | private $fallbackDirsPsr4 = array(); 64 | 65 | // PSR-0 66 | /** 67 | * @var array[] 68 | * @psalm-var array> 69 | */ 70 | private $prefixesPsr0 = array(); 71 | /** 72 | * @var array[] 73 | * @psalm-var array 74 | */ 75 | private $fallbackDirsPsr0 = array(); 76 | 77 | /** @var bool */ 78 | private $useIncludePath = false; 79 | 80 | /** 81 | * @var string[] 82 | * @psalm-var array 83 | */ 84 | private $classMap = array(); 85 | 86 | /** @var bool */ 87 | private $classMapAuthoritative = false; 88 | 89 | /** 90 | * @var bool[] 91 | * @psalm-var array 92 | */ 93 | private $missingClasses = array(); 94 | 95 | /** @var ?string */ 96 | private $apcuPrefix; 97 | 98 | /** 99 | * @var self[] 100 | */ 101 | private static $registeredLoaders = array(); 102 | 103 | /** 104 | * @param ?string $vendorDir 105 | */ 106 | public function __construct($vendorDir = null) 107 | { 108 | $this->vendorDir = $vendorDir; 109 | } 110 | 111 | /** 112 | * @return string[] 113 | */ 114 | public function getPrefixes() 115 | { 116 | if (!empty($this->prefixesPsr0)) { 117 | return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); 118 | } 119 | 120 | return array(); 121 | } 122 | 123 | /** 124 | * @return array[] 125 | * @psalm-return array> 126 | */ 127 | public function getPrefixesPsr4() 128 | { 129 | return $this->prefixDirsPsr4; 130 | } 131 | 132 | /** 133 | * @return array[] 134 | * @psalm-return array 135 | */ 136 | public function getFallbackDirs() 137 | { 138 | return $this->fallbackDirsPsr0; 139 | } 140 | 141 | /** 142 | * @return array[] 143 | * @psalm-return array 144 | */ 145 | public function getFallbackDirsPsr4() 146 | { 147 | return $this->fallbackDirsPsr4; 148 | } 149 | 150 | /** 151 | * @return string[] Array of classname => path 152 | * @psalm-var array 153 | */ 154 | public function getClassMap() 155 | { 156 | return $this->classMap; 157 | } 158 | 159 | /** 160 | * @param string[] $classMap Class to filename map 161 | * @psalm-param array $classMap 162 | * 163 | * @return void 164 | */ 165 | public function addClassMap(array $classMap) 166 | { 167 | if ($this->classMap) { 168 | $this->classMap = array_merge($this->classMap, $classMap); 169 | } else { 170 | $this->classMap = $classMap; 171 | } 172 | } 173 | 174 | /** 175 | * Registers a set of PSR-0 directories for a given prefix, either 176 | * appending or prepending to the ones previously set for this prefix. 177 | * 178 | * @param string $prefix The prefix 179 | * @param string[]|string $paths The PSR-0 root directories 180 | * @param bool $prepend Whether to prepend the directories 181 | * 182 | * @return void 183 | */ 184 | public function add($prefix, $paths, $prepend = false) 185 | { 186 | if (!$prefix) { 187 | if ($prepend) { 188 | $this->fallbackDirsPsr0 = array_merge( 189 | (array) $paths, 190 | $this->fallbackDirsPsr0 191 | ); 192 | } else { 193 | $this->fallbackDirsPsr0 = array_merge( 194 | $this->fallbackDirsPsr0, 195 | (array) $paths 196 | ); 197 | } 198 | 199 | return; 200 | } 201 | 202 | $first = $prefix[0]; 203 | if (!isset($this->prefixesPsr0[$first][$prefix])) { 204 | $this->prefixesPsr0[$first][$prefix] = (array) $paths; 205 | 206 | return; 207 | } 208 | if ($prepend) { 209 | $this->prefixesPsr0[$first][$prefix] = array_merge( 210 | (array) $paths, 211 | $this->prefixesPsr0[$first][$prefix] 212 | ); 213 | } else { 214 | $this->prefixesPsr0[$first][$prefix] = array_merge( 215 | $this->prefixesPsr0[$first][$prefix], 216 | (array) $paths 217 | ); 218 | } 219 | } 220 | 221 | /** 222 | * Registers a set of PSR-4 directories for a given namespace, either 223 | * appending or prepending to the ones previously set for this namespace. 224 | * 225 | * @param string $prefix The prefix/namespace, with trailing '\\' 226 | * @param string[]|string $paths The PSR-4 base directories 227 | * @param bool $prepend Whether to prepend the directories 228 | * 229 | * @throws \InvalidArgumentException 230 | * 231 | * @return void 232 | */ 233 | public function addPsr4($prefix, $paths, $prepend = false) 234 | { 235 | if (!$prefix) { 236 | // Register directories for the root namespace. 237 | if ($prepend) { 238 | $this->fallbackDirsPsr4 = array_merge( 239 | (array) $paths, 240 | $this->fallbackDirsPsr4 241 | ); 242 | } else { 243 | $this->fallbackDirsPsr4 = array_merge( 244 | $this->fallbackDirsPsr4, 245 | (array) $paths 246 | ); 247 | } 248 | } elseif (!isset($this->prefixDirsPsr4[$prefix])) { 249 | // Register directories for a new namespace. 250 | $length = strlen($prefix); 251 | if ('\\' !== $prefix[$length - 1]) { 252 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 253 | } 254 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 255 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 256 | } elseif ($prepend) { 257 | // Prepend directories for an already registered namespace. 258 | $this->prefixDirsPsr4[$prefix] = array_merge( 259 | (array) $paths, 260 | $this->prefixDirsPsr4[$prefix] 261 | ); 262 | } else { 263 | // Append directories for an already registered namespace. 264 | $this->prefixDirsPsr4[$prefix] = array_merge( 265 | $this->prefixDirsPsr4[$prefix], 266 | (array) $paths 267 | ); 268 | } 269 | } 270 | 271 | /** 272 | * Registers a set of PSR-0 directories for a given prefix, 273 | * replacing any others previously set for this prefix. 274 | * 275 | * @param string $prefix The prefix 276 | * @param string[]|string $paths The PSR-0 base directories 277 | * 278 | * @return void 279 | */ 280 | public function set($prefix, $paths) 281 | { 282 | if (!$prefix) { 283 | $this->fallbackDirsPsr0 = (array) $paths; 284 | } else { 285 | $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; 286 | } 287 | } 288 | 289 | /** 290 | * Registers a set of PSR-4 directories for a given namespace, 291 | * replacing any others previously set for this namespace. 292 | * 293 | * @param string $prefix The prefix/namespace, with trailing '\\' 294 | * @param string[]|string $paths The PSR-4 base directories 295 | * 296 | * @throws \InvalidArgumentException 297 | * 298 | * @return void 299 | */ 300 | public function setPsr4($prefix, $paths) 301 | { 302 | if (!$prefix) { 303 | $this->fallbackDirsPsr4 = (array) $paths; 304 | } else { 305 | $length = strlen($prefix); 306 | if ('\\' !== $prefix[$length - 1]) { 307 | throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); 308 | } 309 | $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; 310 | $this->prefixDirsPsr4[$prefix] = (array) $paths; 311 | } 312 | } 313 | 314 | /** 315 | * Turns on searching the include path for class files. 316 | * 317 | * @param bool $useIncludePath 318 | * 319 | * @return void 320 | */ 321 | public function setUseIncludePath($useIncludePath) 322 | { 323 | $this->useIncludePath = $useIncludePath; 324 | } 325 | 326 | /** 327 | * Can be used to check if the autoloader uses the include path to check 328 | * for classes. 329 | * 330 | * @return bool 331 | */ 332 | public function getUseIncludePath() 333 | { 334 | return $this->useIncludePath; 335 | } 336 | 337 | /** 338 | * Turns off searching the prefix and fallback directories for classes 339 | * that have not been registered with the class map. 340 | * 341 | * @param bool $classMapAuthoritative 342 | * 343 | * @return void 344 | */ 345 | public function setClassMapAuthoritative($classMapAuthoritative) 346 | { 347 | $this->classMapAuthoritative = $classMapAuthoritative; 348 | } 349 | 350 | /** 351 | * Should class lookup fail if not found in the current class map? 352 | * 353 | * @return bool 354 | */ 355 | public function isClassMapAuthoritative() 356 | { 357 | return $this->classMapAuthoritative; 358 | } 359 | 360 | /** 361 | * APCu prefix to use to cache found/not-found classes, if the extension is enabled. 362 | * 363 | * @param string|null $apcuPrefix 364 | * 365 | * @return void 366 | */ 367 | public function setApcuPrefix($apcuPrefix) 368 | { 369 | $this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null; 370 | } 371 | 372 | /** 373 | * The APCu prefix in use, or null if APCu caching is not enabled. 374 | * 375 | * @return string|null 376 | */ 377 | public function getApcuPrefix() 378 | { 379 | return $this->apcuPrefix; 380 | } 381 | 382 | /** 383 | * Registers this instance as an autoloader. 384 | * 385 | * @param bool $prepend Whether to prepend the autoloader or not 386 | * 387 | * @return void 388 | */ 389 | public function register($prepend = false) 390 | { 391 | spl_autoload_register(array($this, 'loadClass'), true, $prepend); 392 | 393 | if (null === $this->vendorDir) { 394 | return; 395 | } 396 | 397 | if ($prepend) { 398 | self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; 399 | } else { 400 | unset(self::$registeredLoaders[$this->vendorDir]); 401 | self::$registeredLoaders[$this->vendorDir] = $this; 402 | } 403 | } 404 | 405 | /** 406 | * Unregisters this instance as an autoloader. 407 | * 408 | * @return void 409 | */ 410 | public function unregister() 411 | { 412 | spl_autoload_unregister(array($this, 'loadClass')); 413 | 414 | if (null !== $this->vendorDir) { 415 | unset(self::$registeredLoaders[$this->vendorDir]); 416 | } 417 | } 418 | 419 | /** 420 | * Loads the given class or interface. 421 | * 422 | * @param string $class The name of the class 423 | * @return true|null True if loaded, null otherwise 424 | */ 425 | public function loadClass($class) 426 | { 427 | if ($file = $this->findFile($class)) { 428 | includeFile($file); 429 | 430 | return true; 431 | } 432 | 433 | return null; 434 | } 435 | 436 | /** 437 | * Finds the path to the file where the class is defined. 438 | * 439 | * @param string $class The name of the class 440 | * 441 | * @return string|false The path if found, false otherwise 442 | */ 443 | public function findFile($class) 444 | { 445 | // class map lookup 446 | if (isset($this->classMap[$class])) { 447 | return $this->classMap[$class]; 448 | } 449 | if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) { 450 | return false; 451 | } 452 | if (null !== $this->apcuPrefix) { 453 | $file = apcu_fetch($this->apcuPrefix.$class, $hit); 454 | if ($hit) { 455 | return $file; 456 | } 457 | } 458 | 459 | $file = $this->findFileWithExtension($class, '.php'); 460 | 461 | // Search for Hack files if we are running on HHVM 462 | if (false === $file && defined('HHVM_VERSION')) { 463 | $file = $this->findFileWithExtension($class, '.hh'); 464 | } 465 | 466 | if (null !== $this->apcuPrefix) { 467 | apcu_add($this->apcuPrefix.$class, $file); 468 | } 469 | 470 | if (false === $file) { 471 | // Remember that this class does not exist. 472 | $this->missingClasses[$class] = true; 473 | } 474 | 475 | return $file; 476 | } 477 | 478 | /** 479 | * Returns the currently registered loaders indexed by their corresponding vendor directories. 480 | * 481 | * @return self[] 482 | */ 483 | public static function getRegisteredLoaders() 484 | { 485 | return self::$registeredLoaders; 486 | } 487 | 488 | /** 489 | * @param string $class 490 | * @param string $ext 491 | * @return string|false 492 | */ 493 | private function findFileWithExtension($class, $ext) 494 | { 495 | // PSR-4 lookup 496 | $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; 497 | 498 | $first = $class[0]; 499 | if (isset($this->prefixLengthsPsr4[$first])) { 500 | $subPath = $class; 501 | while (false !== $lastPos = strrpos($subPath, '\\')) { 502 | $subPath = substr($subPath, 0, $lastPos); 503 | $search = $subPath . '\\'; 504 | if (isset($this->prefixDirsPsr4[$search])) { 505 | $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1); 506 | foreach ($this->prefixDirsPsr4[$search] as $dir) { 507 | if (file_exists($file = $dir . $pathEnd)) { 508 | return $file; 509 | } 510 | } 511 | } 512 | } 513 | } 514 | 515 | // PSR-4 fallback dirs 516 | foreach ($this->fallbackDirsPsr4 as $dir) { 517 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) { 518 | return $file; 519 | } 520 | } 521 | 522 | // PSR-0 lookup 523 | if (false !== $pos = strrpos($class, '\\')) { 524 | // namespaced class name 525 | $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1) 526 | . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR); 527 | } else { 528 | // PEAR-like class name 529 | $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext; 530 | } 531 | 532 | if (isset($this->prefixesPsr0[$first])) { 533 | foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) { 534 | if (0 === strpos($class, $prefix)) { 535 | foreach ($dirs as $dir) { 536 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 537 | return $file; 538 | } 539 | } 540 | } 541 | } 542 | } 543 | 544 | // PSR-0 fallback dirs 545 | foreach ($this->fallbackDirsPsr0 as $dir) { 546 | if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) { 547 | return $file; 548 | } 549 | } 550 | 551 | // PSR-0 include paths. 552 | if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) { 553 | return $file; 554 | } 555 | 556 | return false; 557 | } 558 | } 559 | 560 | /** 561 | * Scope isolated include. 562 | * 563 | * Prevents access to $this/self from included files. 564 | * 565 | * @param string $file 566 | * @return void 567 | * @private 568 | */ 569 | function includeFile($file) 570 | { 571 | include $file; 572 | } 573 | -------------------------------------------------------------------------------- /vendor/composer/InstalledVersions.php: -------------------------------------------------------------------------------- 1 | 7 | * Jordi Boggiano 8 | * 9 | * For the full copyright and license information, please view the LICENSE 10 | * file that was distributed with this source code. 11 | */ 12 | 13 | namespace Composer; 14 | 15 | use Composer\Autoload\ClassLoader; 16 | use Composer\Semver\VersionParser; 17 | 18 | /** 19 | * This class is copied in every Composer installed project and available to all 20 | * 21 | * See also https://getcomposer.org/doc/07-runtime.md#installed-versions 22 | * 23 | * To require its presence, you can require `composer-runtime-api ^2.0` 24 | */ 25 | class InstalledVersions 26 | { 27 | /** 28 | * @var mixed[]|null 29 | * @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array}|array{}|null 30 | */ 31 | private static $installed; 32 | 33 | /** 34 | * @var bool|null 35 | */ 36 | private static $canGetVendors; 37 | 38 | /** 39 | * @var array[] 40 | * @psalm-var array}> 41 | */ 42 | private static $installedByVendor = array(); 43 | 44 | /** 45 | * Returns a list of all package names which are present, either by being installed, replaced or provided 46 | * 47 | * @return string[] 48 | * @psalm-return list 49 | */ 50 | public static function getInstalledPackages() 51 | { 52 | $packages = array(); 53 | foreach (self::getInstalled() as $installed) { 54 | $packages[] = array_keys($installed['versions']); 55 | } 56 | 57 | if (1 === \count($packages)) { 58 | return $packages[0]; 59 | } 60 | 61 | return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); 62 | } 63 | 64 | /** 65 | * Returns a list of all package names with a specific type e.g. 'library' 66 | * 67 | * @param string $type 68 | * @return string[] 69 | * @psalm-return list 70 | */ 71 | public static function getInstalledPackagesByType($type) 72 | { 73 | $packagesByType = array(); 74 | 75 | foreach (self::getInstalled() as $installed) { 76 | foreach ($installed['versions'] as $name => $package) { 77 | if (isset($package['type']) && $package['type'] === $type) { 78 | $packagesByType[] = $name; 79 | } 80 | } 81 | } 82 | 83 | return $packagesByType; 84 | } 85 | 86 | /** 87 | * Checks whether the given package is installed 88 | * 89 | * This also returns true if the package name is provided or replaced by another package 90 | * 91 | * @param string $packageName 92 | * @param bool $includeDevRequirements 93 | * @return bool 94 | */ 95 | public static function isInstalled($packageName, $includeDevRequirements = true) 96 | { 97 | foreach (self::getInstalled() as $installed) { 98 | if (isset($installed['versions'][$packageName])) { 99 | return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']); 100 | } 101 | } 102 | 103 | return false; 104 | } 105 | 106 | /** 107 | * Checks whether the given package satisfies a version constraint 108 | * 109 | * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: 110 | * 111 | * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') 112 | * 113 | * @param VersionParser $parser Install composer/semver to have access to this class and functionality 114 | * @param string $packageName 115 | * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package 116 | * @return bool 117 | */ 118 | public static function satisfies(VersionParser $parser, $packageName, $constraint) 119 | { 120 | $constraint = $parser->parseConstraints($constraint); 121 | $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); 122 | 123 | return $provided->matches($constraint); 124 | } 125 | 126 | /** 127 | * Returns a version constraint representing all the range(s) which are installed for a given package 128 | * 129 | * It is easier to use this via isInstalled() with the $constraint argument if you need to check 130 | * whether a given version of a package is installed, and not just whether it exists 131 | * 132 | * @param string $packageName 133 | * @return string Version constraint usable with composer/semver 134 | */ 135 | public static function getVersionRanges($packageName) 136 | { 137 | foreach (self::getInstalled() as $installed) { 138 | if (!isset($installed['versions'][$packageName])) { 139 | continue; 140 | } 141 | 142 | $ranges = array(); 143 | if (isset($installed['versions'][$packageName]['pretty_version'])) { 144 | $ranges[] = $installed['versions'][$packageName]['pretty_version']; 145 | } 146 | if (array_key_exists('aliases', $installed['versions'][$packageName])) { 147 | $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); 148 | } 149 | if (array_key_exists('replaced', $installed['versions'][$packageName])) { 150 | $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); 151 | } 152 | if (array_key_exists('provided', $installed['versions'][$packageName])) { 153 | $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); 154 | } 155 | 156 | return implode(' || ', $ranges); 157 | } 158 | 159 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 160 | } 161 | 162 | /** 163 | * @param string $packageName 164 | * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present 165 | */ 166 | public static function getVersion($packageName) 167 | { 168 | foreach (self::getInstalled() as $installed) { 169 | if (!isset($installed['versions'][$packageName])) { 170 | continue; 171 | } 172 | 173 | if (!isset($installed['versions'][$packageName]['version'])) { 174 | return null; 175 | } 176 | 177 | return $installed['versions'][$packageName]['version']; 178 | } 179 | 180 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 181 | } 182 | 183 | /** 184 | * @param string $packageName 185 | * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present 186 | */ 187 | public static function getPrettyVersion($packageName) 188 | { 189 | foreach (self::getInstalled() as $installed) { 190 | if (!isset($installed['versions'][$packageName])) { 191 | continue; 192 | } 193 | 194 | if (!isset($installed['versions'][$packageName]['pretty_version'])) { 195 | return null; 196 | } 197 | 198 | return $installed['versions'][$packageName]['pretty_version']; 199 | } 200 | 201 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 202 | } 203 | 204 | /** 205 | * @param string $packageName 206 | * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference 207 | */ 208 | public static function getReference($packageName) 209 | { 210 | foreach (self::getInstalled() as $installed) { 211 | if (!isset($installed['versions'][$packageName])) { 212 | continue; 213 | } 214 | 215 | if (!isset($installed['versions'][$packageName]['reference'])) { 216 | return null; 217 | } 218 | 219 | return $installed['versions'][$packageName]['reference']; 220 | } 221 | 222 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 223 | } 224 | 225 | /** 226 | * @param string $packageName 227 | * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. 228 | */ 229 | public static function getInstallPath($packageName) 230 | { 231 | foreach (self::getInstalled() as $installed) { 232 | if (!isset($installed['versions'][$packageName])) { 233 | continue; 234 | } 235 | 236 | return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; 237 | } 238 | 239 | throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); 240 | } 241 | 242 | /** 243 | * @return array 244 | * @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string} 245 | */ 246 | public static function getRootPackage() 247 | { 248 | $installed = self::getInstalled(); 249 | 250 | return $installed[0]['root']; 251 | } 252 | 253 | /** 254 | * Returns the raw installed.php data for custom implementations 255 | * 256 | * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. 257 | * @return array[] 258 | * @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array} 259 | */ 260 | public static function getRawData() 261 | { 262 | @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); 263 | 264 | if (null === self::$installed) { 265 | // only require the installed.php file if this file is loaded from its dumped location, 266 | // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 267 | if (substr(__DIR__, -8, 1) !== 'C') { 268 | self::$installed = include __DIR__ . '/installed.php'; 269 | } else { 270 | self::$installed = array(); 271 | } 272 | } 273 | 274 | return self::$installed; 275 | } 276 | 277 | /** 278 | * Returns the raw data of all installed.php which are currently loaded for custom implementations 279 | * 280 | * @return array[] 281 | * @psalm-return list}> 282 | */ 283 | public static function getAllRawData() 284 | { 285 | return self::getInstalled(); 286 | } 287 | 288 | /** 289 | * Lets you reload the static array from another file 290 | * 291 | * This is only useful for complex integrations in which a project needs to use 292 | * this class but then also needs to execute another project's autoloader in process, 293 | * and wants to ensure both projects have access to their version of installed.php. 294 | * 295 | * A typical case would be PHPUnit, where it would need to make sure it reads all 296 | * the data it needs from this class, then call reload() with 297 | * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure 298 | * the project in which it runs can then also use this class safely, without 299 | * interference between PHPUnit's dependencies and the project's dependencies. 300 | * 301 | * @param array[] $data A vendor/composer/installed.php data set 302 | * @return void 303 | * 304 | * @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array} $data 305 | */ 306 | public static function reload($data) 307 | { 308 | self::$installed = $data; 309 | self::$installedByVendor = array(); 310 | } 311 | 312 | /** 313 | * @return array[] 314 | * @psalm-return list}> 315 | */ 316 | private static function getInstalled() 317 | { 318 | if (null === self::$canGetVendors) { 319 | self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); 320 | } 321 | 322 | $installed = array(); 323 | 324 | if (self::$canGetVendors) { 325 | foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { 326 | if (isset(self::$installedByVendor[$vendorDir])) { 327 | $installed[] = self::$installedByVendor[$vendorDir]; 328 | } elseif (is_file($vendorDir.'/composer/installed.php')) { 329 | $installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php'; 330 | if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { 331 | self::$installed = $installed[count($installed) - 1]; 332 | } 333 | } 334 | } 335 | } 336 | 337 | if (null === self::$installed) { 338 | // only require the installed.php file if this file is loaded from its dumped location, 339 | // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 340 | if (substr(__DIR__, -8, 1) !== 'C') { 341 | self::$installed = require __DIR__ . '/installed.php'; 342 | } else { 343 | self::$installed = array(); 344 | } 345 | } 346 | $installed[] = self::$installed; 347 | 348 | return $installed; 349 | } 350 | } 351 | -------------------------------------------------------------------------------- /vendor/composer/LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) Nils Adermann, Jordi Boggiano 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is furnished 9 | to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /vendor/composer/autoload_classmap.php: -------------------------------------------------------------------------------- 1 | $vendorDir . '/composer/InstalledVersions.php', 10 | 'Grav\\Plugin\\PageInjectPlugin' => $baseDir . '/page-inject.php', 11 | ); 12 | -------------------------------------------------------------------------------- /vendor/composer/autoload_namespaces.php: -------------------------------------------------------------------------------- 1 | array($baseDir . '/classes'), 10 | ); 11 | -------------------------------------------------------------------------------- /vendor/composer/autoload_real.php: -------------------------------------------------------------------------------- 1 | = 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); 32 | if ($useStaticLoader) { 33 | require __DIR__ . '/autoload_static.php'; 34 | 35 | call_user_func(\Composer\Autoload\ComposerStaticInitcb6e7b445b2976f19ebea4affe71a8b0::getInitializer($loader)); 36 | } else { 37 | $map = require __DIR__ . '/autoload_namespaces.php'; 38 | foreach ($map as $namespace => $path) { 39 | $loader->set($namespace, $path); 40 | } 41 | 42 | $map = require __DIR__ . '/autoload_psr4.php'; 43 | foreach ($map as $namespace => $path) { 44 | $loader->setPsr4($namespace, $path); 45 | } 46 | 47 | $classMap = require __DIR__ . '/autoload_classmap.php'; 48 | if ($classMap) { 49 | $loader->addClassMap($classMap); 50 | } 51 | } 52 | 53 | $loader->register(true); 54 | 55 | return $loader; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /vendor/composer/autoload_static.php: -------------------------------------------------------------------------------- 1 | 11 | array ( 12 | 'Grav\\Plugin\\PageInject\\' => 23, 13 | ), 14 | ); 15 | 16 | public static $prefixDirsPsr4 = array ( 17 | 'Grav\\Plugin\\PageInject\\' => 18 | array ( 19 | 0 => __DIR__ . '/../..' . '/classes', 20 | ), 21 | ); 22 | 23 | public static $classMap = array ( 24 | 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', 25 | 'Grav\\Plugin\\PageInjectPlugin' => __DIR__ . '/../..' . '/page-inject.php', 26 | ); 27 | 28 | public static function getInitializer(ClassLoader $loader) 29 | { 30 | return \Closure::bind(function () use ($loader) { 31 | $loader->prefixLengthsPsr4 = ComposerStaticInitcb6e7b445b2976f19ebea4affe71a8b0::$prefixLengthsPsr4; 32 | $loader->prefixDirsPsr4 = ComposerStaticInitcb6e7b445b2976f19ebea4affe71a8b0::$prefixDirsPsr4; 33 | $loader->classMap = ComposerStaticInitcb6e7b445b2976f19ebea4affe71a8b0::$classMap; 34 | 35 | }, null, ClassLoader::class); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /vendor/composer/installed.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [], 3 | "dev": true, 4 | "dev-package-names": [] 5 | } 6 | -------------------------------------------------------------------------------- /vendor/composer/installed.php: -------------------------------------------------------------------------------- 1 | array( 3 | 'pretty_version' => 'dev-develop', 4 | 'version' => 'dev-develop', 5 | 'type' => 'grav-plugin', 6 | 'install_path' => __DIR__ . '/../../', 7 | 'aliases' => array(), 8 | 'reference' => 'd3256f3c89a188a002a7b589f5b4aa95b292901a', 9 | 'name' => 'getgrav/page-inject', 10 | 'dev' => true, 11 | ), 12 | 'versions' => array( 13 | 'getgrav/page-inject' => array( 14 | 'pretty_version' => 'dev-develop', 15 | 'version' => 'dev-develop', 16 | 'type' => 'grav-plugin', 17 | 'install_path' => __DIR__ . '/../../', 18 | 'aliases' => array(), 19 | 'reference' => 'd3256f3c89a188a002a7b589f5b4aa95b292901a', 20 | 'dev_requirement' => false, 21 | ), 22 | ), 23 | ); 24 | -------------------------------------------------------------------------------- /vendor/composer/platform_check.php: -------------------------------------------------------------------------------- 1 | = 70103)) { 8 | $issues[] = 'Your Composer dependencies require a PHP version ">= 7.1.3". You are running ' . PHP_VERSION . '.'; 9 | } 10 | 11 | if ($issues) { 12 | if (!headers_sent()) { 13 | header('HTTP/1.1 500 Internal Server Error'); 14 | } 15 | if (!ini_get('display_errors')) { 16 | if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { 17 | fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); 18 | } elseif (!headers_sent()) { 19 | echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; 20 | } 21 | } 22 | trigger_error( 23 | 'Composer detected issues in your platform: ' . implode(' ', $issues), 24 | E_USER_ERROR 25 | ); 26 | } 27 | --------------------------------------------------------------------------------