From 22adba5d54509a10d5115ace45265803deeb1184 Mon Sep 17 00:00:00 2001 From: ouidade Date: Mon, 14 Mar 2022 12:15:17 +0100 Subject: [PATCH] maj --- CHANGELOG.md | 135 +++ README.md | 15 +- assets/.gitkeep | 2 +- bin/composer.phar | Bin 2205196 -> 2291189 bytes bin/gpm | 6 +- bin/grav | 6 +- bin/plugin | 6 +- composer.json | 36 +- composer.lock | 929 ++++++++++-------- index.php | 6 +- system/blueprints/config/system.yaml | 183 +++- system/blueprints/flex/pages.yaml | 2 +- system/blueprints/flex/user-accounts.yaml | 2 + system/blueprints/flex/user-groups.yaml | 1 + system/blueprints/pages/default.yaml | 12 + system/blueprints/pages/external.yaml | 18 +- .../blueprints/pages/partials/security.yaml | 10 +- system/blueprints/user/account.yaml | 6 + system/config/media.yaml | 2 +- system/config/system.yaml | 24 +- system/defines.php | 4 +- system/images/watermark.png | Bin 0 -> 95789 bytes system/install.php | 2 +- system/languages/es.yaml | 12 +- system/router.php | 23 +- system/src/DOMLettersIterator.php | 165 ++++ system/src/DOMWordsIterator.php | 158 +++ system/src/Grav/Common/Assets.php | 147 ++- system/src/Grav/Common/Assets/BaseAsset.php | 23 +- system/src/Grav/Common/Assets/BlockAssets.php | 207 ++++ system/src/Grav/Common/Assets/Css.php | 4 +- system/src/Grav/Common/Assets/InlineCss.php | 4 +- system/src/Grav/Common/Assets/InlineJs.php | 4 +- .../src/Grav/Common/Assets/InlineJsModule.php | 46 + system/src/Grav/Common/Assets/Js.php | 4 +- system/src/Grav/Common/Assets/JsModule.php | 49 + system/src/Grav/Common/Assets/Link.php | 43 + system/src/Grav/Common/Assets/Pipeline.php | 65 +- .../Common/Assets/Traits/AssetUtilsTrait.php | 18 +- .../Assets/Traits/LegacyAssetsTrait.php | 2 +- .../Assets/Traits/TestingAssetsTrait.php | 11 +- system/src/Grav/Common/Backup/Backups.php | 7 +- system/src/Grav/Common/Browser.php | 2 +- system/src/Grav/Common/Cache.php | 4 +- system/src/Grav/Common/Composer.php | 2 +- .../src/Grav/Common/Config/CompiledBase.php | 2 +- .../Grav/Common/Config/CompiledBlueprints.php | 2 +- .../src/Grav/Common/Config/CompiledConfig.php | 2 +- .../Grav/Common/Config/CompiledLanguages.php | 2 +- system/src/Grav/Common/Config/Config.php | 2 +- .../Grav/Common/Config/ConfigFileFinder.php | 2 +- system/src/Grav/Common/Config/Languages.php | 2 +- system/src/Grav/Common/Config/Setup.php | 4 +- system/src/Grav/Common/Data/Blueprint.php | 13 +- .../src/Grav/Common/Data/BlueprintSchema.php | 24 +- system/src/Grav/Common/Data/Blueprints.php | 2 +- system/src/Grav/Common/Data/Data.php | 3 +- system/src/Grav/Common/Data/DataInterface.php | 2 +- system/src/Grav/Common/Data/Validation.php | 16 +- .../Grav/Common/Data/ValidationException.php | 25 +- system/src/Grav/Common/Debugger.php | 6 +- system/src/Grav/Common/Errors/BareHandler.php | 2 +- system/src/Grav/Common/Errors/Errors.php | 2 +- .../Grav/Common/Errors/SimplePageHandler.php | 2 +- .../src/Grav/Common/Errors/SystemFacade.php | 2 +- system/src/Grav/Common/File/CompiledFile.php | 5 +- .../src/Grav/Common/File/CompiledJsonFile.php | 2 +- .../Grav/Common/File/CompiledMarkdownFile.php | 2 +- .../src/Grav/Common/File/CompiledYamlFile.php | 2 +- .../src/Grav/Common/Filesystem/Archiver.php | 2 +- system/src/Grav/Common/Filesystem/Folder.php | 7 +- .../RecursiveDirectoryFilterIterator.php | 2 +- .../RecursiveFolderFilterIterator.php | 2 +- .../Grav/Common/Filesystem/ZipArchiver.php | 2 +- .../src/Grav/Common/Flex/FlexCollection.php | 2 +- system/src/Grav/Common/Flex/FlexIndex.php | 2 +- system/src/Grav/Common/Flex/FlexObject.php | 2 +- .../Flex/Traits/FlexCollectionTrait.php | 2 +- .../Common/Flex/Traits/FlexCommonTrait.php | 2 +- .../Grav/Common/Flex/Traits/FlexGravTrait.php | 2 +- .../Common/Flex/Traits/FlexIndexTrait.php | 2 +- .../Common/Flex/Traits/FlexObjectTrait.php | 2 +- .../Flex/Types/Generic/GenericCollection.php | 2 +- .../Flex/Types/Generic/GenericIndex.php | 2 +- .../Flex/Types/Generic/GenericObject.php | 2 +- .../Flex/Types/Pages/PageCollection.php | 49 +- .../Common/Flex/Types/Pages/PageIndex.php | 54 +- .../Common/Flex/Types/Pages/PageObject.php | 18 +- .../Flex/Types/Pages/Storage/PageStorage.php | 10 +- .../Types/Pages/Traits/PageContentTrait.php | 2 +- .../Types/Pages/Traits/PageLegacyTrait.php | 2 +- .../Types/Pages/Traits/PageRoutableTrait.php | 2 +- .../Types/Pages/Traits/PageTranslateTrait.php | 2 +- .../Types/UserGroups/UserGroupCollection.php | 4 +- .../Flex/Types/UserGroups/UserGroupIndex.php | 2 +- .../Flex/Types/UserGroups/UserGroupObject.php | 4 +- .../Types/Users/Storage/UserFileStorage.php | 2 +- .../Types/Users/Storage/UserFolderStorage.php | 2 +- .../Users/Traits/UserObjectLegacyTrait.php | 3 +- .../Flex/Types/Users/UserCollection.php | 2 +- .../Common/Flex/Types/Users/UserIndex.php | 4 +- .../Common/Flex/Types/Users/UserObject.php | 70 +- system/src/Grav/Common/Form/FormFlash.php | 7 +- .../Grav/Common/GPM/AbstractCollection.php | 2 +- .../GPM/Common/AbstractPackageCollection.php | 2 +- .../Common/GPM/Common/CachedCollection.php | 2 +- system/src/Grav/Common/GPM/Common/Package.php | 6 +- system/src/Grav/Common/GPM/GPM.php | 12 +- system/src/Grav/Common/GPM/Installer.php | 5 +- system/src/Grav/Common/GPM/Licenses.php | 2 +- .../GPM/Local/AbstractPackageCollection.php | 2 +- system/src/Grav/Common/GPM/Local/Package.php | 2 +- system/src/Grav/Common/GPM/Local/Packages.php | 2 +- system/src/Grav/Common/GPM/Local/Plugins.php | 2 +- system/src/Grav/Common/GPM/Local/Themes.php | 2 +- .../GPM/Remote/AbstractPackageCollection.php | 2 +- .../src/Grav/Common/GPM/Remote/GravCore.php | 2 +- system/src/Grav/Common/GPM/Remote/Package.php | 3 +- .../src/Grav/Common/GPM/Remote/Packages.php | 2 +- system/src/Grav/Common/GPM/Remote/Plugins.php | 2 +- system/src/Grav/Common/GPM/Remote/Themes.php | 2 +- system/src/Grav/Common/GPM/Response.php | 144 +-- system/src/Grav/Common/GPM/Upgrader.php | 2 +- system/src/Grav/Common/Getters.php | 11 +- system/src/Grav/Common/Grav.php | 110 ++- system/src/Grav/Common/GravTrait.php | 2 +- system/src/Grav/Common/HTTP/Client.php | 130 +++ system/src/Grav/Common/HTTP/Response.php | 96 ++ system/src/Grav/Common/Helpers/Base32.php | 2 +- system/src/Grav/Common/Helpers/Excerpts.php | 2 +- system/src/Grav/Common/Helpers/Exif.php | 2 +- system/src/Grav/Common/Helpers/LogViewer.php | 4 +- system/src/Grav/Common/Helpers/Truncator.php | 2 +- system/src/Grav/Common/Helpers/YamlLinter.php | 5 +- system/src/Grav/Common/Inflector.php | 2 +- system/src/Grav/Common/Iterator.php | 5 +- system/src/Grav/Common/Language/Language.php | 3 +- .../Grav/Common/Language/LanguageCodes.php | 6 +- system/src/Grav/Common/Markdown/Parsedown.php | 2 +- .../Grav/Common/Markdown/ParsedownExtra.php | 2 +- .../Common/Markdown/ParsedownGravTrait.php | 3 +- .../Media/Interfaces/AudioMediaInterface.php | 2 +- .../Interfaces/ImageManipulateInterface.php | 2 +- .../Media/Interfaces/ImageMediaInterface.php | 2 +- .../Interfaces/MediaCollectionInterface.php | 2 +- .../Media/Interfaces/MediaFileInterface.php | 2 +- .../Media/Interfaces/MediaInterface.php | 2 +- .../Media/Interfaces/MediaLinkInterface.php | 2 +- .../Media/Interfaces/MediaObjectInterface.php | 11 +- .../Media/Interfaces/MediaPlayerInterface.php | 2 +- .../Media/Interfaces/MediaUploadInterface.php | 2 +- .../Media/Interfaces/VideoMediaInterface.php | 2 +- .../Common/Media/Traits/AudioMediaTrait.php | 2 +- .../Common/Media/Traits/ImageLoadingTrait.php | 2 +- .../Common/Media/Traits/ImageMediaTrait.php | 10 +- .../Common/Media/Traits/MediaFileTrait.php | 2 +- .../Common/Media/Traits/MediaObjectTrait.php | 33 +- .../Common/Media/Traits/MediaPlayerTrait.php | 2 +- .../Grav/Common/Media/Traits/MediaTrait.php | 2 +- .../Common/Media/Traits/MediaUploadTrait.php | 8 +- .../Common/Media/Traits/StaticResizeTrait.php | 2 +- .../Media/Traits/ThumbnailMediaTrait.php | 2 +- .../Common/Media/Traits/VideoMediaTrait.php | 2 +- system/src/Grav/Common/Page/Collection.php | 6 +- system/src/Grav/Common/Page/Header.php | 3 +- .../Interfaces/PageCollectionInterface.php | 29 +- .../Page/Interfaces/PageContentInterface.php | 16 +- .../Common/Page/Interfaces/PageInterface.php | 2 +- .../Interfaces/PageTranslateInterface.php | 5 + .../Page/Interfaces/PagesSourceInterface.php | 2 +- .../Grav/Common/Page/Markdown/Excerpts.php | 4 +- system/src/Grav/Common/Page/Media.php | 61 +- .../Grav/Common/Page/Medium/AbstractMedia.php | 3 +- .../Grav/Common/Page/Medium/AudioMedium.php | 2 +- .../Grav/Common/Page/Medium/GlobalMedia.php | 7 +- .../src/Grav/Common/Page/Medium/ImageFile.php | 3 +- .../Grav/Common/Page/Medium/ImageMedium.php | 104 +- system/src/Grav/Common/Page/Medium/Link.php | 5 +- system/src/Grav/Common/Page/Medium/Medium.php | 12 +- .../Grav/Common/Page/Medium/MediumFactory.php | 11 +- .../Common/Page/Medium/ParsedownHtmlTrait.php | 2 +- .../Page/Medium/RenderableInterface.php | 2 +- .../Common/Page/Medium/StaticImageMedium.php | 10 +- .../Common/Page/Medium/StaticResizeTrait.php | 2 +- .../Page/Medium/ThumbnailImageMedium.php | 2 +- .../Grav/Common/Page/Medium/VideoMedium.php | 2 +- system/src/Grav/Common/Page/Page.php | 49 +- system/src/Grav/Common/Page/Pages.php | 6 +- system/src/Grav/Common/Page/Types.php | 5 +- system/src/Grav/Common/Plugin.php | 32 +- system/src/Grav/Common/Plugins.php | 2 +- .../Common/Processors/AssetsProcessor.php | 2 +- .../Common/Processors/BackupsProcessor.php | 2 +- .../Processors/DebuggerAssetsProcessor.php | 2 +- .../Processors/Events/RequestHandlerEvent.php | 2 +- .../Common/Processors/InitializeProcessor.php | 10 +- .../Grav/Common/Processors/PagesProcessor.php | 22 +- .../Common/Processors/PluginsProcessor.php | 2 +- .../Grav/Common/Processors/ProcessorBase.php | 2 +- .../Common/Processors/ProcessorInterface.php | 2 +- .../Common/Processors/RenderProcessor.php | 2 +- .../Common/Processors/RequestProcessor.php | 5 +- .../Common/Processors/SchedulerProcessor.php | 2 +- .../Grav/Common/Processors/TasksProcessor.php | 2 +- .../Common/Processors/ThemesProcessor.php | 2 +- .../Grav/Common/Processors/TwigProcessor.php | 2 +- system/src/Grav/Common/Scheduler/Cron.php | 2 +- .../Grav/Common/Scheduler/IntervalTrait.php | 2 +- system/src/Grav/Common/Scheduler/Job.php | 2 +- .../src/Grav/Common/Scheduler/Scheduler.php | 2 +- system/src/Grav/Common/Security.php | 16 +- .../Service/AccountsServiceProvider.php | 2 +- .../Common/Service/AssetsServiceProvider.php | 2 +- .../Common/Service/BackupsServiceProvider.php | 2 +- .../Common/Service/ConfigServiceProvider.php | 4 +- .../Common/Service/ErrorServiceProvider.php | 2 +- .../Service/FilesystemServiceProvider.php | 2 +- .../Common/Service/FlexServiceProvider.php | 2 +- .../Service/InflectorServiceProvider.php | 2 +- .../Common/Service/LoggerServiceProvider.php | 2 +- .../Common/Service/OutputServiceProvider.php | 2 +- .../Common/Service/PagesServiceProvider.php | 5 +- .../Common/Service/RequestServiceProvider.php | 2 +- .../Service/SchedulerServiceProvider.php | 2 +- .../Common/Service/SessionServiceProvider.php | 5 +- .../Common/Service/StreamsServiceProvider.php | 2 +- .../Common/Service/TaskServiceProvider.php | 2 +- system/src/Grav/Common/Session.php | 2 +- system/src/Grav/Common/Taxonomy.php | 2 +- system/src/Grav/Common/Theme.php | 2 +- system/src/Grav/Common/Themes.php | 2 +- .../Common/Twig/Exception/TwigException.php | 6 +- .../Twig/Extension/FilesystemExtension.php | 26 +- .../Common/Twig/Extension/GravExtension.php | 33 +- .../Grav/Common/Twig/Node/TwigNodeCache.php | 2 +- .../Grav/Common/Twig/Node/TwigNodeLink.php | 114 +++ .../Common/Twig/Node/TwigNodeMarkdown.php | 2 +- .../Grav/Common/Twig/Node/TwigNodeRender.php | 3 +- .../Grav/Common/Twig/Node/TwigNodeScript.php | 84 +- .../Grav/Common/Twig/Node/TwigNodeStyle.php | 72 +- .../Grav/Common/Twig/Node/TwigNodeSwitch.php | 2 +- .../Grav/Common/Twig/Node/TwigNodeThrow.php | 2 +- .../Common/Twig/Node/TwigNodeTryCatch.php | 2 +- .../Twig/TokenParser/TwigTokenParserCache.php | 2 +- .../Twig/TokenParser/TwigTokenParserLink.php | 109 ++ .../TokenParser/TwigTokenParserMarkdown.php | 2 +- .../TokenParser/TwigTokenParserRender.php | 2 +- .../TokenParser/TwigTokenParserScript.php | 28 +- .../Twig/TokenParser/TwigTokenParserStyle.php | 2 +- .../TokenParser/TwigTokenParserSwitch.php | 2 +- .../Twig/TokenParser/TwigTokenParserThrow.php | 2 +- .../TokenParser/TwigTokenParserTryCatch.php | 2 +- system/src/Grav/Common/Twig/Twig.php | 9 +- .../Common/Twig/TwigClockworkDataSource.php | 2 +- .../Grav/Common/Twig/TwigClockworkDumper.php | 2 +- .../src/Grav/Common/Twig/TwigEnvironment.php | 35 +- system/src/Grav/Common/Twig/TwigExtension.php | 2 +- .../Grav/Common/Twig/WriteCacheFileTrait.php | 2 +- system/src/Grav/Common/Uri.php | 25 +- system/src/Grav/Common/User/Access.php | 2 +- .../src/Grav/Common/User/Authentication.php | 2 +- system/src/Grav/Common/User/DataUser/User.php | 8 +- .../Common/User/DataUser/UserCollection.php | 4 +- system/src/Grav/Common/User/Group.php | 4 +- .../User/Interfaces/AuthorizeInterface.php | 2 +- .../Interfaces/UserCollectionInterface.php | 2 +- .../User/Interfaces/UserGroupInterface.php | 2 +- .../Common/User/Interfaces/UserInterface.php | 2 +- .../src/Grav/Common/User/Traits/UserTrait.php | 2 +- system/src/Grav/Common/User/User.php | 2 +- system/src/Grav/Common/Utils.php | 97 +- system/src/Grav/Common/Yaml.php | 8 +- .../Grav/Console/Application/Application.php | 48 +- .../CommandLoader/PluginCommandLoader.php | 2 +- .../Console/Application/GpmApplication.php | 2 +- .../Console/Application/GravApplication.php | 2 +- .../Console/Application/PluginApplication.php | 2 +- system/src/Grav/Console/Cli/BackupCommand.php | 2 +- system/src/Grav/Console/Cli/CleanCommand.php | 2 +- .../Grav/Console/Cli/ClearCacheCommand.php | 2 +- .../src/Grav/Console/Cli/ComposerCommand.php | 2 +- .../src/Grav/Console/Cli/InstallCommand.php | 2 +- .../src/Grav/Console/Cli/LogViewerCommand.php | 2 +- .../Grav/Console/Cli/NewProjectCommand.php | 2 +- .../Cli/PageSystemValidatorCommand.php | 2 +- .../src/Grav/Console/Cli/SandboxCommand.php | 9 +- .../src/Grav/Console/Cli/SchedulerCommand.php | 4 +- .../src/Grav/Console/Cli/SecurityCommand.php | 2 +- system/src/Grav/Console/Cli/ServerCommand.php | 2 +- .../Grav/Console/Cli/YamlLinterCommand.php | 2 +- system/src/Grav/Console/ConsoleCommand.php | 2 +- system/src/Grav/Console/ConsoleTrait.php | 2 +- .../Grav/Console/Gpm/DirectInstallCommand.php | 2 +- system/src/Grav/Console/Gpm/IndexCommand.php | 4 +- system/src/Grav/Console/Gpm/InfoCommand.php | 2 +- .../src/Grav/Console/Gpm/InstallCommand.php | 4 +- .../Grav/Console/Gpm/SelfupgradeCommand.php | 2 +- .../src/Grav/Console/Gpm/UninstallCommand.php | 2 +- system/src/Grav/Console/Gpm/UpdateCommand.php | 2 +- .../src/Grav/Console/Gpm/VersionCommand.php | 2 +- system/src/Grav/Console/GpmCommand.php | 2 +- system/src/Grav/Console/GravCommand.php | 2 +- .../Grav/Console/Plugin/PluginListCommand.php | 2 +- .../Grav/Console/TerminalObjects/Table.php | 2 +- system/src/Grav/Events/FlexRegisterEvent.php | 2 +- .../Grav/Events/PermissionsRegisterEvent.php | 2 +- system/src/Grav/Events/PluginsLoadedEvent.php | 2 +- system/src/Grav/Events/SessionStartEvent.php | 2 +- system/src/Grav/Framework/Acl/Access.php | 13 +- system/src/Grav/Framework/Acl/Action.php | 21 +- system/src/Grav/Framework/Acl/Permissions.php | 19 +- .../Grav/Framework/Acl/PermissionsReader.php | 19 +- .../Framework/Acl/RecursiveActionIterator.php | 4 +- .../Grav/Framework/Cache/AbstractCache.php | 2 +- .../Framework/Cache/Adapter/ChainCache.php | 10 +- .../Framework/Cache/Adapter/DoctrineCache.php | 10 +- .../Framework/Cache/Adapter/FileCache.php | 21 +- .../Framework/Cache/Adapter/MemoryCache.php | 2 +- .../Framework/Cache/Adapter/SessionCache.php | 2 +- .../Grav/Framework/Cache/CacheInterface.php | 2 +- .../src/Grav/Framework/Cache/CacheTrait.php | 2 +- .../Cache/Exception/CacheException.php | 2 +- .../Exception/InvalidArgumentException.php | 2 +- .../Collection/AbstractFileCollection.php | 10 +- .../Collection/AbstractIndexCollection.php | 75 +- .../Collection/AbstractLazyCollection.php | 14 +- .../Framework/Collection/ArrayCollection.php | 25 +- .../Collection/CollectionInterface.php | 11 +- .../Framework/Collection/FileCollection.php | 8 +- .../Collection/FileCollectionInterface.php | 2 +- .../Grav/Framework/Compat/Serializable.php | 2 +- .../Framework/ContentBlock/ContentBlock.php | 3 +- .../ContentBlock/ContentBlockInterface.php | 2 +- .../Grav/Framework/ContentBlock/HtmlBlock.php | 118 ++- .../ContentBlock/HtmlBlockInterface.php | 39 +- .../Traits/ControllerResponseTrait.php | 22 +- system/src/Grav/Framework/DI/Container.php | 2 +- .../src/Grav/Framework/File/AbstractFile.php | 9 +- system/src/Grav/Framework/File/CsvFile.php | 11 +- system/src/Grav/Framework/File/DataFile.php | 2 +- system/src/Grav/Framework/File/File.php | 11 +- .../File/Formatter/AbstractFormatter.php | 2 +- .../Framework/File/Formatter/CsvFormatter.php | 3 +- .../Framework/File/Formatter/IniFormatter.php | 5 +- .../File/Formatter/JsonFormatter.php | 19 +- .../File/Formatter/MarkdownFormatter.php | 7 +- .../File/Formatter/SerializeFormatter.php | 2 +- .../File/Formatter/YamlFormatter.php | 6 +- system/src/Grav/Framework/File/IniFile.php | 11 +- .../Interfaces/FileFormatterInterface.php | 2 +- .../File/Interfaces/FileInterface.php | 2 +- system/src/Grav/Framework/File/JsonFile.php | 2 +- .../src/Grav/Framework/File/MarkdownFile.php | 11 +- system/src/Grav/Framework/File/YamlFile.php | 11 +- .../Grav/Framework/Filesystem/Filesystem.php | 32 +- .../Interfaces/FilesystemInterface.php | 10 +- system/src/Grav/Framework/Flex/Flex.php | 12 +- .../Grav/Framework/Flex/FlexCollection.php | 34 +- .../src/Grav/Framework/Flex/FlexDirectory.php | 64 +- .../Grav/Framework/Flex/FlexDirectoryForm.php | 13 +- system/src/Grav/Framework/Flex/FlexForm.php | 13 +- .../src/Grav/Framework/Flex/FlexFormFlash.php | 2 +- system/src/Grav/Framework/Flex/FlexIndex.php | 47 +- system/src/Grav/Framework/Flex/FlexObject.php | 83 +- .../Interfaces/FlexAuthorizeInterface.php | 2 +- .../Interfaces/FlexCollectionInterface.php | 4 +- .../Flex/Interfaces/FlexCommonInterface.php | 2 +- .../Interfaces/FlexDirectoryFormInterface.php | 2 +- .../Interfaces/FlexDirectoryInterface.php | 7 +- .../Flex/Interfaces/FlexFormInterface.php | 7 +- .../Flex/Interfaces/FlexIndexInterface.php | 2 +- .../Flex/Interfaces/FlexInterface.php | 4 +- .../Interfaces/FlexObjectFormInterface.php | 2 +- .../Flex/Interfaces/FlexObjectInterface.php | 3 +- .../Flex/Interfaces/FlexStorageInterface.php | 2 +- .../Interfaces/FlexTranslateInterface.php | 2 +- .../Flex/Pages/FlexPageCollection.php | 20 +- .../Framework/Flex/Pages/FlexPageIndex.php | 2 +- .../Framework/Flex/Pages/FlexPageObject.php | 3 +- .../Flex/Pages/Traits/PageAuthorsTrait.php | 2 +- .../Flex/Pages/Traits/PageContentTrait.php | 20 +- .../Flex/Pages/Traits/PageLegacyTrait.php | 14 +- .../Flex/Pages/Traits/PageRoutableTrait.php | 6 +- .../Flex/Pages/Traits/PageTranslateTrait.php | 10 +- .../Storage/AbstractFilesystemStorage.php | 6 +- .../Framework/Flex/Storage/FileStorage.php | 5 +- .../Framework/Flex/Storage/FolderStorage.php | 16 +- .../Framework/Flex/Storage/SimpleStorage.php | 11 +- .../Flex/Traits/FlexAuthorizeTrait.php | 2 +- .../Framework/Flex/Traits/FlexMediaTrait.php | 18 +- .../Flex/Traits/FlexRelatedDirectoryTrait.php | 10 +- system/src/Grav/Framework/Form/FormFlash.php | 4 +- .../src/Grav/Framework/Form/FormFlashFile.php | 7 +- .../Form/Interfaces/FormFactoryInterface.php | 2 +- .../Form/Interfaces/FormFlashInterface.php | 2 +- .../Form/Interfaces/FormInterface.php | 2 +- .../Grav/Framework/Form/Traits/FormTrait.php | 50 +- .../Framework/Interfaces/RenderInterface.php | 2 +- .../Logger/Processors/UserProcessor.php | 34 + .../Interfaces/MediaCollectionInterface.php | 10 +- .../Media/Interfaces/MediaInterface.php | 2 +- .../Interfaces/MediaManipulationInterface.php | 2 +- .../Media/Interfaces/MediaObjectInterface.php | 2 +- system/src/Grav/Framework/Mime/MimeTypes.php | 2 +- .../Object/Access/ArrayAccessTrait.php | 6 +- .../Object/Access/NestedArrayAccessTrait.php | 6 +- .../Access/NestedPropertyCollectionTrait.php | 2 +- .../Object/Access/NestedPropertyTrait.php | 14 +- .../Object/Access/OverloadedPropertyTrait.php | 6 +- .../src/Grav/Framework/Object/ArrayObject.php | 3 +- .../Object/Base/ObjectCollectionTrait.php | 19 +- .../Framework/Object/Base/ObjectTrait.php | 4 +- .../Collection/ObjectExpressionVisitor.php | 7 +- .../NestedObjectCollectionInterface.php | 2 +- .../Interfaces/NestedObjectInterface.php | 2 +- .../Interfaces/ObjectCollectionInterface.php | 2 +- .../Object/Interfaces/ObjectInterface.php | 2 +- .../src/Grav/Framework/Object/LazyObject.php | 3 +- .../Framework/Object/ObjectCollection.php | 15 +- .../src/Grav/Framework/Object/ObjectIndex.php | 32 +- .../Object/Property/ArrayPropertyTrait.php | 4 +- .../Object/Property/LazyPropertyTrait.php | 5 +- .../Object/Property/MixedPropertyTrait.php | 2 +- .../Object/Property/ObjectPropertyTrait.php | 2 +- .../Grav/Framework/Object/PropertyObject.php | 4 +- .../Pagination/AbstractPagination.php | 4 +- .../Pagination/AbstractPaginationPage.php | 4 +- .../Interfaces/PaginationInterface.php | 3 +- .../Interfaces/PaginationPageInterface.php | 2 +- .../Grav/Framework/Pagination/Pagination.php | 2 +- .../Framework/Pagination/PaginationPage.php | 2 +- .../src/Grav/Framework/Psr7/AbstractUri.php | 3 +- system/src/Grav/Framework/Psr7/Request.php | 2 +- system/src/Grav/Framework/Psr7/Response.php | 5 +- .../src/Grav/Framework/Psr7/ServerRequest.php | 16 +- system/src/Grav/Framework/Psr7/Stream.php | 2 +- .../Psr7/Traits/MessageDecoratorTrait.php | 2 +- .../Psr7/Traits/RequestDecoratorTrait.php | 2 +- .../Psr7/Traits/ResponseDecoratorTrait.php | 2 +- .../Traits/ServerRequestDecoratorTrait.php | 2 +- .../Psr7/Traits/StreamDecoratorTrait.php | 3 +- .../Traits/UploadedFileDecoratorTrait.php | 2 +- .../Psr7/Traits/UriDecorationTrait.php | 2 +- .../src/Grav/Framework/Psr7/UploadedFile.php | 2 +- system/src/Grav/Framework/Psr7/Uri.php | 5 +- .../Exception/InvalidArgumentException.php | 2 +- .../Exception/NotFoundException.php | 10 +- .../Exception/NotHandledException.php | 2 +- .../Exception/PageExpiredException.php | 2 +- .../Exception/RequestException.php | 2 +- .../RequestHandler/Middlewares/Exceptions.php | 33 +- .../RequestHandler/RequestHandler.php | 16 +- .../Traits/RequestHandlerTrait.php | 2 +- system/src/Grav/Framework/Route/Route.php | 6 +- .../src/Grav/Framework/Route/RouteFactory.php | 2 +- .../Session/Exceptions/SessionException.php | 2 +- .../src/Grav/Framework/Session/Messages.php | 2 +- system/src/Grav/Framework/Session/Session.php | 25 +- .../Framework/Session/SessionInterface.php | 9 +- system/src/Grav/Framework/Uri/Uri.php | 2 +- system/src/Grav/Framework/Uri/UriFactory.php | 4 +- .../src/Grav/Framework/Uri/UriPartsFilter.php | 2 +- system/src/Grav/Installer/Install.php | 18 +- .../src/Grav/Installer/InstallException.php | 2 +- system/src/Grav/Installer/VersionUpdate.php | 3 +- system/src/Grav/Installer/Versions.php | 2 +- system/src/Grav/Installer/YamlUpdater.php | 3 +- .../DeferredNodeVisitorCompat.php | 21 +- .../index.yaml | 32 - webserver-configs/htaccess.txt | 4 +- webserver-configs/lighttpd.conf | 2 +- webserver-configs/nginx.conf | 4 +- webserver-configs/web.config | 6 +- 473 files changed, 4722 insertions(+), 1766 deletions(-) create mode 100644 system/images/watermark.png create mode 100644 system/src/DOMLettersIterator.php create mode 100644 system/src/DOMWordsIterator.php create mode 100644 system/src/Grav/Common/Assets/BlockAssets.php create mode 100644 system/src/Grav/Common/Assets/InlineJsModule.php create mode 100644 system/src/Grav/Common/Assets/JsModule.php create mode 100644 system/src/Grav/Common/Assets/Link.php create mode 100644 system/src/Grav/Common/HTTP/Client.php create mode 100644 system/src/Grav/Common/HTTP/Response.php create mode 100644 system/src/Grav/Common/Twig/Node/TwigNodeLink.php create mode 100644 system/src/Grav/Common/Twig/TokenParser/TwigTokenParserLink.php create mode 100644 system/src/Grav/Framework/Logger/Processors/UserProcessor.php delete mode 100644 tmp/forms/ma7jev9sc9l2lbinlr2fk21s6o/a4f153b35cbb7417c5038f5663e60d53/index.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e67e47..c394dad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,144 @@ +# v1.7.30 +## 02/07/2022 + +1. [](#new) + * Added twig filter `|field_parent` to get parent field name +2. [](#bugfix) + * Fixed error while deleting retina image in admin + * Fixed "Page Authors" field in Security tab, wrongly loading and saving the value [#3525](https://github.com/getgrav/grav/issues/3525) + * Fixed accounts filter only matches against email address [getgrav/grav-plugin-admin#2224](https://github.com/getgrav/grav-plugin-admin/issues/2224) + +# v1.7.29.1 +## 01/31/2022 + +1. [](#bugfix) + * Fixed `Call to undefined method` error when upgrading from Grav 1.6 [#3523](https://github.com/getgrav/grav/issues/3523) + +# v1.7.29 +## 01/28/2022 + +1. [](#new) + * Added support for registering assets from `HtmlBlock` + * Added unicode-safe `Utils::basename()` and `Utils::pathinfo()` methods +2. [](#improved) + * Improved `Filesystem::basename()` and `Filesystem::pathinfo()` to be unicode-safe + * Made path handling unicode-safe, use new `Utils::basename()` and `Utils::pathinfo()` everywhere +3. [](#bugfix) + * Fixed error on thumbnail image creation + * Fixed MimeType for `gzip` (`application/x-gzip`) + +# v1.7.28 +## 01/24/2022 + +1. [](#new) + * Added links and modules support to `HtmlBlock` class + * Added module support for twig script tag: `{% script module 'theme://js/module.mjs' %}` + * Added twig tag for links: `{% link icon 'theme://images/favicon.png' priority: 20 with { type: 'image/png' } %}` + * Added `HtmlBlock` support for `{% style %}`, `{% script %}` and `{% link %}` tags + * Support for page-level `redirect_default_route` frontmatter header override +3. [](#bugfix) + * Fixed XSS check not detecting escaped `:` + +# v1.7.27.1 +## 01/12/2022 + +3. [](#bugfix) + * Fixed a typo in CSS Asset pipeline that was erroneously joining files with `;` + +# v1.7.27 +## 01/12/2022 + +1. [](#new) + * Support for `YubiKey OTP` 2-Factor authenticator + * Added support for generic `assets.link()` for external references. No pipeline support + * Added support for `assets.addJsModule()` with full pipeline support + * Added `Utils::getExtensionsByMime()` method to get all the registered extensions for the specific mime type + * Added `Media::getRoute()` and `Media::getRawRoute()` methods to get page route if available + * Added `Medium::getAlternatives()` to be able to list all the retina sizes +2. [](#improved) + * Improved `Utils::download()` method to allow overrides on download name, mime and expires header + * Improved `onPageFallBackUrl` event + * Reorganized the Asset system configuration blueprint for clarity +3. [](#bugfix) + * Fixed CLI `--env` and `--lang` options having no effect if they aren't added before all the other options + * Fixed scaled image medium filename when using non-existing retina file + * Fixed an issue with JS `imports` and pipelining Assets + +# v1.7.26.1 +## 01/04/2022 + +3. [](#bugfix) + * Fixed `UserObject::getAccess()` after cloning the object + +# v1.7.26 +## 01/03/2022 + +1. [](#new) + * Made `Grav::redirect()` to accept `Route` class + * Added `translated()` method to `PageTranslateInterface` + * Added second parameter to `UserObject::isMyself()` method + * Added `UserObject::$isAuthorizedCallable` to allow `$user->isAuthorized()` customization + * Use secure session cookies in HTTPS by default (`system.session.secure_https: true`) + * Added new `Plugin::inheritedConfigOption()` function to access plugin specific functions for page overrides +2. [](#improved) + * Upgraded vendor libs for PHP 8.1 compatibility + * Upgraded to **composer v2.1.14** for PHP 8.1 compatibility + * Added third `$name` parameter to `Blueprint::flattenData()` method, useful for flattening repeating data + * `ControllerResponseTrait`: Redirect response should be json if the extension is .json + * When symlinking Grav install, include also tests + * Updated copyright year to `2022` +3. [](#bugfix) + * Fixed bad key lookup in `FlexRelatedDirectoryTrait::getCollectionByProperty()` + * Fixed RequestHandlers `NotFoundException` having empty request + * Block `.json` files in web server configs + * Disabled pretty debug info for Flex as it slows down Twig rendering + * Fixed Twig being very slow when template overrides do not exist + * Fixed `UserObject::$authorizeCallable` binding to the user object + * Fixed `FlexIndex::call()` to return null instead of failing to call undefined method + * Fixed Flex directory configuration creating environment configuration when it should not + +# v1.7.25 +## 11/16/2021 + +1. [](#new) + * Updated phpstan to v1.0 + * Added `FlexObject::getDiff()` to see difference to the saved object +2. [](#improved) + * Use Symfony `dump` instead of PHP's `vardump` in side the `{{ vardump(x) }}` Twig vardump function + * Added `route` and `request` to `onPagesInitialized` event + * Improved page cloning, added method `Page::initialize()` + * Improved `FlexObject::getChanges()`: return changed lists and arrays as whole instead of just changed keys/values + * Improved form validation JSON responses to contain list of failed fields with their error messages + * Improved redirects: send redirect response in JSON if the request was in JSON +3. [](#bugfix) + * Fixed path traversal vulnerability when using `bin/grav server` + * Fixed unescaped error messages in JSON error responses + * Fixed `|t(variable)` twig filter in admin + * Fixed `FlexObject::getChanges()` always returning empty array + * Fixed form validation exceptions to use `400 Bad Request` instead of `500 Internal Server Error` + +# v1.7.24 +## 10/26/2021 + +1. [](#new) + * Added support for image watermarks + * Added support to disable a form, making it readonly +2. [](#improved) + * Flex `$user->authorize()` now checks user groups before `admin.super`, allowing deny rules to work properly +3. [](#bugfix) + * Fixed a bug in `PermissionsReader` in PHP 7.3 + * Fixed `session_store_active` language option (#3464) + * Fixed deprecated warnings on `ArrayAccess` in PHP 8.1 + * Fixed XSS detection with `:` + # v1.7.23 ## 09/29/2021 1. [](#new) * Added method `Pages::referrerRoute()` to get the referrer route and language * Added true unique `Utils::uniqueId()` / `{{ unique_id() }}` utilities with length, prefix, and suffix support + * Added `UserObject::isMyself()` method to check if flex user is currently logged in + * Added support for custom form field options validation with `validate: options: key|ignore` 2. [](#improved) * Replaced GPL `SVG-Sanitizer` with MIT licensed `DOM-Sanitizer` * `Uri::referrer()` now accepts third parameter, if set to `true`, it returns route without base or language code [#3411](https://github.com/getgrav/grav/issues/3411) diff --git a/README.md b/README.md index 31a4a7e..562726d 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,13 @@ [![PHPStan](https://img.shields.io/badge/PHPStan-enabled-brightgreen.svg?style=flat)](https://github.com/phpstan/phpstan) [![Discord](https://img.shields.io/discord/501836936584101899.svg?logo=discord&colorB=728ADA&label=Discord%20Chat)](https://chat.getgrav.org) - [![PHP Tests](https://github.com/getgrav/grav/workflows/PHP%20Tests/badge.svg?branch=develop)](https://github.com/getgrav/grav/actions?query=workflow%3A%22PHP+Tests%22) [![OpenCollective](https://opencollective.com/grav/backers/badge.svg)](#backers) [![OpenCollective](https://opencollective.com/grav/sponsors/badge.svg)](#sponsors) + [![PHP Tests](https://github.com/getgrav/grav/workflows/PHP%20Tests/badge.svg?branch=develop)](https://github.com/getgrav/grav/actions?query=workflow%3A%22PHP+Tests%22) [![OpenCollective](https://opencollective.com/grav/tiers/backers/badge.svg?label=Backers&color=brightgreen)](#backers) [![OpenCollective](https://opencollective.com/grav/tiers/supporters/badge.svg?label=Supporters&color=brightgreen)](#supporters) [![OpenCollective](https://opencollective.com/grav/tiers/sponsors/badge.svg?label=Sponsors&color=brightgreen)](#sponsors) Grav is a **Fast**, **Simple**, and **Flexible**, file-based Web-platform. There is **Zero** installation required. Just extract the ZIP archive, and you are already up and running. It follows similar principles to other flat-file CMS platforms, but has a different design philosophy than most. Grav comes with a powerful **Package Management System** to allow for simple installation and upgrading of plugins and themes, as well as simple updating of Grav itself. The underlying architecture of Grav is designed to use well-established and _best-in-class_ technologies to ensure that Grav is simple to use and easy to extend. Some of these key technologies include: -* [Twig Templating](https://twig.sensiolabs.org/): for powerful control of the user interface +* [Twig Templating](https://twig.symfony.com/): for powerful control of the user interface * [Markdown](https://en.wikipedia.org/wiki/Markdown): for easy content creation * [YAML](https://yaml.org): for simple configuration * [Parsedown](https://parsedown.org/): for fast Markdown and Markdown Extra support @@ -117,12 +117,19 @@ If you discover a possible security issue related to Grav or one of its plugins, * More [Awesome Grav Stuff](https://github.com/getgrav/awesome-grav) # Backers -Support Grav with a monthly donation to help us continue development. [[Become a backer](https://opencollective.com/grav#backer)] +Support Grav with a monthly donation to help us continue development. [[Become a backer](https://opencollective.com/grav/contribute)] + +# Supporters +Support Grav with a monthly donation to help us continue development. [[Become a supporter](https://opencollective.com/grav/contribute)] + + + + # Sponsors -Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/grav#sponsor)] +Support Grav with a yearly donation to help us continue development. [[Become a sponsor](https://opencollective.com/grav/contribute)] diff --git a/assets/.gitkeep b/assets/.gitkeep index 33a9aed..8efbf1f 100644 --- a/assets/.gitkeep +++ b/assets/.gitkeep @@ -1 +1 @@ -/* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. */ +/* @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. */ diff --git a/bin/composer.phar b/bin/composer.phar index 3791ce36ed26e033e485226ed288050ead25032b..f0461904b59bf1fb3f0991c4b79bb066828338dc 100755 GIT binary patch delta 132308 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 delta 85578 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 user/pages/14.api/default.17.md", "post-create-project-cmd": "bin/grav install", - "phpstan": "vendor/bin/phpstan analyse -l 1 -c ./tests/phpstan/phpstan.neon --memory-limit=520M system/src", - "phpstan-framework": "vendor/bin/phpstan analyse -l 3 -c ./tests/phpstan/phpstan.neon --memory-limit=480M system/src/Grav/Framework system/src/Grav/Events system/src/Grav/Installer", + "phpstan": "vendor/bin/phpstan analyse -l 1 -c ./tests/phpstan/phpstan.neon --memory-limit=720M system/src", + "phpstan-framework": "vendor/bin/phpstan analyse -l 4 -c ./tests/phpstan/phpstan.neon --memory-limit=480M system/src/Grav/Framework system/src/Grav/Events system/src/Grav/Installer", "phpstan-plugins": "vendor/bin/phpstan analyse -l 1 -c ./tests/phpstan/plugins.neon --memory-limit=400M user/plugins", "test": "vendor/bin/codecept run unit", "test-windows": "vendor\\bin\\codecept run unit" diff --git a/composer.lock b/composer.lock index 68dc24c..2153094 100644 --- a/composer.lock +++ b/composer.lock @@ -4,68 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "23dd68cea2a3f2d963e57638131f1122", + "content-hash": "1067938388862c52927c6450e8a36df0", "packages": [ - { - "name": "antoligy/dom-string-iterators", - "version": "v1.0.1", - "source": { - "type": "git", - "url": "https://github.com/antoligy/dom-string-iterators.git", - "reference": "fae88f66e1970d68c5585fc42db44f1217bf74e6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/antoligy/dom-string-iterators/zipball/fae88f66e1970d68c5585fc42db44f1217bf74e6", - "reference": "fae88f66e1970d68c5585fc42db44f1217bf74e6", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "CC0-1.0" - ], - "authors": [ - { - "name": "Alex Wilson", - "email": "a@ax.gy" - }, - { - "name": "Kornel Lesinski", - "email": "pornel@pornel.net" - }, - { - "name": "Patrick Galbraith", - "email": "patrick.j.galbraith@gmail.com" - } - ], - "description": "Composer package for DOMWordsIterator and DOMLettersIterator", - "support": { - "issues": "https://github.com/antoligy/dom-string-iterators/issues", - "source": "https://github.com/antoligy/dom-string-iterators/tree/v1.0.1" - }, - "time": "2018-02-03T16:01:11+00:00" - }, { "name": "composer/ca-bundle", - "version": "1.2.10", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/composer/ca-bundle.git", - "reference": "9fdb22c2e97a614657716178093cd1da90a64aa8" + "reference": "4c679186f2aca4ab6a0f1b0b9cf9252decb44d0b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/ca-bundle/zipball/9fdb22c2e97a614657716178093cd1da90a64aa8", - "reference": "9fdb22c2e97a614657716178093cd1da90a64aa8", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/4c679186f2aca4ab6a0f1b0b9cf9252decb44d0b", + "reference": "4c679186f2aca4ab6a0f1b0b9cf9252decb44d0b", "shasum": "" }, "require": { @@ -77,7 +29,7 @@ "phpstan/phpstan": "^0.12.55", "psr/log": "^1.0", "symfony/phpunit-bridge": "^4.2 || ^5", - "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0" + "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0" }, "type": "library", "extra": { @@ -112,7 +64,7 @@ "support": { "irc": "irc://irc.freenode.org/composer", "issues": "https://github.com/composer/ca-bundle/issues", - "source": "https://github.com/composer/ca-bundle/tree/1.2.10" + "source": "https://github.com/composer/ca-bundle/tree/1.3.1" }, "funding": [ { @@ -128,7 +80,7 @@ "type": "tidelift" } ], - "time": "2021-06-07T13:58:28+00:00" + "time": "2021-10-28T20:44:15+00:00" }, { "name": "composer/semver", @@ -596,21 +548,21 @@ }, { "name": "filp/whoops", - "version": "2.14.3", + "version": "2.14.5", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "89584ce67dd32307f1063cc43846674f4679feda" + "reference": "a63e5e8f26ebbebf8ed3c5c691637325512eb0dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/89584ce67dd32307f1063cc43846674f4679feda", - "reference": "89584ce67dd32307f1063cc43846674f4679feda", + "url": "https://api.github.com/repos/filp/whoops/zipball/a63e5e8f26ebbebf8ed3c5c691637325512eb0dc", + "reference": "a63e5e8f26ebbebf8ed3c5c691637325512eb0dc", "shasum": "" }, "require": { "php": "^5.5.9 || ^7.0 || ^8.0", - "psr/log": "^1.0.1" + "psr/log": "^1.0.1 || ^2.0 || ^3.0" }, "require-dev": { "mockery/mockery": "^0.9 || ^1.0", @@ -655,7 +607,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.14.3" + "source": "https://github.com/filp/whoops/tree/2.14.5" }, "funding": [ { @@ -663,7 +615,7 @@ "type": "github" } ], - "time": "2021-09-19T12:00:00+00:00" + "time": "2022-01-07T12:00:00+00:00" }, { "name": "getgrav/cache", @@ -778,16 +730,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "1.8.2", + "version": "1.8.3", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "dc960a912984efb74d0a90222870c72c87f10c91" + "reference": "1afdd860a2566ed3c2b0b4a3de6e23434a79ec85" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/dc960a912984efb74d0a90222870c72c87f10c91", - "reference": "dc960a912984efb74d0a90222870c72c87f10c91", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/1afdd860a2566ed3c2b0b4a3de6e23434a79ec85", + "reference": "1afdd860a2566ed3c2b0b4a3de6e23434a79ec85", "shasum": "" }, "require": { @@ -812,25 +764,46 @@ } }, "autoload": { - "psr-4": { - "GuzzleHttp\\Psr7\\": "src/" - }, "files": [ "src/functions_include.php" - ] + ], + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, { "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", "homepage": "https://github.com/Tobion" } ], @@ -847,28 +820,42 @@ ], "support": { "issues": "https://github.com/guzzle/psr7/issues", - "source": "https://github.com/guzzle/psr7/tree/1.8.2" + "source": "https://github.com/guzzle/psr7/tree/1.8.3" }, - "time": "2021-04-26T09:17:50+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2021-10-05T13:56:00+00:00" }, { "name": "itsgoingd/clockwork", - "version": "v5.1.0", + "version": "v5.1.4", "source": { "type": "git", "url": "https://github.com/itsgoingd/clockwork.git", - "reference": "b963dee47429a49c9669981cfa9a8362ce209278" + "reference": "7252aa771b77ac8678b44290fd7ec7577435cce6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/itsgoingd/clockwork/zipball/b963dee47429a49c9669981cfa9a8362ce209278", - "reference": "b963dee47429a49c9669981cfa9a8362ce209278", + "url": "https://api.github.com/repos/itsgoingd/clockwork/zipball/7252aa771b77ac8678b44290fd7ec7577435cce6", + "reference": "7252aa771b77ac8678b44290fd7ec7577435cce6", "shasum": "" }, "require": { "ext-json": "*", "php": ">=5.6", - "psr/log": "1.*" + "psr/log": "1.* || ^2.0" }, "type": "library", "extra": { @@ -910,7 +897,7 @@ ], "support": { "issues": "https://github.com/itsgoingd/clockwork/issues", - "source": "https://github.com/itsgoingd/clockwork/tree/v5.1.0" + "source": "https://github.com/itsgoingd/clockwork/tree/v5.1.4" }, "funding": [ { @@ -918,20 +905,20 @@ "type": "github" } ], - "time": "2021-08-07T23:04:17+00:00" + "time": "2022-01-30T12:36:18+00:00" }, { "name": "league/climate", - "version": "3.7.0", + "version": "3.8.1", "source": { "type": "git", "url": "https://github.com/thephpleague/climate.git", - "reference": "5c717c3032c5118be7ad2395dbe0813d9894e8c7" + "reference": "22243322c6f049240e0fa6ad6c3873343b6f6055" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/climate/zipball/5c717c3032c5118be7ad2395dbe0813d9894e8c7", - "reference": "5c717c3032c5118be7ad2395dbe0813d9894e8c7", + "url": "https://api.github.com/repos/thephpleague/climate/zipball/22243322c6f049240e0fa6ad6c3873343b6f6055", + "reference": "22243322c6f049240e0fa6ad6c3873343b6f6055", "shasum": "" }, "require": { @@ -981,9 +968,9 @@ ], "support": { "issues": "https://github.com/thephpleague/climate/issues", - "source": "https://github.com/thephpleague/climate/tree/3.7.0" + "source": "https://github.com/thephpleague/climate/tree/3.8.1" }, - "time": "2021-01-10T20:18:52+00:00" + "time": "2022-01-23T14:38:49+00:00" }, { "name": "matthiasmullie/minify", @@ -1118,21 +1105,21 @@ }, { "name": "maximebf/debugbar", - "version": "v1.17.1", + "version": "v1.17.3", "source": { "type": "git", "url": "https://github.com/maximebf/php-debugbar.git", - "reference": "0a3532556be0145603f8a9de23e76dc28eed7054" + "reference": "e8ac3499af0ea5b440908e06cc0abe5898008b3c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/0a3532556be0145603f8a9de23e76dc28eed7054", - "reference": "0a3532556be0145603f8a9de23e76dc28eed7054", + "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/e8ac3499af0ea5b440908e06cc0abe5898008b3c", + "reference": "e8ac3499af0ea5b440908e06cc0abe5898008b3c", "shasum": "" }, "require": { "php": "^7.1|^8", - "psr/log": "^1.0", + "psr/log": "^1|^2|^3", "symfony/var-dumper": "^2.6|^3|^4|^5" }, "require-dev": { @@ -1177,9 +1164,9 @@ ], "support": { "issues": "https://github.com/maximebf/php-debugbar/issues", - "source": "https://github.com/maximebf/php-debugbar/tree/v1.17.1" + "source": "https://github.com/maximebf/php-debugbar/tree/v1.17.3" }, - "time": "2021-08-01T09:19:02+00:00" + "time": "2021-10-19T12:33:27+00:00" }, { "name": "miljar/php-exif", @@ -1329,16 +1316,16 @@ }, { "name": "nyholm/psr7", - "version": "1.4.1", + "version": "1.5.0", "source": { "type": "git", "url": "https://github.com/Nyholm/psr7.git", - "reference": "2212385b47153ea71b1c1b1374f8cb5e4f7892ec" + "reference": "1461e07a0f2a975a52082ca3b769ca912b816226" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Nyholm/psr7/zipball/2212385b47153ea71b1c1b1374f8cb5e4f7892ec", - "reference": "2212385b47153ea71b1c1b1374f8cb5e4f7892ec", + "url": "https://api.github.com/repos/Nyholm/psr7/zipball/1461e07a0f2a975a52082ca3b769ca912b816226", + "reference": "1461e07a0f2a975a52082ca3b769ca912b816226", "shasum": "" }, "require": { @@ -1390,7 +1377,7 @@ ], "support": { "issues": "https://github.com/Nyholm/psr7/issues", - "source": "https://github.com/Nyholm/psr7/tree/1.4.1" + "source": "https://github.com/Nyholm/psr7/tree/1.5.0" }, "funding": [ { @@ -1402,7 +1389,7 @@ "type": "github" } ], - "time": "2021-07-02T08:32:20+00:00" + "time": "2022-02-02T18:37:57+00:00" }, { "name": "nyholm/psr7-server", @@ -1526,29 +1513,29 @@ }, { "name": "pimple/pimple", - "version": "v3.3.1", + "version": "v3.5.0", "source": { "type": "git", "url": "https://github.com/silexphp/Pimple.git", - "reference": "21e45061c3429b1e06233475cc0e1f6fc774d5b0" + "reference": "a94b3a4db7fb774b3d78dad2315ddc07629e1bed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/silexphp/Pimple/zipball/21e45061c3429b1e06233475cc0e1f6fc774d5b0", - "reference": "21e45061c3429b1e06233475cc0e1f6fc774d5b0", + "url": "https://api.github.com/repos/silexphp/Pimple/zipball/a94b3a4db7fb774b3d78dad2315ddc07629e1bed", + "reference": "a94b3a4db7fb774b3d78dad2315ddc07629e1bed", "shasum": "" }, "require": { "php": ">=7.2.5", - "psr/container": "^1.0" + "psr/container": "^1.1 || ^2.0" }, "require-dev": { - "symfony/phpunit-bridge": "^5.0" + "symfony/phpunit-bridge": "^5.4@dev" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.3.x-dev" + "dev-master": "3.4.x-dev" } }, "autoload": { @@ -1573,9 +1560,9 @@ "dependency injection" ], "support": { - "source": "https://github.com/silexphp/Pimple/tree/v3.3.1" + "source": "https://github.com/silexphp/Pimple/tree/v3.5.0" }, - "time": "2020-11-24T20:35:42+00:00" + "time": "2021-10-28T11:13:42+00:00" }, { "name": "psr/cache", @@ -1628,27 +1615,22 @@ }, { "name": "psr/container", - "version": "1.0.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/php-fig/container.git", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f" + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/b7ce3b176482dbbc1245ebf52b181af44c2cf55f", - "reference": "b7ce3b176482dbbc1245ebf52b181af44c2cf55f", + "url": "https://api.github.com/repos/php-fig/container/zipball/8622567409010282b7aeebe4bb841fe98b58dcaf", + "reference": "8622567409010282b7aeebe4bb841fe98b58dcaf", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=7.2.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, "autoload": { "psr-4": { "Psr\\Container\\": "src/" @@ -1661,7 +1643,7 @@ "authors": [ { "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "homepage": "https://www.php-fig.org/" } ], "description": "Common Container Interface (PHP FIG PSR-11)", @@ -1675,9 +1657,9 @@ ], "support": { "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/master" + "source": "https://github.com/php-fig/container/tree/1.1.1" }, - "time": "2017-02-14T16:28:37+00:00" + "time": "2021-03-05T17:36:06+00:00" }, { "name": "psr/http-factory", @@ -2048,16 +2030,16 @@ }, { "name": "rhukster/dom-sanitizer", - "version": "1.0.5", + "version": "1.0.6", "source": { "type": "git", "url": "https://github.com/rhukster/dom-sanitizer.git", - "reference": "836d1dd9808adee0e4bb0ce4cdacc0f9a1497b7e" + "reference": "4db3ef1ac3d5505d044c5eb12aa106ba745bf129" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rhukster/dom-sanitizer/zipball/836d1dd9808adee0e4bb0ce4cdacc0f9a1497b7e", - "reference": "836d1dd9808adee0e4bb0ce4cdacc0f9a1497b7e", + "url": "https://api.github.com/repos/rhukster/dom-sanitizer/zipball/4db3ef1ac3d5505d044c5eb12aa106ba745bf129", + "reference": "4db3ef1ac3d5505d044c5eb12aa106ba745bf129", "shasum": "" }, "require": { @@ -2087,22 +2069,22 @@ "description": "A simple but effective DOM/SVG/MathML Sanitizer for PHP 7.4+", "support": { "issues": "https://github.com/rhukster/dom-sanitizer/issues", - "source": "https://github.com/rhukster/dom-sanitizer/tree/1.0.5" + "source": "https://github.com/rhukster/dom-sanitizer/tree/1.0.6" }, - "time": "2021-09-29T20:14:54+00:00" + "time": "2021-09-30T15:41:33+00:00" }, { "name": "rockettheme/toolbox", - "version": "1.5.10", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/rockettheme/toolbox.git", - "reference": "d9738de013fa12df77754a0f11dded220b246efb" + "reference": "a0eb328b9c416e526c8264b48ccbc1a7519e8618" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/rockettheme/toolbox/zipball/d9738de013fa12df77754a0f11dded220b246efb", - "reference": "d9738de013fa12df77754a0f11dded220b246efb", + "url": "https://api.github.com/repos/rockettheme/toolbox/zipball/a0eb328b9c416e526c8264b48ccbc1a7519e8618", + "reference": "a0eb328b9c416e526c8264b48ccbc1a7519e8618", "shasum": "" }, "require": { @@ -2110,12 +2092,14 @@ "php": ">=5.6.0", "pimple/pimple": "~3.0", "symfony/event-dispatcher": "^3.4|^4.0", + "symfony/polyfill-php80": "^1.23", + "symfony/polyfill-php81": "^1.23", "symfony/yaml": "^3.4|^4.0" }, "require-dev": { - "phpstan/phpstan": "^0.12", - "phpstan/phpstan-deprecation-rules": "^0.12", - "phpunit/phpunit": "~7.0" + "phpstan/phpstan": "^1.2", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpunit/phpunit": "^8.0" }, "type": "library", "autoload": { @@ -2129,7 +2113,10 @@ "RocketTheme\\Toolbox\\ResourceLocator\\": "ResourceLocator/src", "RocketTheme\\Toolbox\\Session\\": "Session/src", "RocketTheme\\Toolbox\\StreamWrapper\\": "StreamWrapper/src" - } + }, + "exclude-from-classmap": [ + "**/tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2143,9 +2130,9 @@ ], "support": { "issues": "https://github.com/rockettheme/toolbox/issues", - "source": "https://github.com/rockettheme/toolbox/tree/1.5.10" + "source": "https://github.com/rockettheme/toolbox/tree/1.6.0" }, - "time": "2021-09-29T16:50:13+00:00" + "time": "2021-12-15T19:20:00+00:00" }, { "name": "seld/cli-prompt", @@ -2204,16 +2191,16 @@ }, { "name": "symfony/console", - "version": "v4.4.30", + "version": "v4.4.37", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "a3f7189a0665ee33b50e9e228c46f50f5acbed22" + "reference": "0259f01dbf9d77badddbbf4c2abb681f24c9cac6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/a3f7189a0665ee33b50e9e228c46f50f5acbed22", - "reference": "a3f7189a0665ee33b50e9e228c46f50f5acbed22", + "url": "https://api.github.com/repos/symfony/console/zipball/0259f01dbf9d77badddbbf4c2abb681f24c9cac6", + "reference": "0259f01dbf9d77badddbbf4c2abb681f24c9cac6", "shasum": "" }, "require": { @@ -2274,7 +2261,7 @@ "description": "Eases the creation of beautiful and testable command line interfaces", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/console/tree/v4.4.30" + "source": "https://github.com/symfony/console/tree/v4.4.37" }, "funding": [ { @@ -2290,25 +2277,25 @@ "type": "tidelift" } ], - "time": "2021-08-25T19:27:26+00:00" + "time": "2022-01-26T16:15:26+00:00" }, { "name": "symfony/contracts", - "version": "v1.1.10", + "version": "v1.1.11", "source": { "type": "git", "url": "https://github.com/symfony/contracts.git", - "reference": "011c20407c4b99d454f44021d023fb39ce23b73d" + "reference": "cf2984f7a99bfae197f9c96f5a16a4041ff6ffb1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/contracts/zipball/011c20407c4b99d454f44021d023fb39ce23b73d", - "reference": "011c20407c4b99d454f44021d023fb39ce23b73d", + "url": "https://api.github.com/repos/symfony/contracts/zipball/cf2984f7a99bfae197f9c96f5a16a4041ff6ffb1", + "reference": "cf2984f7a99bfae197f9c96f5a16a4041ff6ffb1", "shasum": "" }, "require": { "php": ">=7.1.3", - "psr/cache": "^1.0", + "psr/cache": "^1.0|^2.0|^3.0", "psr/container": "^1.0" }, "replace": { @@ -2332,7 +2319,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.1-dev" + "dev-main": "1.1-dev" } }, "autoload": { @@ -2368,7 +2355,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/contracts/tree/v1.1.10" + "source": "https://github.com/symfony/contracts/tree/v1.1.11" }, "funding": [ { @@ -2384,20 +2371,20 @@ "type": "tidelift" } ], - "time": "2020-09-02T16:08:58+00:00" + "time": "2021-11-04T13:32:43+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v4.4.30", + "version": "v4.4.37", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "2fe81680070043c4c80e7cedceb797e34f377bac" + "reference": "3ccfcfb96ecce1217d7b0875a0736976bc6e63dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/2fe81680070043c4c80e7cedceb797e34f377bac", - "reference": "2fe81680070043c4c80e7cedceb797e34f377bac", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/3ccfcfb96ecce1217d7b0875a0736976bc6e63dc", + "reference": "3ccfcfb96ecce1217d7b0875a0736976bc6e63dc", "shasum": "" }, "require": { @@ -2452,7 +2439,7 @@ "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v4.4.30" + "source": "https://github.com/symfony/event-dispatcher/tree/v4.4.37" }, "funding": [ { @@ -2468,20 +2455,20 @@ "type": "tidelift" } ], - "time": "2021-08-04T20:31:23+00:00" + "time": "2022-01-02T09:41:36+00:00" }, { "name": "symfony/http-client", - "version": "v4.4.31", + "version": "v4.4.37", "source": { "type": "git", "url": "https://github.com/symfony/http-client.git", - "reference": "6b900ffa399e25203f30f79f6f4a56b89eee14c2" + "reference": "8129ccd6233338e1d495b7734c003053766cb262" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-client/zipball/6b900ffa399e25203f30f79f6f4a56b89eee14c2", - "reference": "6b900ffa399e25203f30f79f6f4a56b89eee14c2", + "url": "https://api.github.com/repos/symfony/http-client/zipball/8129ccd6233338e1d495b7734c003053766cb262", + "reference": "8129ccd6233338e1d495b7734c003053766cb262", "shasum": "" }, "require": { @@ -2533,7 +2520,7 @@ "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-client/tree/v4.4.31" + "source": "https://github.com/symfony/http-client/tree/v4.4.37" }, "funding": [ { @@ -2549,25 +2536,28 @@ "type": "tidelift" } ], - "time": "2021-09-06T10:00:00+00:00" + "time": "2022-01-19T13:29:07+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.23.0", + "version": "v1.24.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce" + "reference": "30885182c981ab175d4d034db0f6f469898070ab" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce", - "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab", + "reference": "30885182c981ab175d4d034db0f6f469898070ab", "shasum": "" }, "require": { "php": ">=7.1" }, + "provide": { + "ext-ctype": "*" + }, "suggest": { "ext-ctype": "For best performance" }, @@ -2612,7 +2602,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.23.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.24.0" }, "funding": [ { @@ -2628,25 +2618,28 @@ "type": "tidelift" } ], - "time": "2021-02-19T12:13:01+00:00" + "time": "2021-10-20T20:35:02+00:00" }, { "name": "symfony/polyfill-iconv", - "version": "v1.23.0", + "version": "v1.24.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-iconv.git", - "reference": "63b5bb7db83e5673936d6e3b8b3e022ff6474933" + "reference": "f1aed619e28cb077fc83fac8c4c0383578356e40" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/63b5bb7db83e5673936d6e3b8b3e022ff6474933", - "reference": "63b5bb7db83e5673936d6e3b8b3e022ff6474933", + "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/f1aed619e28cb077fc83fac8c4c0383578356e40", + "reference": "f1aed619e28cb077fc83fac8c4c0383578356e40", "shasum": "" }, "require": { "php": ">=7.1" }, + "provide": { + "ext-iconv": "*" + }, "suggest": { "ext-iconv": "For best performance" }, @@ -2661,12 +2654,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Iconv\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Iconv\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2692,7 +2685,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-iconv/tree/v1.23.0" + "source": "https://github.com/symfony/polyfill-iconv/tree/v1.24.0" }, "funding": [ { @@ -2708,25 +2701,28 @@ "type": "tidelift" } ], - "time": "2021-05-27T09:27:20+00:00" + "time": "2022-01-04T09:04:05+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.23.1", + "version": "v1.24.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6" + "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9174a3d80210dca8daa7f31fec659150bbeabfc6", - "reference": "9174a3d80210dca8daa7f31fec659150bbeabfc6", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825", + "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825", "shasum": "" }, "require": { "php": ">=7.1" }, + "provide": { + "ext-mbstring": "*" + }, "suggest": { "ext-mbstring": "For best performance" }, @@ -2772,7 +2768,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.23.1" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.24.0" }, "funding": [ { @@ -2788,11 +2784,11 @@ "type": "tidelift" } ], - "time": "2021-05-27T12:26:48+00:00" + "time": "2021-11-30T18:21:41+00:00" }, { "name": "symfony/polyfill-php74", - "version": "v1.23.0", + "version": "v1.24.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php74.git", @@ -2818,12 +2814,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php74\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Php74\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2852,7 +2848,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php74/tree/v1.23.0" + "source": "https://github.com/symfony/polyfill-php74/tree/v1.24.0" }, "funding": [ { @@ -2872,16 +2868,16 @@ }, { "name": "symfony/polyfill-php80", - "version": "v1.23.1", + "version": "v1.24.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be" + "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/1100343ed1a92e3a38f9ae122fc0eb21602547be", - "reference": "1100343ed1a92e3a38f9ae122fc0eb21602547be", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/57b712b08eddb97c762a8caa32c84e037892d2e9", + "reference": "57b712b08eddb97c762a8caa32c84e037892d2e9", "shasum": "" }, "require": { @@ -2898,12 +2894,12 @@ } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, "files": [ "bootstrap.php" ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, "classmap": [ "Resources/stubs" ] @@ -2935,7 +2931,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.1" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.24.0" }, "funding": [ { @@ -2951,20 +2947,99 @@ "type": "tidelift" } ], - "time": "2021-07-28T13:41:28+00:00" + "time": "2021-09-13T13:58:33+00:00" }, { - "name": "symfony/process", - "version": "v4.4.30", + "name": "symfony/polyfill-php81", + "version": "v1.24.0", "source": { "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "13d3161ef63a8ec21eeccaaf9a4d7f784a87a97d" + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/13d3161ef63a8ec21eeccaaf9a4d7f784a87a97d", - "reference": "13d3161ef63a8ec21eeccaaf9a4d7f784a87a97d", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/5de4ba2d41b15f9bd0e19b2ab9674135813ec98f", + "reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php81\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php81/tree/v1.24.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-09-13T13:58:11+00:00" + }, + { + "name": "symfony/process", + "version": "v4.4.37", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "b2d924e5a4cb284f293d5092b1dbf0d364cb8b67" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/b2d924e5a4cb284f293d5092b1dbf0d364cb8b67", + "reference": "b2d924e5a4cb284f293d5092b1dbf0d364cb8b67", "shasum": "" }, "require": { @@ -2997,7 +3072,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v4.4.30" + "source": "https://github.com/symfony/process/tree/v4.4.37" }, "funding": [ { @@ -3013,20 +3088,20 @@ "type": "tidelift" } ], - "time": "2021-08-04T20:31:23+00:00" + "time": "2022-01-27T17:14:04+00:00" }, { "name": "symfony/var-dumper", - "version": "v4.4.31", + "version": "v4.4.37", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "1f12cc0c2e880a5f39575c19af81438464717839" + "reference": "e74eee4ec02de71db3d60151aa5b203c990556df" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/1f12cc0c2e880a5f39575c19af81438464717839", - "reference": "1f12cc0c2e880a5f39575c19af81438464717839", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/e74eee4ec02de71db3d60151aa5b203c990556df", + "reference": "e74eee4ec02de71db3d60151aa5b203c990556df", "shasum": "" }, "require": { @@ -3086,7 +3161,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v4.4.31" + "source": "https://github.com/symfony/var-dumper/tree/v4.4.37" }, "funding": [ { @@ -3102,20 +3177,20 @@ "type": "tidelift" } ], - "time": "2021-09-24T15:30:11+00:00" + "time": "2022-01-02T09:41:36+00:00" }, { "name": "symfony/yaml", - "version": "v4.4.29", + "version": "v4.4.37", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "3abcc4db06d4e776825eaa3ed8ad924d5bc7432a" + "reference": "d7f637cc0f0cc14beb0984f2bb50da560b271311" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/3abcc4db06d4e776825eaa3ed8ad924d5bc7432a", - "reference": "3abcc4db06d4e776825eaa3ed8ad924d5bc7432a", + "url": "https://api.github.com/repos/symfony/yaml/zipball/d7f637cc0f0cc14beb0984f2bb50da560b271311", + "reference": "d7f637cc0f0cc14beb0984f2bb50da560b271311", "shasum": "" }, "require": { @@ -3157,7 +3232,7 @@ "description": "Loads and dumps YAML files", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/yaml/tree/v4.4.29" + "source": "https://github.com/symfony/yaml/tree/v4.4.37" }, "funding": [ { @@ -3173,20 +3248,20 @@ "type": "tidelift" } ], - "time": "2021-07-27T16:19:30+00:00" + "time": "2022-01-24T20:11:01+00:00" }, { "name": "twig/twig", - "version": "v1.44.5", + "version": "v1.44.6", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "dd4353357c5a116322e92a00d16043a31881a81e" + "reference": "ae39480f010ef88adc7938503c9b02d3baf2f3b3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/dd4353357c5a116322e92a00d16043a31881a81e", - "reference": "dd4353357c5a116322e92a00d16043a31881a81e", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/ae39480f010ef88adc7938503c9b02d3baf2f3b3", + "reference": "ae39480f010ef88adc7938503c9b02d3baf2f3b3", "shasum": "" }, "require": { @@ -3239,7 +3314,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v1.44.5" + "source": "https://github.com/twigphp/Twig/tree/v1.44.6" }, "funding": [ { @@ -3251,20 +3326,20 @@ "type": "tidelift" } ], - "time": "2021-09-17T08:35:19+00:00" + "time": "2021-11-25T13:31:46+00:00" }, { "name": "willdurand/negotiation", - "version": "3.0.0", + "version": "3.1.0", "source": { "type": "git", "url": "https://github.com/willdurand/Negotiation.git", - "reference": "04e14f38d4edfcc974114a07d2777d90c98f3d9c" + "reference": "68e9ea0553ef6e2ee8db5c1d98829f111e623ec2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/willdurand/Negotiation/zipball/04e14f38d4edfcc974114a07d2777d90c98f3d9c", - "reference": "04e14f38d4edfcc974114a07d2777d90c98f3d9c", + "url": "https://api.github.com/repos/willdurand/Negotiation/zipball/68e9ea0553ef6e2ee8db5c1d98829f111e623ec2", + "reference": "68e9ea0553ef6e2ee8db5c1d98829f111e623ec2", "shasum": "" }, "require": { @@ -3305,33 +3380,32 @@ ], "support": { "issues": "https://github.com/willdurand/Negotiation/issues", - "source": "https://github.com/willdurand/Negotiation/tree/3.0.0" + "source": "https://github.com/willdurand/Negotiation/tree/3.1.0" }, - "time": "2020-09-25T08:01:41+00:00" + "time": "2022-01-30T20:08:53+00:00" } ], "packages-dev": [ { "name": "behat/gherkin", - "version": "v4.8.0", + "version": "v4.9.0", "source": { "type": "git", "url": "https://github.com/Behat/Gherkin.git", - "reference": "2391482cd003dfdc36b679b27e9f5326bd656acd" + "reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Behat/Gherkin/zipball/2391482cd003dfdc36b679b27e9f5326bd656acd", - "reference": "2391482cd003dfdc36b679b27e9f5326bd656acd", + "url": "https://api.github.com/repos/Behat/Gherkin/zipball/0bc8d1e30e96183e4f36db9dc79caead300beff4", + "reference": "0bc8d1e30e96183e4f36db9dc79caead300beff4", "shasum": "" }, "require": { "php": "~7.2|~8.0" }, "require-dev": { - "cucumber/cucumber": "dev-gherkin-16.0.0", + "cucumber/cucumber": "dev-gherkin-22.0.0", "phpunit/phpunit": "~8|~9", - "symfony/phpunit-bridge": "~3|~4|~5", "symfony/yaml": "~3|~4|~5" }, "suggest": { @@ -3340,7 +3414,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.4-dev" + "dev-master": "4.x-dev" } }, "autoload": { @@ -3371,29 +3445,29 @@ ], "support": { "issues": "https://github.com/Behat/Gherkin/issues", - "source": "https://github.com/Behat/Gherkin/tree/v4.8.0" + "source": "https://github.com/Behat/Gherkin/tree/v4.9.0" }, - "time": "2021-02-04T12:44:21+00:00" + "time": "2021-10-12T13:05:09+00:00" }, { "name": "codeception/codeception", - "version": "4.1.22", + "version": "4.1.29", "source": { "type": "git", "url": "https://github.com/Codeception/Codeception.git", - "reference": "9777ec3690ceedc4bce2ed13af7af4ca4ee3088f" + "reference": "f8dec8f2bf5347cc596aaf141753f4fb2504c17c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/9777ec3690ceedc4bce2ed13af7af4ca4ee3088f", - "reference": "9777ec3690ceedc4bce2ed13af7af4ca4ee3088f", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/f8dec8f2bf5347cc596aaf141753f4fb2504c17c", + "reference": "f8dec8f2bf5347cc596aaf141753f4fb2504c17c", "shasum": "" }, "require": { "behat/gherkin": "^4.4.0", - "codeception/lib-asserts": "^1.0", + "codeception/lib-asserts": "^1.0 | 2.0.*@dev", "codeception/phpunit-wrapper": ">6.0.15 <6.1.0 | ^6.6.1 | ^7.7.1 | ^8.1.1 | ^9.0", - "codeception/stub": "^2.0 | ^3.0", + "codeception/stub": "^2.0 | ^3.0 | ^4.0", "ext-curl": "*", "ext-json": "*", "ext-mbstring": "*", @@ -3406,11 +3480,11 @@ "symfony/yaml": ">=2.7 <6.0" }, "require-dev": { - "codeception/module-asserts": "1.*@dev", - "codeception/module-cli": "1.*@dev", - "codeception/module-db": "1.*@dev", - "codeception/module-filesystem": "1.*@dev", - "codeception/module-phpbrowser": "1.*@dev", + "codeception/module-asserts": "^1.0 | 2.0.*@dev", + "codeception/module-cli": "^1.0 | 2.0.*@dev", + "codeception/module-db": "^1.0 | 2.0.*@dev", + "codeception/module-filesystem": "^1.0 | 2.0.*@dev", + "codeception/module-phpbrowser": "^1.0 | 2.0.*@dev", "codeception/specify": "~0.3", "codeception/util-universalframework": "*@dev", "monolog/monolog": "~1.8", @@ -3433,6 +3507,9 @@ "branch-alias": [] }, "autoload": { + "files": [ + "functions.php" + ], "psr-4": { "Codeception\\": "src/Codeception", "Codeception\\Extension\\": "ext" @@ -3460,7 +3537,7 @@ ], "support": { "issues": "https://github.com/Codeception/Codeception/issues", - "source": "https://github.com/Codeception/Codeception/tree/4.1.22" + "source": "https://github.com/Codeception/Codeception/tree/4.1.29" }, "funding": [ { @@ -3468,7 +3545,7 @@ "type": "open_collective" } ], - "time": "2021-08-06T17:15:34+00:00" + "time": "2022-01-29T16:56:03+00:00" }, { "name": "codeception/lib-asserts", @@ -3703,16 +3780,16 @@ }, { "name": "codeception/phpunit-wrapper", - "version": "9.0.6", + "version": "9.0.7", "source": { "type": "git", "url": "https://github.com/Codeception/phpunit-wrapper.git", - "reference": "b0c06abb3181eedca690170f7ed0fd26a70bfacc" + "reference": "7d6b1a5ea4ed28d010e5d36b993db813eb49710b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/b0c06abb3181eedca690170f7ed0fd26a70bfacc", - "reference": "b0c06abb3181eedca690170f7ed0fd26a70bfacc", + "url": "https://api.github.com/repos/Codeception/phpunit-wrapper/zipball/7d6b1a5ea4ed28d010e5d36b993db813eb49710b", + "reference": "7d6b1a5ea4ed28d010e5d36b993db813eb49710b", "shasum": "" }, "require": { @@ -3746,9 +3823,9 @@ "description": "PHPUnit classes used by Codeception", "support": { "issues": "https://github.com/Codeception/phpunit-wrapper/issues", - "source": "https://github.com/Codeception/phpunit-wrapper/tree/9.0.6" + "source": "https://github.com/Codeception/phpunit-wrapper/tree/9.0.7" }, - "time": "2020-12-28T13:59:47+00:00" + "time": "2022-01-26T14:43:10+00:00" }, { "name": "codeception/stub", @@ -3907,24 +3984,25 @@ }, { "name": "guzzlehttp/guzzle", - "version": "7.3.0", + "version": "7.4.1", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "7008573787b430c1c1f650e3722d9bba59967628" + "reference": "ee0a041b1760e6a53d2a39c8c34115adc2af2c79" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7008573787b430c1c1f650e3722d9bba59967628", - "reference": "7008573787b430c1c1f650e3722d9bba59967628", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ee0a041b1760e6a53d2a39c8c34115adc2af2c79", + "reference": "ee0a041b1760e6a53d2a39c8c34115adc2af2c79", "shasum": "" }, "require": { "ext-json": "*", - "guzzlehttp/promises": "^1.4", - "guzzlehttp/psr7": "^1.7 || ^2.0", + "guzzlehttp/promises": "^1.5", + "guzzlehttp/psr7": "^1.8.3 || ^2.1", "php": "^7.2.5 || ^8.0", - "psr/http-client": "^1.0" + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" }, "provide": { "psr/http-client-implementation": "1.0" @@ -3934,7 +4012,7 @@ "ext-curl": "*", "php-http/client-integration-tests": "^3.0", "phpunit/phpunit": "^8.5.5 || ^9.3.5", - "psr/log": "^1.1" + "psr/log": "^1.1 || ^2.0 || ^3.0" }, "suggest": { "ext-curl": "Required for CURL handler support", @@ -3944,7 +4022,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "7.3-dev" + "dev-master": "7.4-dev" } }, "autoload": { @@ -3960,19 +4038,43 @@ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, { "name": "Márk Sági-Kazár", "email": "mark.sagikazar@gmail.com", - "homepage": "https://sagikazarmark.hu" + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], "description": "Guzzle is a PHP HTTP client library", - "homepage": "http://guzzlephp.org/", "keywords": [ "client", "curl", @@ -3986,7 +4088,7 @@ ], "support": { "issues": "https://github.com/guzzle/guzzle/issues", - "source": "https://github.com/guzzle/guzzle/tree/7.3.0" + "source": "https://github.com/guzzle/guzzle/tree/7.4.1" }, "funding": [ { @@ -3998,28 +4100,24 @@ "type": "github" }, { - "url": "https://github.com/alexeyshockov", - "type": "github" - }, - { - "url": "https://github.com/gmponos", - "type": "github" + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" } ], - "time": "2021-03-23T11:33:13+00:00" + "time": "2021-12-06T18:43:05+00:00" }, { "name": "guzzlehttp/promises", - "version": "1.4.1", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/guzzle/promises.git", - "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d" + "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/promises/zipball/8e7d04f1f6450fef59366c399cfad4b9383aa30d", - "reference": "8e7d04f1f6450fef59366c399cfad4b9383aa30d", + "url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da", + "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da", "shasum": "" }, "require": { @@ -4031,7 +4129,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "1.5-dev" } }, "autoload": { @@ -4047,10 +4145,25 @@ "MIT" ], "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, { "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], "description": "Guzzle promises library", @@ -4059,9 +4172,23 @@ ], "support": { "issues": "https://github.com/guzzle/promises/issues", - "source": "https://github.com/guzzle/promises/tree/1.4.1" + "source": "https://github.com/guzzle/promises/tree/1.5.1" }, - "time": "2021-03-07T09:25:29+00:00" + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2021-10-22T20:56:57+00:00" }, { "name": "myclabs/deep-copy", @@ -4080,9 +4207,6 @@ "require": { "php": "^7.1 || ^8.0" }, - "replace": { - "myclabs/deep-copy": "self.version" - }, "require-dev": { "doctrine/collections": "^1.0", "doctrine/common": "^2.6", @@ -4090,12 +4214,12 @@ }, "type": "library", "autoload": { - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - }, "files": [ "src/DeepCopy/deep_copy.php" - ] + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -4123,16 +4247,16 @@ }, { "name": "nikic/php-parser", - "version": "v4.13.0", + "version": "v4.13.2", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "50953a2691a922aa1769461637869a0a2faa3f53" + "reference": "210577fe3cf7badcc5814d99455df46564f3c077" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/50953a2691a922aa1769461637869a0a2faa3f53", - "reference": "50953a2691a922aa1769461637869a0a2faa3f53", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077", + "reference": "210577fe3cf7badcc5814d99455df46564f3c077", "shasum": "" }, "require": { @@ -4173,9 +4297,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2" }, - "time": "2021-09-20T12:20:58+00:00" + "time": "2021-11-30T19:35:32+00:00" }, { "name": "phar-io/manifest", @@ -4343,16 +4467,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.2.2", + "version": "5.3.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556" + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556", - "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", + "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", "shasum": "" }, "require": { @@ -4363,7 +4487,8 @@ "webmozart/assert": "^1.9.1" }, "require-dev": { - "mockery/mockery": "~1.3.2" + "mockery/mockery": "~1.3.2", + "psalm/phar": "^4.8" }, "type": "library", "extra": { @@ -4393,22 +4518,22 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/master" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" }, - "time": "2020-09-03T19:13:55+00:00" + "time": "2021-10-19T17:43:47+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "1.5.0", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f" + "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/30f38bffc6f24293dadd1823936372dfa9e86e2f", - "reference": "30f38bffc6f24293dadd1823936372dfa9e86e2f", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/93ebd0014cab80c4ea9f5e297ea48672f1b87706", + "reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706", "shasum": "" }, "require": { @@ -4443,22 +4568,22 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.5.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.0" }, - "time": "2021-09-17T15:28:14+00:00" + "time": "2022-01-04T19:58:01+00:00" }, { "name": "phpspec/prophecy", - "version": "1.14.0", + "version": "v1.15.0", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e" + "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", - "reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13", + "reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13", "shasum": "" }, "require": { @@ -4510,22 +4635,22 @@ ], "support": { "issues": "https://github.com/phpspec/prophecy/issues", - "source": "https://github.com/phpspec/prophecy/tree/1.14.0" + "source": "https://github.com/phpspec/prophecy/tree/v1.15.0" }, - "time": "2021-09-10T09:02:12+00:00" + "time": "2021-12-08T12:19:24+00:00" }, { "name": "phpstan/phpstan", - "version": "0.12.99", + "version": "1.4.6", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7" + "reference": "8a7761f1c520e0dad6e04d862fdc697445457cfe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b4d40f1d759942f523be267a1bab6884f46ca3f7", - "reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/8a7761f1c520e0dad6e04d862fdc697445457cfe", + "reference": "8a7761f1c520e0dad6e04d862fdc697445457cfe", "shasum": "" }, "require": { @@ -4541,7 +4666,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "0.12-dev" + "dev-master": "1.4-dev" } }, "autoload": { @@ -4556,7 +4681,7 @@ "description": "PHPStan - PHP Static Analysis Tool", "support": { "issues": "https://github.com/phpstan/phpstan/issues", - "source": "https://github.com/phpstan/phpstan/tree/0.12.99" + "source": "https://github.com/phpstan/phpstan/tree/1.4.6" }, "funding": [ { @@ -4576,36 +4701,35 @@ "type": "tidelift" } ], - "time": "2021-09-12T20:09:55+00:00" + "time": "2022-02-06T12:56:13+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", - "version": "0.12.6", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan-deprecation-rules.git", - "reference": "46dbd43c2db973d2876d6653e53f5c2cc3a01fbb" + "reference": "e5ccafb0dd8d835dd65d8d7a1a0d2b1b75414682" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/46dbd43c2db973d2876d6653e53f5c2cc3a01fbb", - "reference": "46dbd43c2db973d2876d6653e53f5c2cc3a01fbb", + "url": "https://api.github.com/repos/phpstan/phpstan-deprecation-rules/zipball/e5ccafb0dd8d835dd65d8d7a1a0d2b1b75414682", + "reference": "e5ccafb0dd8d835dd65d8d7a1a0d2b1b75414682", "shasum": "" }, "require": { "php": "^7.1 || ^8.0", - "phpstan/phpstan": "^0.12.60" + "phpstan/phpstan": "^1.0" }, "require-dev": { - "phing/phing": "^2.16.3", "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/phpstan-phpunit": "^0.12", - "phpunit/phpunit": "^7.5.20" + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^9.5" }, "type": "phpstan-extension", "extra": { "branch-alias": { - "dev-master": "0.12-dev" + "dev-master": "1.0-dev" }, "phpstan": { "includes": [ @@ -4625,29 +4749,29 @@ "description": "PHPStan rules for detecting usage of deprecated classes, methods, properties, constants and traits.", "support": { "issues": "https://github.com/phpstan/phpstan-deprecation-rules/issues", - "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/0.12.6" + "source": "https://github.com/phpstan/phpstan-deprecation-rules/tree/1.0.0" }, - "time": "2020-12-13T10:20:54+00:00" + "time": "2021-09-23T11:02:21+00:00" }, { "name": "phpunit/php-code-coverage", - "version": "9.2.7", + "version": "9.2.10", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218" + "reference": "d5850aaf931743067f4bfc1ae4cbd06468400687" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d4c798ed8d51506800b441f7a13ecb0f76f12218", - "reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d5850aaf931743067f4bfc1ae4cbd06468400687", + "reference": "d5850aaf931743067f4bfc1ae4cbd06468400687", "shasum": "" }, "require": { "ext-dom": "*", "ext-libxml": "*", "ext-xmlwriter": "*", - "nikic/php-parser": "^4.12.0", + "nikic/php-parser": "^4.13.0", "php": ">=7.3", "phpunit/php-file-iterator": "^3.0.3", "phpunit/php-text-template": "^2.0.2", @@ -4696,7 +4820,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.7" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.10" }, "funding": [ { @@ -4704,20 +4828,20 @@ "type": "github" } ], - "time": "2021-09-17T05:39:03+00:00" + "time": "2021-12-05T09:12:13+00:00" }, { "name": "phpunit/php-file-iterator", - "version": "3.0.5", + "version": "3.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8" + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/aa4be8575f26070b100fccb67faabb28f21f66f8", - "reference": "aa4be8575f26070b100fccb67faabb28f21f66f8", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", + "reference": "cf1c2e7c203ac650e352f4cc675a7021e7d1b3cf", "shasum": "" }, "require": { @@ -4756,7 +4880,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.5" + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/3.0.6" }, "funding": [ { @@ -4764,7 +4888,7 @@ "type": "github" } ], - "time": "2020-09-28T05:57:25+00:00" + "time": "2021-12-02T12:48:52+00:00" }, { "name": "phpunit/php-invoker", @@ -4949,16 +5073,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.5.10", + "version": "9.5.13", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a" + "reference": "597cb647654ede35e43b137926dfdfef0fb11743" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c814a05837f2edb0d1471d6e3f4ab3501ca3899a", - "reference": "c814a05837f2edb0d1471d6e3f4ab3501ca3899a", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/597cb647654ede35e43b137926dfdfef0fb11743", + "reference": "597cb647654ede35e43b137926dfdfef0fb11743", "shasum": "" }, "require": { @@ -5036,11 +5160,11 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.10" + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.13" }, "funding": [ { - "url": "https://phpunit.de/donate.html", + "url": "https://phpunit.de/sponsors.html", "type": "custom" }, { @@ -5048,7 +5172,7 @@ "type": "github" } ], - "time": "2021-09-25T07:38:51+00:00" + "time": "2022-01-24T07:33:35+00:00" }, { "name": "psr/http-client", @@ -5531,16 +5655,16 @@ }, { "name": "sebastian/exporter", - "version": "4.0.3", + "version": "4.0.4", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65" + "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/d89cc98761b8cb5a1a235a6b703ae50d34080e65", - "reference": "d89cc98761b8cb5a1a235a6b703ae50d34080e65", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/65e8b7db476c5dd267e65eea9cab77584d3cfff9", + "reference": "65e8b7db476c5dd267e65eea9cab77584d3cfff9", "shasum": "" }, "require": { @@ -5589,14 +5713,14 @@ } ], "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "http://www.github.com/sebastianbergmann/exporter", + "homepage": "https://www.github.com/sebastianbergmann/exporter", "keywords": [ "export", "exporter" ], "support": { "issues": "https://github.com/sebastianbergmann/exporter/issues", - "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.3" + "source": "https://github.com/sebastianbergmann/exporter/tree/4.0.4" }, "funding": [ { @@ -5604,7 +5728,7 @@ "type": "github" } ], - "time": "2020-09-28T05:24:23+00:00" + "time": "2021-11-11T14:18:36+00:00" }, { "name": "sebastian/global-state", @@ -6068,28 +6192,28 @@ }, { "name": "symfony/browser-kit", - "version": "v5.3.4", + "version": "v5.4.3", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "c1e3f64fcc631c96e2c5843b666db66679ced11c" + "reference": "18e73179c6a33d520de1b644941eba108dd811ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/c1e3f64fcc631c96e2c5843b666db66679ced11c", - "reference": "c1e3f64fcc631c96e2c5843b666db66679ced11c", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/18e73179c6a33d520de1b644941eba108dd811ad", + "reference": "18e73179c6a33d520de1b644941eba108dd811ad", "shasum": "" }, "require": { "php": ">=7.2.5", - "symfony/dom-crawler": "^4.4|^5.0", + "symfony/dom-crawler": "^4.4|^5.0|^6.0", "symfony/polyfill-php80": "^1.16" }, "require-dev": { - "symfony/css-selector": "^4.4|^5.0", - "symfony/http-client": "^4.4|^5.0", - "symfony/mime": "^4.4|^5.0", - "symfony/process": "^4.4|^5.0" + "symfony/css-selector": "^4.4|^5.0|^6.0", + "symfony/http-client": "^4.4|^5.0|^6.0", + "symfony/mime": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0" }, "suggest": { "symfony/process": "" @@ -6120,7 +6244,7 @@ "description": "Simulates the behavior of a web browser, allowing you to make requests, click on links and submit forms programmatically", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/browser-kit/tree/v5.3.4" + "source": "https://github.com/symfony/browser-kit/tree/v5.4.3" }, "funding": [ { @@ -6136,20 +6260,20 @@ "type": "tidelift" } ], - "time": "2021-07-21T12:40:44+00:00" + "time": "2022-01-02T09:53:40+00:00" }, { "name": "symfony/css-selector", - "version": "v5.3.4", + "version": "v5.4.3", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "7fb120adc7f600a59027775b224c13a33530dd90" + "reference": "b0a190285cd95cb019237851205b8140ef6e368e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/7fb120adc7f600a59027775b224c13a33530dd90", - "reference": "7fb120adc7f600a59027775b224c13a33530dd90", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/b0a190285cd95cb019237851205b8140ef6e368e", + "reference": "b0a190285cd95cb019237851205b8140ef6e368e", "shasum": "" }, "require": { @@ -6186,7 +6310,7 @@ "description": "Converts CSS selectors to XPath expressions", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/css-selector/tree/v5.3.4" + "source": "https://github.com/symfony/css-selector/tree/v5.4.3" }, "funding": [ { @@ -6202,20 +6326,20 @@ "type": "tidelift" } ], - "time": "2021-07-21T12:38:00+00:00" + "time": "2022-01-02T09:53:40+00:00" }, { "name": "symfony/deprecation-contracts", - "version": "v2.4.0", + "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627" + "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627", - "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8", + "reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8", "shasum": "" }, "require": { @@ -6224,7 +6348,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.4-dev" + "dev-main": "2.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -6253,7 +6377,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.0" }, "funding": [ { @@ -6269,25 +6393,25 @@ "type": "tidelift" } ], - "time": "2021-03-23T23:28:01+00:00" + "time": "2021-07-12T14:48:14+00:00" }, { "name": "symfony/dom-crawler", - "version": "v5.3.7", + "version": "v5.4.3", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "c7eef3a60ccfdd8eafe07f81652e769ac9c7146c" + "reference": "2634381fdf27a2a0a8ac8eb404025eb656c65d0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/c7eef3a60ccfdd8eafe07f81652e769ac9c7146c", - "reference": "c7eef3a60ccfdd8eafe07f81652e769ac9c7146c", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/2634381fdf27a2a0a8ac8eb404025eb656c65d0c", + "reference": "2634381fdf27a2a0a8ac8eb404025eb656c65d0c", "shasum": "" }, "require": { "php": ">=7.2.5", - "symfony/deprecation-contracts": "^2.1", + "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.0", "symfony/polyfill-php80": "^1.16" @@ -6297,7 +6421,7 @@ }, "require-dev": { "masterminds/html5": "^2.6", - "symfony/css-selector": "^4.4|^5.0" + "symfony/css-selector": "^4.4|^5.0|^6.0" }, "suggest": { "symfony/css-selector": "" @@ -6328,7 +6452,7 @@ "description": "Eases DOM navigation for HTML and XML documents", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dom-crawler/tree/v5.3.7" + "source": "https://github.com/symfony/dom-crawler/tree/v5.4.3" }, "funding": [ { @@ -6344,24 +6468,25 @@ "type": "tidelift" } ], - "time": "2021-08-29T19:32:13+00:00" + "time": "2022-01-02T09:53:40+00:00" }, { "name": "symfony/finder", - "version": "v5.3.7", + "version": "v5.4.3", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "a10000ada1e600d109a6c7632e9ac42e8bf2fb93" + "reference": "231313534dded84c7ecaa79d14bc5da4ccb69b7d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/a10000ada1e600d109a6c7632e9ac42e8bf2fb93", - "reference": "a10000ada1e600d109a6c7632e9ac42e8bf2fb93", + "url": "https://api.github.com/repos/symfony/finder/zipball/231313534dded84c7ecaa79d14bc5da4ccb69b7d", + "reference": "231313534dded84c7ecaa79d14bc5da4ccb69b7d", "shasum": "" }, "require": { "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", "symfony/polyfill-php80": "^1.16" }, "type": "library", @@ -6390,7 +6515,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v5.3.7" + "source": "https://github.com/symfony/finder/tree/v5.4.3" }, "funding": [ { @@ -6406,7 +6531,7 @@ "type": "tidelift" } ], - "time": "2021-08-04T21:20:46+00:00" + "time": "2022-01-26T16:34:36+00:00" }, { "name": "theseer/tokenizer", @@ -6535,5 +6660,5 @@ "platform-overrides": { "php": "7.3.6" }, - "plugin-api-version": "2.1.0" + "plugin-api-version": "2.2.0" } diff --git a/index.php b/index.php index 091e9a8..3e30831 100644 --- a/index.php +++ b/index.php @@ -3,7 +3,7 @@ /** * @package Grav.Core * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -12,10 +12,6 @@ namespace Grav; \define('GRAV_REQUEST_TIME', microtime(true)); \define('GRAV_PHP_MIN', '7.3.6'); -if (version_compare($ver = PHP_VERSION, $req = GRAV_PHP_MIN, '<')) { - die(sprintf('You are running PHP %s, but Grav needs at least PHP %s to run.', $ver, $req)); -} - if (PHP_SAPI === 'cli-server') { $symfony_server = stripos(getenv('_'), 'symfony') !== false || stripos($_SERVER['SERVER_SOFTWARE'] ?? '', 'symfony') !== false || stripos($_ENV['SERVER_SOFTWARE'] ?? '', 'symfony') !== false; diff --git a/system/blueprints/config/system.yaml b/system/blueprints/config/system.yaml index dffe099..b6beb41 100644 --- a/system/blueprints/config/system.yaml +++ b/system/blueprints/config/system.yaml @@ -888,9 +888,45 @@ form: title: PLUGIN_ADMIN.ASSETS fields: - assets_section: + general_config_section: type: section - title: PLUGIN_ADMIN.ASSETS + title: PLUGIN_ADMIN.GENERAL_CONFIG + underline: true + + assets.enable_asset_timestamp: + type: toggle + label: PLUGIN_ADMIN.ENABLED_TIMESTAMPS_ON_ASSETS + help: PLUGIN_ADMIN.ENABLED_TIMESTAMPS_ON_ASSETS_HELP + highlight: 0 + options: + 1: PLUGIN_ADMIN.YES + 0: PLUGIN_ADMIN.NO + validate: + type: bool + + assets.enable_asset_sri: + type: toggle + label: PLUGIN_ADMIN.ENABLED_SRI_ON_ASSETS + help: PLUGIN_ADMIN.ENABLED_SRI_ON_ASSETS_HELP + highlight: 0 + options: + 1: PLUGIN_ADMIN.YES + 0: PLUGIN_ADMIN.NO + validate: + type: bool + + assets.collections: + type: multilevel + label: PLUGIN_ADMIN.COLLECTIONS + placeholder_key: collection_name + placeholder_value: collection_path + validate: + type: array + + + css_assets_section: + type: section + title: PLUGIN_ADMIN.CSS_ASSETS underline: true assets.css_pipeline: @@ -959,6 +995,11 @@ form: validate: type: bool + js_assets_section: + type: section + title: PLUGIN_ADMIN.JS_ASSETS + underline: true + assets.js_pipeline: type: toggle label: PLUGIN_ADMIN.JAVASCRIPT_PIPELINE @@ -1003,10 +1044,15 @@ form: validate: type: bool - assets.enable_asset_timestamp: + js_module_assets_section: + type: section + title: PLUGIN_ADMIN.JS_MODULE_ASSETS + underline: true + + assets.js_module_pipeline: type: toggle - label: PLUGIN_ADMIN.ENABLED_TIMESTAMPS_ON_ASSETS - help: PLUGIN_ADMIN.ENABLED_TIMESTAMPS_ON_ASSETS_HELP + label: PLUGIN_ADMIN.JAVASCRIPT_MODULE_PIPELINE + help: PLUGIN_ADMIN.JAVASCRIPT_MODULE_PIPELINE_HELP highlight: 0 options: 1: PLUGIN_ADMIN.YES @@ -1014,24 +1060,29 @@ form: validate: type: bool - assets.enable_asset_sri: + assets.js_module_pipeline_include_externals: type: toggle - label: PLUGIN_ADMIN.ENABLED_SRI_ON_ASSETS - help: PLUGIN_ADMIN.ENABLED_SRI_ON_ASSETS_HELP - highlight: 0 + label: PLUGIN_ADMIN.JAVASCRIPT_MODULE_PIPELINE_INCLUDE_EXTERNALS + help: PLUGIN_ADMIN.JAVASCRIPT_MODULE_PIPELINE_INCLUDE_EXTERNALS_HELP + highlight: 1 options: 1: PLUGIN_ADMIN.YES 0: PLUGIN_ADMIN.NO validate: type: bool - assets.collections: - type: multilevel - label: PLUGIN_ADMIN.COLLECTIONS - placeholder_key: collection_name - placeholder_value: collection_path + assets.js_module_pipeline_before_excludes: + type: toggle + label: PLUGIN_ADMIN.JAVASCRIPT_MODULE_PIPELINE_BEFORE_EXCLUDES + help: PLUGIN_ADMIN.JAVASCRIPT_MODULE_PIPELINE_BEFORE_EXCLUDES_HELP + highlight: 1 + options: + 1: PLUGIN_ADMIN.YES + 0: PLUGIN_ADMIN.NO validate: - type: array + type: bool + + errors: type: tab @@ -1394,6 +1445,18 @@ form: validate: type: bool + session.secure_https: + type: toggle + label: PLUGIN_ADMIN.SESSION_SECURE_HTTPS + help: PLUGIN_ADMIN.SESSION_SECURE_HTTPS_HELP + highlight: 1 + options: + 1: PLUGIN_ADMIN.YES + 0: PLUGIN_ADMIN.NO + default: true + validate: + type: bool + session.httponly: type: toggle label: PLUGIN_ADMIN.SESSION_HTTPONLY @@ -1446,6 +1509,10 @@ form: title: PLUGIN_ADMIN.ADVANCED underline: true + gpm_section: + type: section + title: PLUGIN_ADMIN.GPM_SECTION + gpm.releases: type: toggle label: PLUGIN_ADMIN.GPM_RELEASES @@ -1455,23 +1522,6 @@ form: stable: PLUGIN_ADMIN.STABLE testing: PLUGIN_ADMIN.TESTING - gpm.proxy_url: - type: text - size: medium - placeholder: "e.g. 127.0.0.1:3128" - label: PLUGIN_ADMIN.PROXY_URL - help: PLUGIN_ADMIN.PROXY_URL_HELP - - gpm.method: - type: toggle - label: PLUGIN_ADMIN.GPM_METHOD - highlight: auto - help: PLUGIN_ADMIN.GPM_METHOD_HELP - options: - auto: PLUGIN_ADMIN.AUTO - fopen: PLUGIN_ADMIN.FOPEN - curl: PLUGIN_ADMIN.CURL - gpm.official_gpm_only: type: toggle label: PLUGIN_ADMIN.GPM_OFFICIAL_ONLY @@ -1484,17 +1534,80 @@ form: validate: type: bool - gpm.verify_peer: + http_section: + type: section + title: PLUGIN_ADMIN.HTTP_SECTION + + http.method: type: toggle - label: PLUGIN_ADMIN.GPM_VERIFY_PEER + label: PLUGIN_ADMIN.GPM_METHOD + highlight: auto + help: PLUGIN_ADMIN.GPM_METHOD_HELP + options: + auto: PLUGIN_ADMIN.AUTO + fopen: PLUGIN_ADMIN.FOPEN + curl: PLUGIN_ADMIN.CURL + + http.enable_proxy: + type: toggle + label: PLUGIN_ADMIN.SSL_ENABLE_PROXY highlight: 1 - help: PLUGIN_ADMIN.GPM_VERIFY_PEER_HELP + options: + 1: PLUGIN_ADMIN.YES + 0: PLUGIN_ADMIN.NO + default: false + validate: + type: bool + + http.proxy_url: + type: text + size: medium + placeholder: "e.g. 127.0.0.1:3128" + label: PLUGIN_ADMIN.PROXY_URL + help: PLUGIN_ADMIN.PROXY_URL_HELP + + http.proxy_cert_path: + type: text + size: medium + placeholder: "e.g. /Users/bob/certs/" + label: PLUGIN_ADMIN.PROXY_CERT + help: PLUGIN_ADMIN.PROXY_CERT_HELP + + http.verify_peer: + type: toggle + label: PLUGIN_ADMIN.SSL_VERIFY_PEER + highlight: 1 + help: PLUGIN_ADMIN.SSL_VERIFY_PEER_HELP options: 1: PLUGIN_ADMIN.YES 0: PLUGIN_ADMIN.NO validate: type: bool + http.verify_host: + type: toggle + label: PLUGIN_ADMIN.SSL_VERIFY_HOST + highlight: 1 + help: PLUGIN_ADMIN.SSL_VERIFY_HOST_HELP + options: + 1: PLUGIN_ADMIN.YES + 0: PLUGIN_ADMIN.NO + validate: + type: bool + + http.concurrent_connections: + type: number + size: x-small + label: PLUGIN_ADMIN.HTTP_CONNECTIONS + help: PLUGIN_ADMIN.HTTP_CONNECTIONS_HELP + validate: + min: 1 + max: 20 + + misc_section: + type: section + title: PLUGIN_ADMIN.MISC_SECTION + reverse_proxy_setup: type: toggle label: PLUGIN_ADMIN.REVERSE_PROXY diff --git a/system/blueprints/flex/pages.yaml b/system/blueprints/flex/pages.yaml index 5c6ed8e..89dab6a 100644 --- a/system/blueprints/flex/pages.yaml +++ b/system/blueprints/flex/pages.yaml @@ -104,7 +104,7 @@ config: edit: title: - template: "{% if object.root %}Root ( <root> ){% else %}{{ (form.value('header.title') ?? form.value('folder'))|e }} ( {{ (object.getRoute().toString(false) ?: '/')|e }} ){% endif %}" + template: "{% if object.root %}Root ( <root> ){% else %}{{ (form.value('header.title') ?? form.value('folder'))|e }} ( {{ (object.getRoute().toString(false) ?: '/')|e }} ){% endif %}" # TODO: not used yet buttons: diff --git a/system/blueprints/flex/user-accounts.yaml b/system/blueprints/flex/user-accounts.yaml index 384bf97..8d336f9 100644 --- a/system/blueprints/flex/user-accounts.yaml +++ b/system/blueprints/flex/user-accounts.yaml @@ -122,6 +122,8 @@ config: fields: - key - email + - username + - fullname blueprints: configure: diff --git a/system/blueprints/flex/user-groups.yaml b/system/blueprints/flex/user-groups.yaml index 05ba87b..a5d348b 100644 --- a/system/blueprints/flex/user-groups.yaml +++ b/system/blueprints/flex/user-groups.yaml @@ -113,6 +113,7 @@ config: fields: - key - groupname + - readableName - description blueprints: diff --git a/system/blueprints/pages/default.yaml b/system/blueprints/pages/default.yaml index f3ef29f..a573a83 100644 --- a/system/blueprints/pages/default.yaml +++ b/system/blueprints/pages/default.yaml @@ -320,6 +320,18 @@ form: fields: + header.redirect_default_route: + type: toggle + toggleable: true + label: PLUGIN_ADMIN.REDIRECT_DEFAULT_ROUTE + help: PLUGIN_ADMIN.REDIRECT_DEFAULT_ROUTE_HELP + config-highlight@: system.pages.redirect_default_route + options: + 1: PLUGIN_ADMIN.YES + 0: PLUGIN_ADMIN.NO + validate: + type: bool + header.routes.default: type: text toggleable: true diff --git a/system/blueprints/pages/external.yaml b/system/blueprints/pages/external.yaml index df1d7b0..d3bb57a 100644 --- a/system/blueprints/pages/external.yaml +++ b/system/blueprints/pages/external.yaml @@ -1,7 +1,7 @@ -title: PLUGIN_ADMIN:EXTERNAL +title: PLUGIN_ADMIN.EXTERNAL extends@: - type: default - context: blueprints://pages + type: default + context: blueprints://pages form: validation: loose @@ -29,16 +29,16 @@ form: unset@: true header.external_url: - type: text - label: PLUGIN_ADMIN.EXTERNAL_URL - placeholder: https://getgrav.org - validate: - required: true + type: text + label: PLUGIN_ADMIN.EXTERNAL_URL + placeholder: https://getgrav.org + validate: + required: true + options: fields: publishing: - fields: header.date: diff --git a/system/blueprints/pages/partials/security.yaml b/system/blueprints/pages/partials/security.yaml index d7619b9..26d8c7d 100644 --- a/system/blueprints/pages/partials/security.yaml +++ b/system/blueprints/pages/partials/security.yaml @@ -51,17 +51,13 @@ form: type: bool header.permissions.authors: - type: list + type: array toggleable: true + value_only: true + placeholder_value: PLUGIN_ADMIN.USERNAME label: PLUGIN_ADMIN.PAGE_AUTHORS help: PLUGIN_ADMIN.PAGE_AUTHORS_HELP - fields: - value: - type: text - placeholder: PLUGIN_ADMIN.USERNAME - style: vertical - header.permissions.groups: ignore@: true type: acl_picker diff --git a/system/blueprints/user/account.yaml b/system/blueprints/user/account.yaml index 127a99e..cfe5376 100644 --- a/system/blueprints/user/account.yaml +++ b/system/blueprints/user/account.yaml @@ -107,6 +107,12 @@ form: label: PLUGIN_ADMIN.2FA_SECRET sublabel: PLUGIN_ADMIN.2FA_SECRET_HELP + yubikey_id: + type: text + label: PLUGIN_ADMIN.YUBIKEY_ID + description: PLUGIN_ADMIN.YUBIKEY_HELP + size: small + maxlength: 12 diff --git a/system/config/media.yaml b/system/config/media.yaml index b118a21..fad5e0d 100644 --- a/system/config/media.yaml +++ b/system/config/media.yaml @@ -199,7 +199,7 @@ types: gz: type: file thumb: media/thumb-gz.png - mime: application/gzip + mime: application/x-gzip tar: type: file thumb: media/thumb-tar.png diff --git a/system/config/system.yaml b/system/config/system.yaml index fb5f391..2de075b 100644 --- a/system/config/system.yaml +++ b/system/config/system.yaml @@ -96,7 +96,7 @@ cache: purge_at: '0 4 * * *' # How often to purge old file cache (using new scheduler) clear_at: '0 3 * * *' # How often to clear cache (using new scheduler) clear_job_type: 'standard' # Type to clear when processing the scheduled clear job `standard`|`all` - clear_images_by_default: false # By default grav does not include processed images in cache clear, this can be enabled + clear_images_by_default: false # By default grav does not include processed images in cache clear, this can be enabled cli_compatibility: false # Ensures only non-volatile drivers are used (file, redis, memcache, etc.) lifetime: 604800 # Lifetime of cached data in seconds (0 = infinite) gzip: false # GZip compress the page output @@ -127,6 +127,9 @@ assets: # Configuration for Assets Mana js_pipeline: false # The JS pipeline is the unification of multiple JS resources into one file js_pipeline_include_externals: true # Include external URLs in the pipeline by default js_pipeline_before_excludes: true # Render the pipeline before any excluded files + js_module_pipeline: false # The JS Module pipeline is the unification of multiple JS Module resources into one file + js_module_pipeline_include_externals: true # Include external URLs in the pipeline by default + js_module_pipeline_before_excludes: true # Render the pipeline before any excluded files js_minify: true # Minify the JS during pipelining enable_asset_timestamp: false # Enable asset timestamps enable_asset_sri: false # Enable asset SRI @@ -162,6 +165,12 @@ images: retina_scale: 1 # scale to adjust auto-sizes for better handling of HiDPI resolutions defaults: loading: auto # Let browser pick [auto|lazy|eager] + watermark: + image: 'system://images/watermark.png' # Path to a watermark image + position_y: 'center' # top|center|bottom + position_x: 'center' # left|center|right + scale: 33 # percentage of watermark scale + watermark_all: false # automatically watermark all images media: enable_media_timestamp: false # Enable media timestamps @@ -176,6 +185,7 @@ session: name: grav-site # Name prefix of the session cookie. Use alphanumeric, dashes or underscores only. Do not use dots in the session name uniqueness: path # Should sessions be `path` based or `security.salt` based secure: false # Set session secure. If true, indicates that communication for this cookie must be over an encrypted transmission. Enable this only on sites that run exclusively on HTTPS + secure_https: true # Set session secure on HTTPS but not on HTTP. Has no effect if you have `session.secure: true`. Set to false if your site jumps between HTTP and HTTPS. httponly: true # Set session HTTP only. If true, indicates that cookies should be used only over HTTP, and JavaScript modification is not allowed. samesite: Lax # Set session SameSite. Possible values are Lax, Strict and None. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite split: true # Sessions should be independent between site and plugins (such as admin) @@ -184,11 +194,17 @@ session: gpm: releases: stable # Set to either 'stable' or 'testing' - proxy_url: # Configure a manual proxy URL for GPM (eg 127.0.0.1:3128) - method: 'auto' # Either 'curl', 'fopen' or 'auto'. 'auto' will try fopen first and if not available cURL - verify_peer: true # Sometimes on some systems (Windows most commonly) GPM is unable to connect because the SSL certificate cannot be verified. Disabling this setting might help. official_gpm_only: true # By default GPM direct-install will only allow URLs via the official GPM proxy to ensure security +http: + method: auto # Either 'curl', 'fopen' or 'auto'. 'auto' will try fopen first and if not available cURL + enable_proxy: true # Enable proxy server configuration + proxy_url: # Configure a manual proxy URL for GPM (eg 127.0.0.1:3128) + proxy_cert_path: # Local path to proxy certificate folder containing pem files + concurrent_connections: 5 # Concurrent HTTP connections when multiplexing + verify_peer: true # Enable/Disable SSL verification of peer certificates + verify_host: true # Enable/Disable SSL verification of host certificates + accounts: type: regular # EXPERIMENTAL: Account type: regular or flex storage: file # EXPERIMENTAL: Flex storage type: file or folder diff --git a/system/defines.php b/system/defines.php index e3cbb36..39dd692 100644 --- a/system/defines.php +++ b/system/defines.php @@ -3,13 +3,13 @@ /** * @package Grav\Core * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ // Some standard defines define('GRAV', true); -define('GRAV_VERSION', '1.7.23'); +define('GRAV_VERSION', '1.7.30'); define('GRAV_SCHEMA', '1.7.0_2020-11-20_1'); define('GRAV_TESTING', false); diff --git a/system/images/watermark.png b/system/images/watermark.png new file mode 100644 index 0000000000000000000000000000000000000000..0f38a75c40d48ba2fd062a82eb50090f4cc61133 GIT binary patch literal 95789 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%0D2)uoNCMzd4 z4#kTL#oXu@n|bCUT5!dX{{bK=muucT!s8qNM%;`z^jbhDM!bJ^86LrH?o%%R6+eKa zT*VKXBBH&y|NN8X#hm3vF-YYZN*%axg_A?&jbNBY9whVQs|4n*a1rf>oYobl?Zc`a zvTE=D;!u?;M17i~Y)R|7JnVD#H5bvY-&l*J^!X;-2eDzot+=>Vn5RHiuoooI@7`Za z?MpvT{5WHV6v@!FyhS(s{;KfOp{nEDY5^OshFiUj1mNX+gA4{Rd&=`2T4PkkPGxS+ zMbU6}1PonH1t#*^?JQ|9hxN96P^JZq=r4-XIil`PVr5mJWTF4gUKVHe>e@+13Y<`4 zNAQq>Q^62XL4EUYw%F$YTZLpN{YkxzF`|pV8m2gA45qW=H07gat$xR0lxLNQ`4;YG z*LxUdO=$~w7zuO|=V2!1hk4TT`7CSf^_nsrRk58yYw&v7>mv8gr~giNj-b5Gm!%uB zS-FgcrNeIYdt!2vL6JoyDxx&nacQ#5XOi5xpr>&W{!cdD04TfgD|E38#c7|<@sU41 z_T*1!*&a{zC9CS~ml*p;PF}AZo}KGv*L<}AT2X)m(DX&BPJeZ`ondl9mRnUK+&&p?#h#8<@)BktPi1`AU zG}>IKg-A*qyZ8lU+mQ$);(-$uWl8c;>Ky}Hw7%O3X<#4|Kc@k>%l5as(mnkt1}AyN zuznp3S-zY@sp=QYRDYaQumX;`g#!IWyQFd>Lj$E;a}>TSm-}W2twH&#ua5Kzv4A)N zlIj4UeHi=;&~Ci0IdOC7p*`hVnP`H!K`M3!F@tDaXI1P|+Ij{#ty!1?mJaNj(>NS8 z%UL&SxPbORE}&hO188UB-{k>-_AU+bgds)$%N~ee2k-%#UgB%Fl~)i$mqSfpnGVKm z%xIF4^rg67G~j@S7+~>9eW3&XxEB=?-j$BdOUy>XdB4;jHkN`q-2p+Y5<5<+C0o4n zXwTM`&v3C|HU&f#^SLEW)xcLsB!S1oY~?H9dfe(c!xG~wQW;?DB< zE>b?Yxfg!(G{CrBiI!isYjJ0X%&gD%0RLIm)_QuZ@e9RyZk2^Qwq=tH7yGHl;_l}v zs(9&WbW9h%=nvbL>8|(pMf}m(o%4AZN$;>v#^IVqY94RGYA;<9w>`;Igu*aXFoi_A+%ElxP*3{jfHq%iyQ$6 z?JpRlTYb&~S+H@VP8M<2lU+g%mP1h+{4Jt5HWWBE5+uK7zaRGxJBBVP)q5KR5ZdWB z-QyfWdoq{M?vW#57O~bfdJovwpk4Pr{35gqW;2G)zVlxnM2#@8){1e!@+6P>lCL?Lu*0Mne227@aEqxv_8xGvUAP_#GB}hy#;P0$}np)|JO6(JzVCdnU4CGv{^1 zRN|etgC9Hq9$$Y!S{Yh^By56}wm(7^Q0S$ok>Y=R_Z zeHd=ud^E?8M%ZG6`7vYjjJyz@Spd9ni&6pHp`TO~H?&{Jo#Gj7$d6 zGA=oYwkK+q1lZer`Z}eyXV+@HFqmocC}9^Tod|L@9mS3fTZcTSh4i-BoeXbWMW3h? z^5hz7*i2{%6V6_cxwnMG#}xoVTuqI9za+zlr;h1#$f6uFGC}94o889->ygCJ!d_*A zO6k@_b=U0)|HC$Js|~oBng`Lml?1M_I+eQ%PNa8#SRZhxi37TgUB~^@y40rBvM)Uw z53(+(rrXWdwB}3~yLtHN8zb@h+}q(_g=CZBQTkfmMRJZmH`H!{`>Xp~~kj ze=;}4TX5|Yx-6Y^iUkFW8VLsOpK%w3C8)90opgEP`Mlb&N7J^0^E5dT%NiM#0ABC( zx0s9#?LjtdYhZKP++>{1gqo|;6|5P2G%$18&vdnIV%kGN;etp3!uxksa8pb)f3wYx T&xFalló la validación: ' INVALID_INPUT: 'Dato inválido en: ' MISSING_REQUIRED_FIELD: 'Falta el campo requerido: ' - XSS_ISSUES: "Se detectaron problemas XSS potenciales en el campo '%s'" + XSS_ISSUES: "Se detectaron potenciales problemas XSS en el campo '%s'" MONTHS_OF_THE_YEAR: - 'Enero' - 'Febrero' @@ -86,7 +86,7 @@ GRAV: - 'Viernes' - 'Sábado' - 'Domingo' - YES: "Si" + YES: "Sí" NO: "No" CRON: EVERY: cada @@ -96,12 +96,12 @@ GRAV: EVERY_DAY_OF_MONTH: cada día del mes EVERY_MONTH: cada mes TEXT_PERIOD: Cada - TEXT_MINS: ' a minuto(s) despues de la hora' + TEXT_MINS: ' a minuto(s) después de la hora' TEXT_TIME: ' a :' TEXT_DOW: ' en ' TEXT_MONTH: ' de' TEXT_DOM: ' en' - ERROR1: La etiqueta %s no está soportada! - ERROR2: El número de elementos es erroneo + ERROR1: '¡La etiqueta %s no está soportada!' + ERROR2: El número de elementos es erróneo ERROR3: El jquery_element debería establecerse en la configuración del jqCron ERROR4: Expresión no reconocida diff --git a/system/router.php b/system/router.php index 187d4d8..c19f814 100644 --- a/system/router.php +++ b/system/router.php @@ -3,7 +3,7 @@ /** * @package Grav\Core * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -13,8 +13,25 @@ if (PHP_SAPI !== 'cli-server') { $_SERVER['PHP_CLI_ROUTER'] = true; -if (is_file($_SERVER['DOCUMENT_ROOT'] . DIRECTORY_SEPARATOR . $_SERVER['SCRIPT_NAME'])) { - return false; +$root = $_SERVER['DOCUMENT_ROOT']; +$path = $_SERVER['SCRIPT_NAME']; +if ($path !== '/index.php' && is_file($root . $path)) { + if (!( + // Block all direct access to files and folders beginning with a dot + strpos($path, '/.') !== false + // Block all direct access for these folders + || preg_match('`^/(\.git|cache|bin|logs|backup|webserver-configs|tests)/`ui', $path) + // Block access to specific file types for these system folders + || preg_match('`^/(system|vendor)/(.*)\.(txt|xml|md|html|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$`ui', $path) + // Block access to specific file types for these user folders + || preg_match('`^/(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$`ui', $path) + // Block all direct access to .md files + || preg_match('`\.md$`ui', $path) + // Block access to specific files in the root folder + || preg_match('`^/(LICENSE\.txt|composer\.lock|composer\.json|\.htaccess)$`ui', $path) + )) { + return false; + } } $grav_index = 'index.php'; diff --git a/system/src/DOMLettersIterator.php b/system/src/DOMLettersIterator.php new file mode 100644 index 0000000..e3c2c4a --- /dev/null +++ b/system/src/DOMLettersIterator.php @@ -0,0 +1,165 @@ +load('example.xml'); + * foreach(new DOMLettersIterator($doc) as $letter) echo $letter; + * + * NB: If you only need characters without their position + * in the document, use DOMNode->textContent instead. + * + * @author porneL http://pornel.net + * @license Public Domain + * @url https://github.com/antoligy/dom-string-iterators + * + * @implements Iterator + */ +final class DOMLettersIterator implements Iterator +{ + /** @var DOMElement */ + private $start; + /** @var DOMElement|null */ + private $current; + /** @var int */ + private $offset = -1; + /** @var int|null */ + private $key; + /** @var array|null */ + private $letters; + + /** + * expects DOMElement or DOMDocument (see DOMDocument::load and DOMDocument::loadHTML) + * + * @param DOMNode $el + */ + public function __construct(DOMNode $el) + { + if ($el instanceof DOMDocument) { + $el = $el->documentElement; + } + + if (!$el instanceof DOMElement) { + throw new InvalidArgumentException('Invalid arguments, expected DOMElement or DOMDocument'); + } + + $this->start = $el; + } + + /** + * Returns position in text as DOMText node and character offset. + * (it's NOT a byte offset, you must use mb_substr() or similar to use this offset properly). + * node may be NULL if iterator has finished. + * + * @return array + */ + public function currentTextPosition(): array + { + return [$this->current, $this->offset]; + } + + /** + * Returns DOMElement that is currently being iterated or NULL if iterator has finished. + * + * @return DOMElement|null + */ + public function currentElement(): ?DOMElement + { + return $this->current ? $this->current->parentNode : null; + } + + // Implementation of Iterator interface + + /** + * @return int|null + */ + public function key(): ?int + { + return $this->key; + } + + /** + * @return void + */ + public function next(): void + { + if (null === $this->current) { + return; + } + + if ($this->current->nodeType === XML_TEXT_NODE || $this->current->nodeType === XML_CDATA_SECTION_NODE) { + if ($this->offset === -1) { + preg_match_all('/./us', $this->current->textContent, $m); + $this->letters = $m[0]; + } + + $this->offset++; + $this->key++; + if ($this->letters && $this->offset < count($this->letters)) { + return; + } + + $this->offset = -1; + } + + while ($this->current->nodeType === XML_ELEMENT_NODE && $this->current->firstChild) { + $this->current = $this->current->firstChild; + if ($this->current->nodeType === XML_TEXT_NODE || $this->current->nodeType === XML_CDATA_SECTION_NODE) { + $this->next(); + return; + } + } + + while (!$this->current->nextSibling && $this->current->parentNode) { + $this->current = $this->current->parentNode; + if ($this->current === $this->start) { + $this->current = null; + return; + } + } + + $this->current = $this->current->nextSibling; + + $this->next(); + } + + /** + * Return the current element + * @link https://php.net/manual/en/iterator.current.php + * + * @return string|null + */ + public function current(): ?string + { + return $this->letters ? $this->letters[$this->offset] : null; + } + + /** + * Checks if current position is valid + * @link https://php.net/manual/en/iterator.valid.php + * + * @return bool + */ + public function valid(): bool + { + return (bool)$this->current; + } + + /** + * @return void + */ + public function rewind(): void + { + $this->current = $this->start; + $this->offset = -1; + $this->key = 0; + $this->letters = []; + + $this->next(); + } +} + diff --git a/system/src/DOMWordsIterator.php b/system/src/DOMWordsIterator.php new file mode 100644 index 0000000..fb7c2e3 --- /dev/null +++ b/system/src/DOMWordsIterator.php @@ -0,0 +1,158 @@ +load('example.xml'); + * foreach(new DOMWordsIterator($doc) as $word) echo $word; + * + * @author pjgalbraith http://www.pjgalbraith.com + * @author porneL http://pornel.net (based on DOMLettersIterator available at http://pornel.net/source/domlettersiterator.php) + * @license Public Domain + * @url https://github.com/antoligy/dom-string-iterators + * + * @implements Iterator + */ + +final class DOMWordsIterator implements Iterator +{ + /** @var DOMElement */ + private $start; + /** @var DOMElement|null */ + private $current; + /** @var int */ + private $offset = -1; + /** @var int|null */ + private $key; + /** @var array>|null */ + private $words; + + /** + * expects DOMElement or DOMDocument (see DOMDocument::load and DOMDocument::loadHTML) + * + * @param DOMNode $el + */ + public function __construct(DOMNode $el) + { + if ($el instanceof DOMDocument) { + $el = $el->documentElement; + } + + if (!$el instanceof DOMElement) { + throw new InvalidArgumentException('Invalid arguments, expected DOMElement or DOMDocument'); + } + + $this->start = $el; + } + + /** + * Returns position in text as DOMText node and character offset. + * (it's NOT a byte offset, you must use mb_substr() or similar to use this offset properly). + * node may be NULL if iterator has finished. + * + * @return array + */ + public function currentWordPosition(): array + { + return [$this->current, $this->offset, $this->words]; + } + + /** + * Returns DOMElement that is currently being iterated or NULL if iterator has finished. + * + * @return DOMElement|null + */ + public function currentElement(): ?DOMElement + { + return $this->current ? $this->current->parentNode : null; + } + + // Implementation of Iterator interface + + /** + * Return the key of the current element + * @link https://php.net/manual/en/iterator.key.php + * @return int|null + */ + public function key(): ?int + { + return $this->key; + } + + /** + * @return void + */ + public function next(): void + { + if (null === $this->current) { + return; + } + + if ($this->current->nodeType === XML_TEXT_NODE || $this->current->nodeType === XML_CDATA_SECTION_NODE) { + if ($this->offset === -1) { + $this->words = preg_split("/[\n\r\t ]+/", $this->current->textContent, -1, PREG_SPLIT_NO_EMPTY|PREG_SPLIT_OFFSET_CAPTURE) ?: []; + } + $this->offset++; + + if ($this->words && $this->offset < count($this->words)) { + $this->key++; + return; + } + $this->offset = -1; + } + + while ($this->current->nodeType === XML_ELEMENT_NODE && $this->current->firstChild) { + $this->current = $this->current->firstChild; + if ($this->current->nodeType === XML_TEXT_NODE || $this->current->nodeType === XML_CDATA_SECTION_NODE) { + $this->next(); + return; + } + } + + while (!$this->current->nextSibling && $this->current->parentNode) { + $this->current = $this->current->parentNode; + if ($this->current === $this->start) { + $this->current = null; + return; + } + } + + $this->current = $this->current->nextSibling; + + $this->next(); + } + + /** + * Return the current element + * @link https://php.net/manual/en/iterator.current.php + * @return string|null + */ + public function current(): ?string + { + return $this->words ? (string)$this->words[$this->offset][0] : null; + } + + /** + * Checks if current position is valid + * @link https://php.net/manual/en/iterator.valid.php + * @return bool + */ + public function valid(): bool + { + return (bool)$this->current; + } + + public function rewind(): void + { + $this->current = $this->start; + $this->offset = -1; + $this->key = 0; + $this->words = []; + + $this->next(); + } +} diff --git a/system/src/Grav/Common/Assets.php b/system/src/Grav/Common/Assets.php index a9b930b..134c62f 100644 --- a/system/src/Grav/Common/Assets.php +++ b/system/src/Grav/Common/Assets.php @@ -3,7 +3,7 @@ /** * @package Grav\Common * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -30,14 +30,21 @@ class Assets extends PropertyObject use TestingAssetsTrait; use LegacyAssetsTrait; + const LINK = 'link'; const CSS = 'css'; const JS = 'js'; + const JS_MODULE = 'js_module'; + const LINK_COLLECTION = 'assets_link'; const CSS_COLLECTION = 'assets_css'; const JS_COLLECTION = 'assets_js'; + const JS_MODULE_COLLECTION = 'assets_js_module'; + const LINK_TYPE = Assets\Link::class; const CSS_TYPE = Assets\Css::class; const JS_TYPE = Assets\Js::class; + const JS_MODULE_TYPE = Assets\JsModule::class; const INLINE_CSS_TYPE = Assets\InlineCss::class; const INLINE_JS_TYPE = Assets\InlineJs::class; + const INLINE_JS_MODULE_TYPE = Assets\InlineJsModule::class; /** @const Regex to match CSS and JavaScript files */ const DEFAULT_REGEX = '/.\.(css|js)$/i'; @@ -48,15 +55,24 @@ class Assets extends PropertyObject /** @const Regex to match JavaScript files */ const JS_REGEX = '/.\.js$/i'; + /** @const Regex to match JavaScriptModyle files */ + const JS_MODULE_REGEX = '/.\.mjs$/i'; + /** @var string */ protected $assets_dir; /** @var string */ protected $assets_url; + /** @var array */ + protected $assets_link = []; /** @var array */ protected $assets_css = []; /** @var array */ protected $assets_js = []; + /** @var array */ + protected $assets_js_module = []; + + // Following variables come from the configuration: /** @var bool */ @@ -66,19 +82,17 @@ class Assets extends PropertyObject /** @var bool */ protected $css_pipeline_before_excludes; /** @var bool */ - protected $inlinecss_pipeline_include_externals; - /** @var bool */ - protected $inlinecss_pipeline_before_excludes; - /** @var bool */ protected $js_pipeline; /** @var bool */ protected $js_pipeline_include_externals; /** @var bool */ protected $js_pipeline_before_excludes; /** @var bool */ - protected $inlinejs_pipeline_include_externals; + protected $js_module_pipeline; /** @var bool */ - protected $inlinejs_pipeline_before_excludes; + protected $js_module_pipeline_include_externals; + /** @var bool */ + protected $js_module_pipeline_before_excludes; /** @var array */ protected $pipeline_options = []; @@ -184,7 +198,7 @@ class Assets extends PropertyObject call_user_func_array([$this, 'add'], $args); } else { // Get extension - $extension = pathinfo(parse_url($asset, PHP_URL_PATH), PATHINFO_EXTENSION); + $extension = Utils::pathinfo(parse_url($asset, PHP_URL_PATH), PATHINFO_EXTENSION); // JavaScript or CSS if ($extension !== '') { @@ -193,6 +207,8 @@ class Assets extends PropertyObject call_user_func_array([$this, 'addCss'], $args); } elseif ($extension === 'js') { call_user_func_array([$this, 'addJs'], $args); + } elseif ($extension === 'mjs') { + call_user_func_array([$this, 'addJsModule'], $args); } } } @@ -222,7 +238,7 @@ class Assets extends PropertyObject return $this; } - if (($type === $this::CSS_TYPE || $type === $this::JS_TYPE) && isset($this->collections[$asset])) { + if ($this->isValidType($type) && isset($this->collections[$asset])) { $this->addType($collection, $type, $this->collections[$asset], $options); return $this; } @@ -230,7 +246,9 @@ class Assets extends PropertyObject // If pipeline disabled, set to position if provided, else after if (isset($options['pipeline'])) { if ($options['pipeline'] === false) { - $exclude_type = ($type === $this::JS_TYPE || $type === $this::INLINE_JS_TYPE) ? $this::JS : $this::CSS; + + $exclude_type = $this->getBaseType($type); + $excludes = strtolower($exclude_type . '_pipeline_before_excludes'); if ($this->{$excludes}) { $default = 'after'; @@ -269,6 +287,16 @@ class Assets extends PropertyObject return $this; } + /** + * Add a CSS asset or a collection of assets. + * + * @return $this + */ + public function addLink($asset) + { + return $this->addType($this::LINK_COLLECTION, $this::LINK_TYPE, $asset, $this->unifyLegacyArguments(func_get_args(), $this::LINK_TYPE)); + } + /** * Add a CSS asset or a collection of assets. * @@ -309,6 +337,25 @@ class Assets extends PropertyObject return $this->addType($this::JS_COLLECTION, $this::INLINE_JS_TYPE, $asset, $this->unifyLegacyArguments(func_get_args(), $this::INLINE_JS_TYPE)); } + /** + * Add a JS asset or a collection of assets. + * + * @return $this + */ + public function addJsModule($asset) + { + return $this->addType($this::JS_MODULE_COLLECTION, $this::JS_MODULE_TYPE, $asset, $this->unifyLegacyArguments(func_get_args(), $this::JS_MODULE_TYPE)); + } + + /** + * Add an Inline JS asset or a collection of assets. + * + * @return $this + */ + public function addInlineJsModule($asset) + { + return $this->addType($this::JS_MODULE_COLLECTION, $this::INLINE_JS_MODULE_TYPE, $asset, $this->unifyLegacyArguments(func_get_args(), $this::INLINE_JS_MODULE_TYPE)); + } /** * Add/replace collection. @@ -400,7 +447,7 @@ class Assets extends PropertyObject $after_assets = $this->filterAssets($group_assets, 'position', 'after', true); // Pipeline - if ($this->{$pipeline_enabled}) { + if ($this->{$pipeline_enabled} ?? false) { $options = array_merge($this->pipeline_options, ['timestamp' => $this->timestamp]); $pipeline = new Pipeline($options); @@ -432,9 +479,29 @@ class Assets extends PropertyObject * @param array $attributes * @return string */ - public function css($group = 'head', $attributes = []) + public function css($group = 'head', $attributes = [], $include_link = true) { - return $this->render('css', $group, $attributes); + $output = ''; + + if ($include_link) { + $output = $this->link($group, $attributes); + } + + $output .= $this->render(self::CSS, $group, $attributes); + + return $output; + } + + /** + * Build the CSS link tags. + * + * @param string $group name of the group + * @param array $attributes + * @return string + */ + public function link($group = 'head', $attributes = []) + { + return $this->render(self::LINK, $group, $attributes); } /** @@ -444,8 +511,58 @@ class Assets extends PropertyObject * @param array $attributes * @return string */ - public function js($group = 'head', $attributes = []) + public function js($group = 'head', $attributes = [], $include_js_module = true) { - return $this->render('js', $group, $attributes); + $output = $this->render(self::JS, $group, $attributes); + + if ($include_js_module) { + $output .= $this->jsModule($group, $attributes); + } + + return $output; + } + + /** + * Build the Javascript Modules tags + * + * @param $group + * @param $attributes + * @return string + */ + public function jsModule($group = 'head', $attributes = []) + { + return $this->render(self::JS_MODULE, $group, $attributes); + } + + public function all($group = 'head', $attributes = []) + { + $output = $this->css($group, $attributes, false); + $output .= $this->link($group, $attributes); + $output .= $this->js($group, $attributes, false); + $output .= $this->jsModule($group, $attributes); + return $output; + } + + protected function isValidType($type) + { + return in_array($type, [self::CSS_TYPE, self::JS_TYPE, self::JS_MODULE_TYPE]); + } + + protected function getBaseType($type) + { + switch ($type) { + case $this::JS_TYPE: + case $this::INLINE_JS_TYPE: + $base_type = $this::JS; + break; + case $this::JS_MODULE_TYPE: + case $this::INLINE_JS_MODULE_TYPE: + $base_type = $this::JS_MODULE; + break; + default: + $base_type = $this::CSS; + } + + return $base_type; } } diff --git a/system/src/Grav/Common/Assets/BaseAsset.php b/system/src/Grav/Common/Assets/BaseAsset.php index a115ce9..d062d75 100644 --- a/system/src/Grav/Common/Assets/BaseAsset.php +++ b/system/src/Grav/Common/Assets/BaseAsset.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Assets * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -26,8 +26,9 @@ abstract class BaseAsset extends PropertyObject { use AssetUtilsTrait; - protected const CSS_ASSET = true; - protected const JS_ASSET = false; + protected const CSS_ASSET = 1; + protected const JS_ASSET = 2; + protected const JS_MODULE_ASSET = 3; /** @var string|false */ protected $asset; @@ -69,7 +70,7 @@ abstract class BaseAsset extends PropertyObject * @param array $elements * @param string|null $key */ - public function __construct(array $elements = [], $key = null) + public function __construct(array $elements = [], ?string $key = null) { $base_config = [ 'group' => 'head', @@ -248,6 +249,7 @@ abstract class BaseAsset extends PropertyObject * * @return array */ + #[\ReturnTypeWillChange] public function jsonSerialize() { return ['type' => $this->getType(), 'elements' => $this->getElements()]; @@ -265,4 +267,17 @@ abstract class BaseAsset extends PropertyObject { return ''; } + + /** + * Finds relative JS urls() and rewrites the URL with an absolute one + * + * @param string $file the css source file + * @param string $dir local relative path to the css file + * @param bool $local is this a local or remote asset + * @return string + */ + protected function jsRewrite($file, $dir, $local) + { + return ''; + } } diff --git a/system/src/Grav/Common/Assets/BlockAssets.php b/system/src/Grav/Common/Assets/BlockAssets.php new file mode 100644 index 0000000..798bdd3 --- /dev/null +++ b/system/src/Grav/Common/Assets/BlockAssets.php @@ -0,0 +1,207 @@ +getAssets(); + foreach ($types as $type => $groups) { + switch ($type) { + case 'frameworks': + static::registerFrameworks($assets, $groups); + break; + case 'styles': + static::registerStyles($assets, $groups); + break; + case 'scripts': + static::registerScripts($assets, $groups); + break; + case 'links': + static::registerLinks($assets, $groups); + break; + case 'html': + static::registerHtml($assets, $groups); + break; + } + } + } + + /** + * @param Assets $assets + * @param array $groups + * @return void + */ + protected static function registerFrameworks(Assets $assets, array $list): void + { + if ($list) { + throw new \RuntimeException('Not Implemented'); + } + } + + /** + * @param Assets $assets + * @param array $groups + * @return void + */ + protected static function registerStyles(Assets $assets, array $groups): void + { + $grav = Grav::instance(); + + /** @var Config $config */ + $config = $grav['config']; + + foreach ($groups as $group => $styles) { + foreach ($styles as $style) { + switch ($style[':type']) { + case 'file': + $options = [ + 'priority' => $style[':priority'], + 'group' => $group, + 'type' => $style['type'], + 'media' => $style['media'] + ] + $style['element']; + + $assets->addCss(static::getRelativeUrl($style['href'], $config->get('system.assets.css_pipeline')), $options); + break; + case 'inline': + $options = [ + 'priority' => $style[':priority'], + 'group' => $group, + 'type' => $style['type'], + ] + $style['element']; + + $assets->addInlineCss($style['content'], $options); + break; + } + } + } + } + + /** + * @param Assets $assets + * @param array $groups + * @return void + */ + protected static function registerScripts(Assets $assets, array $groups): void + { + $grav = Grav::instance(); + + /** @var Config $config */ + $config = $grav['config']; + + foreach ($groups as $group => $scripts) { + $group = $group === 'footer' ? 'bottom' : $group; + + foreach ($scripts as $script) { + switch ($script[':type']) { + case 'file': + $options = [ + 'group' => $group, + 'priority' => $script[':priority'], + 'src' => $script['src'], + 'type' => $script['type'], + 'loading' => $script['loading'], + 'defer' => $script['defer'], + 'async' => $script['async'], + 'handle' => $script['handle'] + ] + $script['element']; + + $assets->addJs(static::getRelativeUrl($script['src'], $config->get('system.assets.js_pipeline')), $options); + break; + case 'inline': + $options = [ + 'priority' => $script[':priority'], + 'group' => $group, + 'type' => $script['type'], + 'loading' => $script['loading'] + ] + $script['element']; + + $assets->addInlineJs($script['content'], $options); + break; + } + } + } + } + + /** + * @param Assets $assets + * @param array $groups + * @return void + */ + protected static function registerLinks(Assets $assets, array $groups): void + { + foreach ($groups as $group => $links) { + foreach ($links as $link) { + $href = $link['href']; + $options = [ + 'group' => $group, + 'priority' => $link[':priority'], + 'rel' => $link['rel'], + ] + $link['element']; + + $assets->addLink($href, $options); + } + } + } + + /** + * @param Assets $assets + * @param array $groups + * @return void + */ + protected static function registerHtml(Assets $assets, array $groups): void + { + if ($groups) { + throw new \RuntimeException('Not Implemented'); + } + } + + /** + * @param string $url + * @param bool $pipeline + * @return string + */ + protected static function getRelativeUrl($url, $pipeline) + { + $grav = Grav::instance(); + + $base = rtrim($grav['base_url'], '/') ?: '/'; + + if (strpos($url, $base) === 0) { + if ($pipeline) { + // Remove file timestamp if CSS pipeline has been enabled. + $url = preg_replace('|[?#].*|', '', $url); + } + + return substr($url, strlen($base) - 1); + } + return $url; + } +} diff --git a/system/src/Grav/Common/Assets/Css.php b/system/src/Grav/Common/Assets/Css.php index 4c6a9c9..ea6b388 100644 --- a/system/src/Grav/Common/Assets/Css.php +++ b/system/src/Grav/Common/Assets/Css.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Assets * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -22,7 +22,7 @@ class Css extends BaseAsset * @param array $elements * @param string|null $key */ - public function __construct(array $elements = [], $key = null) + public function __construct(array $elements = [], ?string $key = null) { $base_options = [ 'asset_type' => 'css', diff --git a/system/src/Grav/Common/Assets/InlineCss.php b/system/src/Grav/Common/Assets/InlineCss.php index f024a95..4984db4 100644 --- a/system/src/Grav/Common/Assets/InlineCss.php +++ b/system/src/Grav/Common/Assets/InlineCss.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Assets * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -22,7 +22,7 @@ class InlineCss extends BaseAsset * @param array $elements * @param string|null $key */ - public function __construct(array $elements = [], $key = null) + public function __construct(array $elements = [], ?string $key = null) { $base_options = [ 'asset_type' => 'css', diff --git a/system/src/Grav/Common/Assets/InlineJs.php b/system/src/Grav/Common/Assets/InlineJs.php index 6787608..e38a51a 100644 --- a/system/src/Grav/Common/Assets/InlineJs.php +++ b/system/src/Grav/Common/Assets/InlineJs.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Assets * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -22,7 +22,7 @@ class InlineJs extends BaseAsset * @param array $elements * @param string|null $key */ - public function __construct(array $elements = [], $key = null) + public function __construct(array $elements = [], ?string $key = null) { $base_options = [ 'asset_type' => 'js', diff --git a/system/src/Grav/Common/Assets/InlineJsModule.php b/system/src/Grav/Common/Assets/InlineJsModule.php new file mode 100644 index 0000000..42ce6f1 --- /dev/null +++ b/system/src/Grav/Common/Assets/InlineJsModule.php @@ -0,0 +1,46 @@ + 'js_module', + 'attributes' => ['type' => 'module'], + 'position' => 'after' + ]; + + $merged_attributes = Utils::arrayMergeRecursiveUnique($base_options, $elements); + + parent::__construct($merged_attributes, $key); + } + + /** + * @return string + */ + public function render() + { + return 'renderAttributes(). ">\n" . trim($this->asset) . "\n\n"; + } + +} diff --git a/system/src/Grav/Common/Assets/Js.php b/system/src/Grav/Common/Assets/Js.php index fc2a472..8687a86 100644 --- a/system/src/Grav/Common/Assets/Js.php +++ b/system/src/Grav/Common/Assets/Js.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Assets * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -22,7 +22,7 @@ class Js extends BaseAsset * @param array $elements * @param string|null $key */ - public function __construct(array $elements = [], $key = null) + public function __construct(array $elements = [], ?string $key = null) { $base_options = [ 'asset_type' => 'js', diff --git a/system/src/Grav/Common/Assets/JsModule.php b/system/src/Grav/Common/Assets/JsModule.php new file mode 100644 index 0000000..5c2a836 --- /dev/null +++ b/system/src/Grav/Common/Assets/JsModule.php @@ -0,0 +1,49 @@ + 'js_module', + 'attributes' => ['type' => 'module'] + ]; + + $merged_attributes = Utils::arrayMergeRecursiveUnique($base_options, $elements); + + parent::__construct($merged_attributes, $key); + } + + /** + * @return string + */ + public function render() + { + if (isset($this->attributes['loading']) && $this->attributes['loading'] === 'inline') { + $buffer = $this->gatherLinks([$this], self::JS_MODULE_ASSET); + return 'renderAttributes() . ">\n" . trim($buffer) . "\n\n"; + } + + return '\n"; + } +} diff --git a/system/src/Grav/Common/Assets/Link.php b/system/src/Grav/Common/Assets/Link.php new file mode 100644 index 0000000..ecafcea --- /dev/null +++ b/system/src/Grav/Common/Assets/Link.php @@ -0,0 +1,43 @@ + 'link', + ]; + + $merged_attributes = Utils::arrayMergeRecursiveUnique($base_options, $elements); + + parent::__construct($merged_attributes, $key); + } + + /** + * @return string + */ + public function render() + { + return 'renderAttributes() . $this->integrityHash($this->asset) . ">\n"; + } +} diff --git a/system/src/Grav/Common/Assets/Pipeline.php b/system/src/Grav/Common/Assets/Pipeline.php index 948104a..0010d7b 100644 --- a/system/src/Grav/Common/Assets/Pipeline.php +++ b/system/src/Grav/Common/Assets/Pipeline.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Assets * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -29,12 +29,16 @@ class Pipeline extends PropertyObject { use AssetUtilsTrait; - protected const CSS_ASSET = true; - protected const JS_ASSET = false; + protected const CSS_ASSET = 1; + protected const JS_ASSET = 2; + protected const JS_MODULE_ASSET = 3; /** @const Regex to match CSS urls */ protected const CSS_URL_REGEX = '{url\(([\'\"]?)(.*?)\1\)}'; + /** @const Regex to match JS imports */ + protected const JS_IMPORT_REGEX = '{import.+from\s?[\'|\"](.+?)[\'|\"]}'; + /** @const Regex to match CSS sourcemap comments */ protected const CSS_SOURCEMAP_REGEX = '{\/\*# (.*?) \*\/}'; @@ -122,7 +126,7 @@ class Pipeline extends PropertyObject // Compute uid based on assets and timestamp $json_assets = json_encode($assets); - $uid = md5($json_assets . $this->css_minify . $this->css_rewrite . $group); + $uid = md5($json_assets . (int)$this->css_minify . (int)$this->css_rewrite . $group); $file = $uid . '.css'; $relative_path = "{$this->base_url}{$this->assets_url}/{$file}"; @@ -169,7 +173,7 @@ class Pipeline extends PropertyObject * @param array $attributes * @return bool|string URL or generated content if available, else false */ - public function renderJs($assets, $group, $attributes = []) + public function renderJs($assets, $group, $attributes = [], $type = self::JS_ASSET) { // temporary list of assets to pipeline $inline_group = false; @@ -198,7 +202,7 @@ class Pipeline extends PropertyObject } // Concatenate files - $buffer = $this->gatherLinks($assets, self::JS_ASSET); + $buffer = $this->gatherLinks($assets, $type); // Minify if required if ($this->shouldMinify('js')) { @@ -223,6 +227,19 @@ class Pipeline extends PropertyObject return $output; } + /** + * Minify and concatenate JS files. + * + * @param array $assets + * @param string $group + * @param array $attributes + * @return bool|string URL or generated content if available, else false + */ + public function renderJs_Module($assets, $group, $attributes = []) + { + $attributes['type'] = 'module'; + return $this->renderJs($assets, $group, $attributes, self::JS_MODULE_ASSET); + } /** * Finds relative CSS urls() and rewrites the URL with an absolute one @@ -262,6 +279,42 @@ class Pipeline extends PropertyObject return $file; } + /** + * Finds relative JS urls() and rewrites the URL with an absolute one + * + * @param string $file the css source file + * @param string $dir local relative path to the css file + * @param bool $local is this a local or remote asset + * @return string + */ + protected function jsRewrite($file, $dir, $local) + { + // Find any js import elements, grab the URLs and calculate an absolute path + // Then replace the old url with the new one + $file = (string)preg_replace_callback(self::JS_IMPORT_REGEX, function ($matches) use ($dir, $local) { + + $old_url = $matches[1]; + + // Ensure link is not rooted to web server, a data URL, or to a remote host + if (preg_match(self::FIRST_FORWARDSLASH_REGEX, $old_url) || $this->isRemoteLink($old_url)) { + return $matches[0]; + } + + // clean leading / + $old_url = Utils::normalizePath($dir . '/' . $old_url); + $old_url = str_replace('/./', '/', $old_url); + if (preg_match(self::FIRST_FORWARDSLASH_REGEX, $old_url)) { + $old_url = ltrim($old_url, '/'); + } + + $new_url = ($local ? $this->base_url : '') . $old_url; + + return str_replace($matches[1], $new_url, $matches[0]); + }, $file); + + return $file; + } + /** * @param string $type * @return bool diff --git a/system/src/Grav/Common/Assets/Traits/AssetUtilsTrait.php b/system/src/Grav/Common/Assets/Traits/AssetUtilsTrait.php index 3f5a690..0e3f392 100644 --- a/system/src/Grav/Common/Assets/Traits/AssetUtilsTrait.php +++ b/system/src/Grav/Common/Assets/Traits/AssetUtilsTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Assets\Traits * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -62,13 +62,13 @@ trait AssetUtilsTrait * Download and concatenate the content of several links. * * @param array $assets - * @param bool $css + * @param int $type * @return string */ - protected function gatherLinks(array $assets, $css = true) + protected function gatherLinks(array $assets, int $type = self::CSS_ASSET): string { $buffer = ''; - foreach ($assets as $id => $asset) { + foreach ($assets as $asset) { $local = true; $link = $asset->getAsset(); @@ -100,21 +100,25 @@ trait AssetUtilsTrait } // Double check last character being - if (!$css) { + if ($type === self::JS_ASSET || $type === self::JS_MODULE_ASSET) { $file = rtrim($file, ' ;') . ';'; } // If this is CSS + the file is local + rewrite enabled - if ($css && $this->css_rewrite) { + if ($type === self::CSS_ASSET && $this->css_rewrite) { $file = $this->cssRewrite($file, $relative_dir, $local); } + if ($type === self::JS_MODULE_ASSET) { + $file = $this->jsRewrite($file, $relative_dir, $local); + } + $file = rtrim($file) . PHP_EOL; $buffer .= $file; } // Pull out @imports and move to top - if ($css) { + if ($type === self::CSS_ASSET) { $buffer = $this->moveImports($buffer); } diff --git a/system/src/Grav/Common/Assets/Traits/LegacyAssetsTrait.php b/system/src/Grav/Common/Assets/Traits/LegacyAssetsTrait.php index 36b2152..c1ef0a3 100644 --- a/system/src/Grav/Common/Assets/Traits/LegacyAssetsTrait.php +++ b/system/src/Grav/Common/Assets/Traits/LegacyAssetsTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Assets\Traits * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Assets/Traits/TestingAssetsTrait.php b/system/src/Grav/Common/Assets/Traits/TestingAssetsTrait.php index 15dc00e..b11b439 100644 --- a/system/src/Grav/Common/Assets/Traits/TestingAssetsTrait.php +++ b/system/src/Grav/Common/Assets/Traits/TestingAssetsTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Assets\Traits * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -285,6 +285,15 @@ trait TestingAssetsTrait return $this; } + // Add JavaScript Module files + if ($pattern === self::JS_MODULE_REGEX) { + foreach ($files as $file) { + $this->addJsModule($file); + } + + return $this; + } + // Unknown pattern. foreach ($files as $asset) { $this->add($asset); diff --git a/system/src/Grav/Common/Backup/Backups.php b/system/src/Grav/Common/Backup/Backups.php index b70def9..9c23908 100644 --- a/system/src/Grav/Common/Backup/Backups.php +++ b/system/src/Grav/Common/Backup/Backups.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Backup * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -105,7 +105,7 @@ class Backups public function getBackupDownloadUrl($backup, $base_url) { $param_sep = $param_sep = Grav::instance()['config']->get('system.param_sep', ':'); - $download = urlencode(base64_encode(basename($backup))); + $download = urlencode(base64_encode(Utils::basename($backup))); $url = rtrim(Grav::instance()['uri']->rootUrl(true), '/') . '/' . trim( $base_url, '/' @@ -144,9 +144,8 @@ class Backups public static function getTotalBackupsSize() { $backups = static::getAvailableBackups(); - $size = array_sum(array_column($backups, 'size')); - return $size ?? 0; + return $backups ? array_sum(array_column($backups, 'size')) : 0; } /** diff --git a/system/src/Grav/Common/Browser.php b/system/src/Grav/Common/Browser.php index e4a6513..ffc8da3 100644 --- a/system/src/Grav/Common/Browser.php +++ b/system/src/Grav/Common/Browser.php @@ -3,7 +3,7 @@ /** * @package Grav\Common * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Cache.php b/system/src/Grav/Common/Cache.php index a503518..ed40a08 100644 --- a/system/src/Grav/Common/Cache.php +++ b/system/src/Grav/Common/Cache.php @@ -3,7 +3,7 @@ /** * @package Grav\Common * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -177,7 +177,7 @@ class Cache extends Getters public function purgeOldCache() { $cache_dir = dirname($this->cache_dir); - $current = basename($this->cache_dir); + $current = Utils::basename($this->cache_dir); $count = 0; foreach (new DirectoryIterator($cache_dir) as $file) { diff --git a/system/src/Grav/Common/Composer.php b/system/src/Grav/Common/Composer.php index f1f6e5d..667edf2 100644 --- a/system/src/Grav/Common/Composer.php +++ b/system/src/Grav/Common/Composer.php @@ -3,7 +3,7 @@ /** * @package Grav\Common * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Config/CompiledBase.php b/system/src/Grav/Common/Config/CompiledBase.php index 0764189..b462b4c 100644 --- a/system/src/Grav/Common/Config/CompiledBase.php +++ b/system/src/Grav/Common/Config/CompiledBase.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Config * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Config/CompiledBlueprints.php b/system/src/Grav/Common/Config/CompiledBlueprints.php index 7743054..e838591 100644 --- a/system/src/Grav/Common/Config/CompiledBlueprints.php +++ b/system/src/Grav/Common/Config/CompiledBlueprints.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Config * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Config/CompiledConfig.php b/system/src/Grav/Common/Config/CompiledConfig.php index 22225bc..2db6de5 100644 --- a/system/src/Grav/Common/Config/CompiledConfig.php +++ b/system/src/Grav/Common/Config/CompiledConfig.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Config * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Config/CompiledLanguages.php b/system/src/Grav/Common/Config/CompiledLanguages.php index 6674389..0a26686 100644 --- a/system/src/Grav/Common/Config/CompiledLanguages.php +++ b/system/src/Grav/Common/Config/CompiledLanguages.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Config * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Config/Config.php b/system/src/Grav/Common/Config/Config.php index 7adb523..ca027bf 100644 --- a/system/src/Grav/Common/Config/Config.php +++ b/system/src/Grav/Common/Config/Config.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Config * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Config/ConfigFileFinder.php b/system/src/Grav/Common/Config/ConfigFileFinder.php index e6b1afe..28f9dc1 100644 --- a/system/src/Grav/Common/Config/ConfigFileFinder.php +++ b/system/src/Grav/Common/Config/ConfigFileFinder.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Config * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Config/Languages.php b/system/src/Grav/Common/Config/Languages.php index 4a863a1..05798c5 100644 --- a/system/src/Grav/Common/Config/Languages.php +++ b/system/src/Grav/Common/Config/Languages.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Config * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Config/Setup.php b/system/src/Grav/Common/Config/Setup.php index 6ff7372..f8ce943 100644 --- a/system/src/Grav/Common/Config/Setup.php +++ b/system/src/Grav/Common/Config/Setup.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Config * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -401,7 +401,7 @@ class Setup extends Data } // Create security.yaml salt if it doesn't exist into existing configuration environment if possible. - $securityFile = basename(static::$securityFile); + $securityFile = Utils::basename(static::$securityFile); $securityFolder = substr(static::$securityFile, 0, -\strlen($securityFile)); $securityFolder = $locator->findResource($securityFolder, true) ?: $locator->findResource($securityFolder, true, true); $filename = "{$securityFolder}/{$securityFile}"; diff --git a/system/src/Grav/Common/Data/Blueprint.php b/system/src/Grav/Common/Data/Blueprint.php index 2878199..ef6aef5 100644 --- a/system/src/Grav/Common/Data/Blueprint.php +++ b/system/src/Grav/Common/Data/Blueprint.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Data * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -99,7 +99,7 @@ class Blueprint extends BlueprintForm */ public function getDefaultValue(string $name) { - $path = explode('.', $name) ?: []; + $path = explode('.', $name); $current = $this->getDefaults(); foreach ($path as $field) { @@ -293,15 +293,16 @@ class Blueprint extends BlueprintForm /** * Flatten data by using blueprints. * - * @param array $data - * @param bool $includeAll + * @param array $data Data to be flattened. + * @param bool $includeAll True if undefined properties should also be included. + * @param string $name Property which will be flattened, useful for flattening repeating data. * @return array */ - public function flattenData(array $data, bool $includeAll = false) + public function flattenData(array $data, bool $includeAll = false, string $name = '') { $this->initInternals(); - return $this->blueprintSchema->flattenData($data, $includeAll); + return $this->blueprintSchema->flattenData($data, $includeAll, $name); } diff --git a/system/src/Grav/Common/Data/BlueprintSchema.php b/system/src/Grav/Common/Data/BlueprintSchema.php index 7990bec..8cc6303 100644 --- a/system/src/Grav/Common/Data/BlueprintSchema.php +++ b/system/src/Grav/Common/Data/BlueprintSchema.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Data * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -83,7 +83,7 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface } if (!empty($messages)) { - throw (new ValidationException())->setMessages($messages); + throw (new ValidationException('', 400))->setMessages($messages); } } @@ -115,23 +115,29 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface /** * Flatten data by using blueprints. * - * @param array $data Data to be flattened. - * @param bool $includeAll + * @param array $data Data to be flattened. + * @param bool $includeAll True if undefined properties should also be included. + * @param string $name Property which will be flattened, useful for flattening repeating data. * @return array */ - public function flattenData(array $data, bool $includeAll = false) + public function flattenData(array $data, bool $includeAll = false, string $name = '') { + $prefix = $name !== '' ? $name . '.' : ''; + $list = []; if ($includeAll) { - foreach ($this->items as $key => $rules) { + $items = $name !== '' ? $this->getProperty($name)['fields'] ?? [] : $this->items; + foreach ($items as $key => $rules) { $type = $rules['type'] ?? ''; if (!str_starts_with($type, '_') && !str_contains($key, '*')) { - $list[$key] = null; + $list[$prefix . $key] = null; } } } - return array_replace($list, $this->flattenArray($data, $this->nested, '')); + $nested = $this->getNestedRules($name); + + return array_replace($list, $this->flattenArray($data, $nested, $prefix)); } /** @@ -199,7 +205,7 @@ class BlueprintSchema extends BlueprintSchemaBase implements ExportInterface /** @var Config $config */ $config = Grav::instance()['config']; if (!$config->get('system.strict_mode.blueprint_strict_compat', true)) { - throw new RuntimeException(sprintf('%s is not defined in blueprints', $key)); + throw new RuntimeException(sprintf('%s is not defined in blueprints', $key), 400); } user_error(sprintf('Having extra key %s in your data is deprecated with blueprint having \'validation: strict\'', $key), E_USER_DEPRECATED); diff --git a/system/src/Grav/Common/Data/Blueprints.php b/system/src/Grav/Common/Data/Blueprints.php index abe153f..dfdd46f 100644 --- a/system/src/Grav/Common/Data/Blueprints.php +++ b/system/src/Grav/Common/Data/Blueprints.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Data * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Data/Data.php b/system/src/Grav/Common/Data/Data.php index 4de437d..0f09b2d 100644 --- a/system/src/Grav/Common/Data/Data.php +++ b/system/src/Grav/Common/Data/Data.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Data * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -335,6 +335,7 @@ class Data implements DataInterface, ArrayAccess, \Countable, JsonSerializable, /** * @return array */ + #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->items; diff --git a/system/src/Grav/Common/Data/DataInterface.php b/system/src/Grav/Common/Data/DataInterface.php index b4452bb..a4bc214 100644 --- a/system/src/Grav/Common/Data/DataInterface.php +++ b/system/src/Grav/Common/Data/DataInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Data * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Data/Validation.php b/system/src/Grav/Common/Data/Validation.php index c5b21ff..569ea51 100644 --- a/system/src/Grav/Common/Data/Validation.php +++ b/system/src/Grav/Common/Data/Validation.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Data * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -781,14 +781,22 @@ class Validation } // If creating new values is allowed, no further checks are needed. - if (!empty($field['selectize']['create'])) { + $validateOptions = $field['validate']['options'] ?? null; + if (!empty($field['selectize']['create']) || $validateOptions === 'ignore') { return true; } $options = $field['options'] ?? []; $use = $field['use'] ?? 'values'; - if (empty($field['selectize']) || empty($field['multiple'])) { + if ($validateOptions) { + // Use custom options structure. + foreach ($options as &$option) { + $option = $option[$validateOptions] ?? null; + } + unset($option); + $options = array_values($options); + } elseif (empty($field['selectize']) || empty($field['multiple'])) { $options = array_keys($options); } if ($use === 'keys') { @@ -808,7 +816,7 @@ class Validation { $value = static::filterArray($value, $params, $field); - return Utils::arrayUnflattenDotNotation($value); + return is_array($value) ? Utils::arrayUnflattenDotNotation($value) : null; } /** diff --git a/system/src/Grav/Common/Data/ValidationException.php b/system/src/Grav/Common/Data/ValidationException.php index 2d94ab8..9a3d900 100644 --- a/system/src/Grav/Common/Data/ValidationException.php +++ b/system/src/Grav/Common/Data/ValidationException.php @@ -3,23 +3,25 @@ /** * @package Grav\Common\Data * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Common\Data; use Grav\Common\Grav; +use JsonSerializable; use RuntimeException; /** * Class ValidationException * @package Grav\Common\Data */ -class ValidationException extends RuntimeException +class ValidationException extends RuntimeException implements JsonSerializable { /** @var array */ protected $messages = []; + protected $escape = true; /** * @param array $messages @@ -32,21 +34,34 @@ class ValidationException extends RuntimeException $language = Grav::instance()['language']; $this->message = $language->translate('GRAV.FORM.VALIDATION_FAIL', null, true) . ' ' . $this->message; - foreach ($messages as $variable => &$list) { + foreach ($messages as $list) { $list = array_unique($list); foreach ($list as $message) { - $this->message .= "
$message"; + $this->message .= '
' . htmlspecialchars($message, ENT_QUOTES | ENT_HTML5, 'UTF-8'); } } return $this; } + public function setSimpleMessage(bool $escape = true): void + { + $first = reset($this->messages); + $message = reset($first); + + $this->message = $escape ? htmlspecialchars($message, ENT_QUOTES | ENT_HTML5, 'UTF-8') : $message; + } + /** * @return array */ - public function getMessages() + public function getMessages(): array { return $this->messages; } + + public function jsonSerialize(): array + { + return ['validation' => $this->messages]; + } } diff --git a/system/src/Grav/Common/Debugger.php b/system/src/Grav/Common/Debugger.php index b8143f6..e7b1624 100644 --- a/system/src/Grav/Common/Debugger.php +++ b/system/src/Grav/Common/Debugger.php @@ -3,7 +3,7 @@ /** * @package Grav\Common * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -856,6 +856,10 @@ class Debugger $scope = 'grav'; } elseif (strpos($errfile, '/twig/') !== false) { $scope = 'twig'; + // TODO: remove when upgrading to Twig 2+ + if (str_contains($errstr, '#[\ReturnTypeWillChange]') || str_contains($errstr, 'Passing null to parameter')) { + return true; + } } elseif (stripos($errfile, '/yaml/') !== false) { $scope = 'yaml'; } elseif (strpos($errfile, '/vendor/') !== false) { diff --git a/system/src/Grav/Common/Errors/BareHandler.php b/system/src/Grav/Common/Errors/BareHandler.php index 206b57e..9093827 100644 --- a/system/src/Grav/Common/Errors/BareHandler.php +++ b/system/src/Grav/Common/Errors/BareHandler.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Errors * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Errors/Errors.php b/system/src/Grav/Common/Errors/Errors.php index 3dc99c6..92f885b 100644 --- a/system/src/Grav/Common/Errors/Errors.php +++ b/system/src/Grav/Common/Errors/Errors.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Errors * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Errors/SimplePageHandler.php b/system/src/Grav/Common/Errors/SimplePageHandler.php index 0118929..80c4f96 100644 --- a/system/src/Grav/Common/Errors/SimplePageHandler.php +++ b/system/src/Grav/Common/Errors/SimplePageHandler.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Errors * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Errors/SystemFacade.php b/system/src/Grav/Common/Errors/SystemFacade.php index 8a7f1ce..edf2b53 100644 --- a/system/src/Grav/Common/Errors/SystemFacade.php +++ b/system/src/Grav/Common/Errors/SystemFacade.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Errors * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/File/CompiledFile.php b/system/src/Grav/Common/File/CompiledFile.php index 66fa620..2228422 100644 --- a/system/src/Grav/Common/File/CompiledFile.php +++ b/system/src/Grav/Common/File/CompiledFile.php @@ -3,13 +3,14 @@ /** * @package Grav\Common\File * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Common\File; use Exception; +use Grav\Common\Utils; use RocketTheme\Toolbox\File\PhpFile; use RuntimeException; use Throwable; @@ -88,7 +89,7 @@ trait CompiledFile $this->content = $cache['data']; } } catch (Exception $e) { - throw new RuntimeException(sprintf('Failed to read %s: %s', basename($this->filename), $e->getMessage()), 500, $e); + throw new RuntimeException(sprintf('Failed to read %s: %s', Utils::basename($this->filename), $e->getMessage()), 500, $e); } return parent::content($var); diff --git a/system/src/Grav/Common/File/CompiledJsonFile.php b/system/src/Grav/Common/File/CompiledJsonFile.php index dae95ba..e8347bb 100644 --- a/system/src/Grav/Common/File/CompiledJsonFile.php +++ b/system/src/Grav/Common/File/CompiledJsonFile.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\File * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/File/CompiledMarkdownFile.php b/system/src/Grav/Common/File/CompiledMarkdownFile.php index 1107417..1083111 100644 --- a/system/src/Grav/Common/File/CompiledMarkdownFile.php +++ b/system/src/Grav/Common/File/CompiledMarkdownFile.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\File * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/File/CompiledYamlFile.php b/system/src/Grav/Common/File/CompiledYamlFile.php index 0fedeb7..0f0c64f 100644 --- a/system/src/Grav/Common/File/CompiledYamlFile.php +++ b/system/src/Grav/Common/File/CompiledYamlFile.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\File * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Filesystem/Archiver.php b/system/src/Grav/Common/Filesystem/Archiver.php index 090bc13..4c291a5 100644 --- a/system/src/Grav/Common/Filesystem/Archiver.php +++ b/system/src/Grav/Common/Filesystem/Archiver.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Filesystem * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Filesystem/Folder.php b/system/src/Grav/Common/Filesystem/Folder.php index ecbc6ce..8dac2e5 100644 --- a/system/src/Grav/Common/Filesystem/Folder.php +++ b/system/src/Grav/Common/Filesystem/Folder.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Filesystem * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -513,7 +513,7 @@ abstract class Folder } $directories = glob($directory . '/*', GLOB_ONLYDIR); - return count($directories); + return $directories ? count($directories) : false; } /** @@ -530,7 +530,8 @@ abstract class Folder } // Go through all items in filesystem and recursively remove everything. - $files = array_diff(scandir($folder, SCANDIR_SORT_NONE), array('.', '..')); + $files = scandir($folder, SCANDIR_SORT_NONE); + $files = $files ? array_diff($files, ['.', '..']) : []; foreach ($files as $file) { $path = "{$folder}/{$file}"; is_dir($path) ? self::doDelete($path) : @unlink($path); diff --git a/system/src/Grav/Common/Filesystem/RecursiveDirectoryFilterIterator.php b/system/src/Grav/Common/Filesystem/RecursiveDirectoryFilterIterator.php index 0dac81c..75c19bc 100644 --- a/system/src/Grav/Common/Filesystem/RecursiveDirectoryFilterIterator.php +++ b/system/src/Grav/Common/Filesystem/RecursiveDirectoryFilterIterator.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Filesystem * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Filesystem/RecursiveFolderFilterIterator.php b/system/src/Grav/Common/Filesystem/RecursiveFolderFilterIterator.php index ded0259..12d6c6b 100644 --- a/system/src/Grav/Common/Filesystem/RecursiveFolderFilterIterator.php +++ b/system/src/Grav/Common/Filesystem/RecursiveFolderFilterIterator.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Filesystem * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Filesystem/ZipArchiver.php b/system/src/Grav/Common/Filesystem/ZipArchiver.php index 6e53e70..4f2a9ee 100644 --- a/system/src/Grav/Common/Filesystem/ZipArchiver.php +++ b/system/src/Grav/Common/Filesystem/ZipArchiver.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Filesystem * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Flex/FlexCollection.php b/system/src/Grav/Common/Flex/FlexCollection.php index 6429c9e..704e21f 100644 --- a/system/src/Grav/Common/Flex/FlexCollection.php +++ b/system/src/Grav/Common/Flex/FlexCollection.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Flex/FlexIndex.php b/system/src/Grav/Common/Flex/FlexIndex.php index 44b6fdd..f903c88 100644 --- a/system/src/Grav/Common/Flex/FlexIndex.php +++ b/system/src/Grav/Common/Flex/FlexIndex.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Flex/FlexObject.php b/system/src/Grav/Common/Flex/FlexObject.php index b64aea1..887d7a3 100644 --- a/system/src/Grav/Common/Flex/FlexObject.php +++ b/system/src/Grav/Common/Flex/FlexObject.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Flex/Traits/FlexCollectionTrait.php b/system/src/Grav/Common/Flex/Traits/FlexCollectionTrait.php index 29b640e..b919b40 100644 --- a/system/src/Grav/Common/Flex/Traits/FlexCollectionTrait.php +++ b/system/src/Grav/Common/Flex/Traits/FlexCollectionTrait.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Flex/Traits/FlexCommonTrait.php b/system/src/Grav/Common/Flex/Traits/FlexCommonTrait.php index 1077e00..9291a42 100644 --- a/system/src/Grav/Common/Flex/Traits/FlexCommonTrait.php +++ b/system/src/Grav/Common/Flex/Traits/FlexCommonTrait.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Flex/Traits/FlexGravTrait.php b/system/src/Grav/Common/Flex/Traits/FlexGravTrait.php index 9d5c9e0..c34b89d 100644 --- a/system/src/Grav/Common/Flex/Traits/FlexGravTrait.php +++ b/system/src/Grav/Common/Flex/Traits/FlexGravTrait.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Flex/Traits/FlexIndexTrait.php b/system/src/Grav/Common/Flex/Traits/FlexIndexTrait.php index 1d0ee5c..a71eb99 100644 --- a/system/src/Grav/Common/Flex/Traits/FlexIndexTrait.php +++ b/system/src/Grav/Common/Flex/Traits/FlexIndexTrait.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Flex/Traits/FlexObjectTrait.php b/system/src/Grav/Common/Flex/Traits/FlexObjectTrait.php index 1175a3b..3c81f07 100644 --- a/system/src/Grav/Common/Flex/Traits/FlexObjectTrait.php +++ b/system/src/Grav/Common/Flex/Traits/FlexObjectTrait.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Flex/Types/Generic/GenericCollection.php b/system/src/Grav/Common/Flex/Types/Generic/GenericCollection.php index 6b2fbc1..e4ec3d4 100644 --- a/system/src/Grav/Common/Flex/Types/Generic/GenericCollection.php +++ b/system/src/Grav/Common/Flex/Types/Generic/GenericCollection.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Flex/Types/Generic/GenericIndex.php b/system/src/Grav/Common/Flex/Types/Generic/GenericIndex.php index 81dbf95..fdba503 100644 --- a/system/src/Grav/Common/Flex/Types/Generic/GenericIndex.php +++ b/system/src/Grav/Common/Flex/Types/Generic/GenericIndex.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Flex/Types/Generic/GenericObject.php b/system/src/Grav/Common/Flex/Types/Generic/GenericObject.php index 4a56752..eba642b 100644 --- a/system/src/Grav/Common/Flex/Types/Generic/GenericObject.php +++ b/system/src/Grav/Common/Flex/Types/Generic/GenericObject.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Flex/Types/Pages/PageCollection.php b/system/src/Grav/Common/Flex/Types/Pages/PageCollection.php index 7503653..b6c5230 100644 --- a/system/src/Grav/Common/Flex/Types/Pages/PageCollection.php +++ b/system/src/Grav/Common/Flex/Types/Pages/PageCollection.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -34,7 +34,9 @@ use function is_string; * Class GravPageCollection * @package Grav\Plugin\FlexObjects\Types\GravPages * - * @extends FlexPageCollection + * @template T as PageObject + * @extends FlexPageCollection + * @implements PageCollectionInterface * * Incompatibilities with Grav\Common\Page\Collection: * $page = $collection->key() will not work at all @@ -46,10 +48,6 @@ use function is_string; * $collection->prev() does not rewind the internal pointer * AND most methods are immutable; they do not update the current collection, but return updated one * - * @method static shuffle() - * @method static select(array $keys) - * @method static unselect(array $keys) - * @method static createFrom(array $elements, string $keyField = null) * @method PageIndex getIndex() */ class PageCollection extends FlexPageCollection implements PageCollectionInterface @@ -108,13 +106,11 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa } /** - * @return PageObject + * @return PageInterface */ public function getRoot() { - $index = $this->getIndex(); - - return $index->getRoot(); + return $this->getIndex()->getRoot(); } /** @@ -154,7 +150,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * Add a single page to a collection * * @param PageInterface $page - * @return static + * @return $this */ public function addPage(PageInterface $page) { @@ -174,6 +170,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * * @param PageCollectionInterface $collection * @return static + * @phpstan-return static */ public function merge(PageCollectionInterface $collection) { @@ -185,6 +182,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * * @param PageCollectionInterface $collection * @return static + * @phpstan-return static */ public function intersect(PageCollectionInterface $collection) { @@ -203,7 +201,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * Return previous item. * * @return PageInterface|false - * @phpstan-return PageObject|false + * @phpstan-return T|false */ public function prev() { @@ -218,7 +216,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * Return nth item. * @param int $key * @return PageInterface|bool - * @phpstan-return PageObject|false + * @phpstan-return T|false */ public function nth($key) { @@ -230,6 +228,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * * @param int $num Specifies how many entries should be picked. * @return static + * @phpstan-return static */ public function random($num = 1) { @@ -241,6 +240,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * * @param array $items Items to be appended. Existing keys will be overridden with the new values. * @return static + * @phpstan-return static */ public function append($items) { @@ -252,6 +252,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * * @param int $size * @return static[] + * @phpstan-return static[] */ public function batch($size): array { @@ -273,6 +274,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * @param array|null $manual * @param int|null $sort_flags * @return static + * @phpstan-return static */ public function order($by, $dir = 'asc', $manual = null, $sort_flags = null) { @@ -333,7 +335,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa $list[$key] = $child->slug(); break; case 'basename': - $list[$key] = basename($key); + $list[$key] = Utils::basename($key); break; case 'folder': $list[$key] = $child->folder(); @@ -441,6 +443,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * @param string|null $endDate * @param string|null $field * @return static + * @phpstan-return static * @throws Exception */ public function dateRange($startDate = null, $endDate = null, $field = null) @@ -468,6 +471,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * Creates new collection with only visible pages * * @return static The collection with only visible pages + * @phpstan-return static */ public function visible() { @@ -485,6 +489,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * Creates new collection with only non-visible pages * * @return static The collection with only non-visible pages + * @phpstan-return static */ public function nonVisible() { @@ -502,6 +507,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * Creates new collection with only pages * * @return static The collection with only pages + * @phpstan-return static */ public function pages() { @@ -523,6 +529,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * Creates new collection with only modules * * @return static The collection with only modules + * @phpstan-return static */ public function modules() { @@ -544,6 +551,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * Alias of modules() * * @return static + * @phpstan-return static */ public function modular() { @@ -554,6 +562,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * Alias of pages() * * @return static + * @phpstan-return static */ public function nonModular() { @@ -564,6 +573,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * Creates new collection with only published pages * * @return static The collection with only published pages + * @phpstan-return static */ public function published() { @@ -581,6 +591,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * Creates new collection with only non-published pages * * @return static The collection with only non-published pages + * @phpstan-return static */ public function nonPublished() { @@ -598,6 +609,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * Creates new collection with only routable pages * * @return static The collection with only routable pages + * @phpstan-return static */ public function routable() { @@ -615,6 +627,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * Creates new collection with only non-routable pages * * @return static The collection with only non-routable pages + * @phpstan-return static */ public function nonRoutable() { @@ -633,6 +646,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * * @param string $type * @return static The collection + * @phpstan-return static */ public function ofType($type) { @@ -651,6 +665,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * * @param string[] $types * @return static The collection + * @phpstan-return static */ public function ofOneOfTheseTypes($types) { @@ -669,6 +684,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * * @param array $accessLevels * @return static The collection + * @phpstan-return static */ public function ofOneOfTheseAccessLevels($accessLevels) { @@ -710,6 +726,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa /** * @param bool $bool * @return static + * @phpstan-return static */ public function withOrdered(bool $bool = true) { @@ -721,6 +738,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa /** * @param bool $bool * @return static + * @phpstan-return static */ public function withModules(bool $bool = true) { @@ -732,6 +750,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa /** * @param bool $bool * @return static + * @phpstan-return static */ public function withPages(bool $bool = true) { @@ -745,6 +764,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * @param string|null $languageCode * @param bool|null $fallback * @return static + * @phpstan-return static */ public function withTranslation(bool $bool = true, string $languageCode = null, bool $fallback = null) { @@ -778,6 +798,7 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa * @param array $filters * @param bool $recursive * @return static + * @phpstan-return static */ public function filterBy(array $filters, bool $recursive = false) { diff --git a/system/src/Grav/Common/Flex/Types/Pages/PageIndex.php b/system/src/Grav/Common/Flex/Types/Pages/PageIndex.php index f02a648..c0ca5c4 100644 --- a/system/src/Grav/Common/Flex/Types/Pages/PageIndex.php +++ b/system/src/Grav/Common/Flex/Types/Pages/PageIndex.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -39,7 +39,10 @@ use function is_string; * Class GravPageObject * @package Grav\Plugin\FlexObjects\Types\GravPages * - * @extends FlexPageIndex + * @template T of PageObject + * @template C of PageCollection + * @extends FlexPageIndex + * @implements PageCollectionInterface * * @method PageIndex withModules(bool $bool = true) * @method PageIndex withPages(bool $bool = true) @@ -101,6 +104,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface /** * @param string $key * @return PageObject|null + * @phpstan-return T|null */ public function get($key) { @@ -117,11 +121,13 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface $element = $element->getTranslation(ltrim($params, '.')); } + \assert(null === $element || $element instanceof PageObject); + return $element; } /** - * @return PageObject + * @return PageInterface */ public function getRoot() { @@ -172,7 +178,8 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface /** * @param string|null $languageCode * @param bool|null $fallback - * @return PageIndex + * @return static + * @phpstan-return static */ public function withTranslated(string $languageCode = null, bool $fallback = null) { @@ -276,6 +283,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * @param array $filters * @param bool $recursive * @return static + * @phpstan-return static */ public function filterBy(array $filters, bool $recursive = false) { @@ -332,6 +340,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface /** * @param array $filters * @return static + * @phpstan-return static */ protected function filterByParent(array $filters) { @@ -402,6 +411,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * @param array $entries * @param string|null $keyField * @return static + * @phpstan-return static */ protected function createFrom(array $entries, string $keyField = null) { @@ -554,6 +564,9 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface $filters = array_filter($filters, static function($val) { return $val !== null && $val !== ''; }); if ($page) { + $status = 'success'; + $msg = 'PLUGIN_ADMIN.PAGE_ROUTE_FOUND'; + if ($page->root() && (!$filter_type || in_array('root', $filter_type, true))) { if ($field) { $response[] = [ @@ -593,9 +606,6 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface } } - $status = 'success'; - $msg = 'PLUGIN_ADMIN.PAGE_ROUTE_FOUND'; - /** @var PageIndex $children */ $children = $page->children()->getIndex(); $selectedChildren = $children->filterBy($filters, true); @@ -627,7 +637,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface $payload = [ 'name' => $child->menu(), 'value' => $child->rawRoute(), - 'item-key' => basename($child->rawRoute() ?? ''), + 'item-key' => Utils::basename($child->rawRoute() ?? ''), 'filename' => $child->folder(), 'extension' => $child->extension(), 'type' => 'dir', @@ -682,7 +692,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface $route = $child->getRoute(); $route = $route ? ($route->toString(false) ?: '/') : ''; $payload = [ - 'item-key' => htmlspecialchars(basename($child->rawRoute() ?? $child->getKey())), + 'item-key' => htmlspecialchars(Utils::basename($child->rawRoute() ?? $child->getKey())), 'icon' => $icon, 'title' => htmlspecialchars($child->menu()), 'route' => [ @@ -721,7 +731,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface $response = Utils::arrayFlatten($sorted); } - return [$status, $msg ?? 'PLUGIN_ADMIN.NO_ROUTE_PROVIDED', $response, $path]; + return [$status, $msg, $response, $path]; } /** @@ -787,6 +797,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * * @param PageInterface $page * @return PageCollection + * @phpstan-return C */ public function addPage(PageInterface $page) { @@ -798,6 +809,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * Create a copy of this collection * * @return static + * @phpstan-return static */ public function copy() { @@ -810,6 +822,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * * @param PageCollectionInterface $collection * @return PageCollection + * @phpstan-return C */ public function merge(PageCollectionInterface $collection) { @@ -822,6 +835,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * * @param PageCollectionInterface $collection * @return PageCollection + * @phpstan-return C */ public function intersect(PageCollectionInterface $collection) { @@ -833,6 +847,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * * @param int $size * @return PageCollection[] + * @phpstan-return C[] */ public function batch($size) { @@ -844,6 +859,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * * @param string $key * @return PageObject|null + * @phpstan-return T|null * @throws InvalidArgumentException */ public function remove($key) @@ -859,6 +875,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * @param array $manual * @param string $sort_flags * @return static + * @phpstan-return static */ public function order($by, $dir = 'asc', $manual = null, $sort_flags = null) { @@ -902,6 +919,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * * @param string $path * @return PageObject|null The previous item. + * @phpstan-return T|null */ public function prevSibling($path) { @@ -916,6 +934,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * * @param string $path * @return PageObject|null The next item. + * @phpstan-return T|null */ public function nextSibling($path) { @@ -931,6 +950,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * @param string $path * @param int $direction either -1 or +1 * @return PageObject|false The sibling item. + * @phpstan-return T|false */ public function adjacentSibling($path, $direction = 1) { @@ -964,6 +984,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * @param string|null $endDate * @param string|null $field * @return static + * @phpstan-return static * @throws Exception */ public function dateRange($startDate = null, $endDate = null, $field = null) @@ -988,6 +1009,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * Creates new collection with only visible pages * * @return static The collection with only visible pages + * @phpstan-return static */ public function visible() { @@ -1000,6 +1022,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * Creates new collection with only non-visible pages * * @return static The collection with only non-visible pages + * @phpstan-return static */ public function nonVisible() { @@ -1012,6 +1035,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * Creates new collection with only non-modular pages * * @return static The collection with only non-modular pages + * @phpstan-return static */ public function pages() { @@ -1024,6 +1048,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * Creates new collection with only modular pages * * @return static The collection with only modular pages + * @phpstan-return static */ public function modules() { @@ -1036,6 +1061,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * Creates new collection with only modular pages * * @return static The collection with only modular pages + * @phpstan-return static */ public function modular() { @@ -1046,6 +1072,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * Creates new collection with only non-modular pages * * @return static The collection with only non-modular pages + * @phpstan-return static */ public function nonModular() { @@ -1056,6 +1083,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * Creates new collection with only published pages * * @return static The collection with only published pages + * @phpstan-return static */ public function published() { @@ -1068,6 +1096,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * Creates new collection with only non-published pages * * @return static The collection with only non-published pages + * @phpstan-return static */ public function nonPublished() { @@ -1080,6 +1109,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * Creates new collection with only routable pages * * @return static The collection with only routable pages + * @phpstan-return static */ public function routable() { @@ -1092,6 +1122,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * Creates new collection with only non-routable pages * * @return static The collection with only non-routable pages + * @phpstan-return static */ public function nonRoutable() { @@ -1105,6 +1136,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * * @param string $type * @return static The collection + * @phpstan-return static */ public function ofType($type) { @@ -1118,6 +1150,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * * @param string[] $types * @return static The collection + * @phpstan-return static */ public function ofOneOfTheseTypes($types) { @@ -1131,6 +1164,7 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface * * @param array $accessLevels * @return static The collection + * @phpstan-return static */ public function ofOneOfTheseAccessLevels($accessLevels) { diff --git a/system/src/Grav/Common/Flex/Types/Pages/PageObject.php b/system/src/Grav/Common/Flex/Types/Pages/PageObject.php index 4c83f95..fed11b4 100644 --- a/system/src/Grav/Common/Flex/Types/Pages/PageObject.php +++ b/system/src/Grav/Common/Flex/Types/Pages/PageObject.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -25,9 +25,9 @@ use Grav\Common\Page\Pages; use Grav\Common\User\Interfaces\UserInterface; use Grav\Common\Utils; use Grav\Framework\Filesystem\Filesystem; -use Grav\Framework\Flex\FlexObject; use Grav\Framework\Flex\Interfaces\FlexObjectInterface; use Grav\Framework\Flex\Pages\FlexPageObject; +use Grav\Framework\Object\ObjectCollection; use Grav\Framework\Route\Route; use Grav\Framework\Route\RouteFactory; use Grav\Plugin\Admin\Admin; @@ -93,11 +93,6 @@ class PageObject extends FlexPageObject } } - public function translated(): bool - { - return $this->translatedLanguages(true) ? true : false; - } - /** * @param string|array $query * @return Route|null @@ -282,7 +277,7 @@ class PageObject extends FlexPageObject /** * @param array|bool $reorder - * @return FlexObject|FlexObjectInterface + * @return static */ public function save($reorder = true) { @@ -317,7 +312,7 @@ class PageObject extends FlexPageObject } /** - * @return PageObject + * @return static */ public function delete() { @@ -391,6 +386,7 @@ class PageObject extends FlexPageObject /** * @param array $ordering * @return PageCollection|null + * @phpstan-return ObjectCollection|null */ protected function reorderSiblings(array $ordering) { @@ -466,7 +462,9 @@ class PageObject extends FlexPageObject if ($isMoved && $this->order() !== false) { $parentKey = $this->getProperty('parent_key'); if ($parentKey === '') { - $newParent = $this->getFlexDirectory()->getIndex()->getRoot(); + /** @var PageIndex $index */ + $index = $this->getFlexDirectory()->getIndex(); + $newParent = $index->getRoot(); } else { $newParent = $this->getFlexDirectory()->getObject($parentKey, 'storage_key'); if (!$newParent instanceof PageInterface) { diff --git a/system/src/Grav/Common/Flex/Types/Pages/Storage/PageStorage.php b/system/src/Grav/Common/Flex/Types/Pages/Storage/PageStorage.php index ba7fa06..f590e36 100644 --- a/system/src/Grav/Common/Flex/Types/Pages/Storage/PageStorage.php +++ b/system/src/Grav/Common/Flex/Types/Pages/Storage/PageStorage.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -16,13 +16,13 @@ use Grav\Common\Debugger; use Grav\Common\Flex\Types\Pages\PageIndex; use Grav\Common\Grav; use Grav\Common\Language\Language; +use Grav\Common\Utils; use Grav\Framework\Filesystem\Filesystem; use Grav\Framework\Flex\Storage\FolderStorage; use RocketTheme\Toolbox\File\MarkdownFile; use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; use RuntimeException; use SplFileInfo; -use function assert; use function in_array; use function is_string; @@ -277,7 +277,7 @@ class PageStorage extends FolderStorage } else { $params = $template = $language = ''; } - $objectKey = basename($key); + $objectKey = Utils::basename($key); if (preg_match('|^(\d+)\.(.+)$|', $objectKey, $matches)) { [, $order, $folder] = $matches; } else { @@ -584,7 +584,7 @@ class PageStorage extends FolderStorage $mark = $matches[2] ?? ''; $ext = $matches[1] ?? ''; $ext .= $this->dataExt; - $markdown[$mark][basename($k, $ext)] = $timestamp; + $markdown[$mark][Utils::basename($k, $ext)] = $timestamp; } $modified = max($modified, $timestamp); @@ -675,7 +675,7 @@ class PageStorage extends FolderStorage /** @phpstan-var array{'storage_key': string, 'storage_timestamp': int, 'children': array} $parent */ $parent = &$list[$parentKey]; - $basename = basename($storage_key); + $basename = Utils::basename($storage_key); if (isset($parent['children'][$basename])) { $timestamp = $meta['storage_timestamp']; diff --git a/system/src/Grav/Common/Flex/Types/Pages/Traits/PageContentTrait.php b/system/src/Grav/Common/Flex/Types/Pages/Traits/PageContentTrait.php index 1bde7b0..9a2c7c3 100644 --- a/system/src/Grav/Common/Flex/Types/Pages/Traits/PageContentTrait.php +++ b/system/src/Grav/Common/Flex/Types/Pages/Traits/PageContentTrait.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Flex/Types/Pages/Traits/PageLegacyTrait.php b/system/src/Grav/Common/Flex/Types/Pages/Traits/PageLegacyTrait.php index 589ea88..98f3949 100644 --- a/system/src/Grav/Common/Flex/Types/Pages/Traits/PageLegacyTrait.php +++ b/system/src/Grav/Common/Flex/Types/Pages/Traits/PageLegacyTrait.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Flex/Types/Pages/Traits/PageRoutableTrait.php b/system/src/Grav/Common/Flex/Types/Pages/Traits/PageRoutableTrait.php index 3b490a5..ace74dc 100644 --- a/system/src/Grav/Common/Flex/Types/Pages/Traits/PageRoutableTrait.php +++ b/system/src/Grav/Common/Flex/Types/Pages/Traits/PageRoutableTrait.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Flex/Types/Pages/Traits/PageTranslateTrait.php b/system/src/Grav/Common/Flex/Types/Pages/Traits/PageTranslateTrait.php index dff42c9..a30ae1a 100644 --- a/system/src/Grav/Common/Flex/Types/Pages/Traits/PageTranslateTrait.php +++ b/system/src/Grav/Common/Flex/Types/Pages/Traits/PageTranslateTrait.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Flex/Types/UserGroups/UserGroupCollection.php b/system/src/Grav/Common/Flex/Types/UserGroups/UserGroupCollection.php index 93abbf8..3a91d77 100644 --- a/system/src/Grav/Common/Flex/Types/UserGroups/UserGroupCollection.php +++ b/system/src/Grav/Common/Flex/Types/UserGroups/UserGroupCollection.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -27,7 +27,7 @@ class UserGroupCollection extends FlexCollection public static function getCachedMethods(): array { return [ - 'authorize' => 'session', + 'authorize' => false, ] + parent::getCachedMethods(); } diff --git a/system/src/Grav/Common/Flex/Types/UserGroups/UserGroupIndex.php b/system/src/Grav/Common/Flex/Types/UserGroups/UserGroupIndex.php index 4bee5ac..66de52e 100644 --- a/system/src/Grav/Common/Flex/Types/UserGroups/UserGroupIndex.php +++ b/system/src/Grav/Common/Flex/Types/UserGroups/UserGroupIndex.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Flex/Types/UserGroups/UserGroupObject.php b/system/src/Grav/Common/Flex/Types/UserGroups/UserGroupObject.php index ea68fa1..4a59d52 100644 --- a/system/src/Grav/Common/Flex/Types/UserGroups/UserGroupObject.php +++ b/system/src/Grav/Common/Flex/Types/UserGroups/UserGroupObject.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -37,7 +37,7 @@ class UserGroupObject extends FlexObject implements UserGroupInterface public static function getCachedMethods(): array { return [ - 'authorize' => 'session', + 'authorize' => false, ] + parent::getCachedMethods(); } diff --git a/system/src/Grav/Common/Flex/Types/Users/Storage/UserFileStorage.php b/system/src/Grav/Common/Flex/Types/Users/Storage/UserFileStorage.php index f565c9f..664f24f 100644 --- a/system/src/Grav/Common/Flex/Types/Users/Storage/UserFileStorage.php +++ b/system/src/Grav/Common/Flex/Types/Users/Storage/UserFileStorage.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Flex/Types/Users/Storage/UserFolderStorage.php b/system/src/Grav/Common/Flex/Types/Users/Storage/UserFolderStorage.php index 7d9924e..a037f0e 100644 --- a/system/src/Grav/Common/Flex/Types/Users/Storage/UserFolderStorage.php +++ b/system/src/Grav/Common/Flex/Types/Users/Storage/UserFolderStorage.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Flex/Types/Users/Traits/UserObjectLegacyTrait.php b/system/src/Grav/Common/Flex/Types/Users/Traits/UserObjectLegacyTrait.php index b0fc0c4..07acf66 100644 --- a/system/src/Grav/Common/Flex/Types/Users/Traits/UserObjectLegacyTrait.php +++ b/system/src/Grav/Common/Flex/Types/Users/Traits/UserObjectLegacyTrait.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -84,6 +84,7 @@ trait UserObjectLegacyTrait * @return int * @deprecated 1.6 Method makes no sense for user account. */ + #[\ReturnTypeWillChange] public function count() { user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.6', E_USER_DEPRECATED); diff --git a/system/src/Grav/Common/Flex/Types/Users/UserCollection.php b/system/src/Grav/Common/Flex/Types/Users/UserCollection.php index 9b6490a..f5641e7 100644 --- a/system/src/Grav/Common/Flex/Types/Users/UserCollection.php +++ b/system/src/Grav/Common/Flex/Types/Users/UserCollection.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Flex/Types/Users/UserIndex.php b/system/src/Grav/Common/Flex/Types/Users/UserIndex.php index 6e0bc65..4a33b73 100644 --- a/system/src/Grav/Common/Flex/Types/Users/UserIndex.php +++ b/system/src/Grav/Common/Flex/Types/Users/UserIndex.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -164,7 +164,7 @@ class UserIndex extends FlexIndex implements UserCollectionInterface */ protected static function filterUsername(string $key, FlexStorageInterface $storage): string { - return $storage->normalizeKey($key); + return method_exists($storage, 'normalizeKey') ? $storage->normalizeKey($key) : $key; } /** diff --git a/system/src/Grav/Common/Flex/Types/Users/UserObject.php b/system/src/Grav/Common/Flex/Types/Users/UserObject.php index 310c313..d921791 100644 --- a/system/src/Grav/Common/Flex/Types/Users/UserObject.php +++ b/system/src/Grav/Common/Flex/Types/Users/UserObject.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -30,6 +30,7 @@ use Grav\Common\Flex\Types\UserGroups\UserGroupCollection; use Grav\Common\Flex\Types\UserGroups\UserGroupIndex; use Grav\Common\User\Interfaces\UserInterface; use Grav\Common\User\Traits\UserTrait; +use Grav\Common\Utils; use Grav\Framework\File\Formatter\JsonFormatter; use Grav\Framework\File\Formatter\YamlFormatter; use Grav\Framework\Filesystem\Filesystem; @@ -79,6 +80,8 @@ class UserObject extends FlexObject implements UserInterface, Countable /** @var Closure|null */ static public $authorizeCallable; + /** @var Closure|null */ + static public $isAuthorizedCallable; /** @var array|null */ protected $_uploads_original; @@ -123,7 +126,8 @@ class UserObject extends FlexObject implements UserInterface, Countable // Define username if it's not set. if (!isset($elements['username'])) { $storageKey = $elements['__META']['storage_key'] ?? null; - if (null !== $storageKey && $key === $directory->getStorage()->normalizeKey($storageKey)) { + $storage = $directory->getStorage(); + if (null !== $storageKey && method_exists($storage, 'normalizeKey') && $key === $storage->normalizeKey($storageKey)) { $elements['username'] = $storageKey; } else { $elements['username'] = $key; @@ -138,6 +142,14 @@ class UserObject extends FlexObject implements UserInterface, Countable parent::__construct($elements, $key, $directory, $validate); } + public function __clone() + { + $this->_access = null; + $this->_groups = null; + + parent::__clone(); + } + /** * @return void */ @@ -230,6 +242,22 @@ class UserObject extends FlexObject implements UserInterface, Countable return $this; } + /** + * @param UserInterface|null $user + * @return bool + */ + public function isMyself(?UserInterface $user = null): bool + { + if (null === $user) { + $user = $this->getActiveUser(); + if ($user && !$user->authenticated) { + $user = null; + } + } + + return $user && $this->username === $user->username; + } + /** * Checks user authorization to the action. * @@ -264,10 +292,11 @@ class UserObject extends FlexObject implements UserInterface, Countable } } + // Check custom application access. $authorizeCallable = static::$authorizeCallable; if ($authorizeCallable instanceof Closure) { - $authorizeCallable->bindTo($this); - $authorized = $authorizeCallable($action, $scope); + $callable = $authorizeCallable->bindTo($this, $this); + $authorized = $callable($action, $scope); if (is_bool($authorized)) { return $authorized; } @@ -280,13 +309,14 @@ class UserObject extends FlexObject implements UserInterface, Countable return $authorized; } - // If specific rule isn't hit, check if user is super user. - if ($access->authorize('admin.super') === true) { - return true; + // Check group access. + $authorized = $this->getGroups()->authorize($action, $scope); + if (is_bool($authorized)) { + return $authorized; } - // Check group access. - return $this->getGroups()->authorize($action, $scope); + // If any specific rule isn't hit, check if user is a superuser. + return $access->authorize('admin.super') === true; } /** @@ -616,7 +646,7 @@ class UserObject extends FlexObject implements UserInterface, Countable $medium = MediumFactory::fromFile($path); if ($medium) { $media->add($path, $medium); - $name = basename($path); + $name = Utils::basename($path); if ($name !== $path) { $media->add($name, $medium); } @@ -671,6 +701,16 @@ class UserObject extends FlexObject implements UserInterface, Countable */ protected function isAuthorizedOverride(UserInterface $user, string $action, string $scope, bool $isMe = false): ?bool { + // Check custom application access. + $isAuthorizedCallable = static::$isAuthorizedCallable; + if ($isAuthorizedCallable instanceof Closure) { + $callable = $isAuthorizedCallable->bindTo($this, $this); + $authorized = $callable($user, $action, $scope, $isMe); + if (is_bool($authorized)) { + return $authorized; + } + } + if ($user instanceof self && $user->getStorageKey() === $this->getStorageKey()) { // User cannot delete his own account, otherwise he has full access. return $action !== 'delete'; @@ -775,7 +815,7 @@ class UserObject extends FlexObject implements UserInterface, Countable } // Calculate path without the retina scaling factor. - $realpath = $filesystem->pathname($filepath) . str_replace(['@3x', '@2x'], '', basename($filepath)); + $realpath = $filesystem->pathname($filepath) . str_replace(['@3x', '@2x'], '', Utils::basename($filepath)); $list[$filename] = [$file, $settings]; @@ -886,7 +926,9 @@ class UserObject extends FlexObject implements UserInterface, Countable protected function getGroups() { if (null === $this->_groups) { - $this->_groups = $this->getUserGroups()->select((array)$this->getProperty('groups')); + /** @var UserGroupIndex $groups */ + $groups = $this->getUserGroups()->select((array)$this->getProperty('groups')); + $this->_groups = $groups; } return $this->_groups; @@ -898,7 +940,7 @@ class UserObject extends FlexObject implements UserInterface, Countable protected function getAccess(): Access { if (null === $this->_access) { - $this->getProperty('access'); + $this->_access = new Access($this->getProperty('access')); } return $this->_access; @@ -914,8 +956,6 @@ class UserObject extends FlexObject implements UserInterface, Countable $value = new Access($value); } - $this->_access = $value; - return $value->jsonSerialize(); } diff --git a/system/src/Grav/Common/Form/FormFlash.php b/system/src/Grav/Common/Form/FormFlash.php index 430f15e..7acaf7f 100644 --- a/system/src/Grav/Common/Form/FormFlash.php +++ b/system/src/Grav/Common/Form/FormFlash.php @@ -3,13 +3,14 @@ /** * @package Grav\Common\Form * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Common\Form; use Grav\Common\Filesystem\Folder; +use Grav\Common\Utils; use Grav\Framework\Form\FormFlash as FrameworkFormFlash; use function is_array; @@ -58,7 +59,7 @@ class FormFlash extends FrameworkFormFlash Folder::create($tmp_dir); $tmp_file = $upload['file']['tmp_name']; - $basename = basename($tmp_file); + $basename = Utils::basename($tmp_file); if (!move_uploaded_file($tmp_file, $tmp_dir . '/' . $basename)) { return false; @@ -90,7 +91,7 @@ class FormFlash extends FrameworkFormFlash Folder::create($tmp_dir); $tmp_file = $upload['file']['tmp_name']; - $basename = basename($tmp_file); + $basename = Utils::basename($tmp_file); if (!move_uploaded_file($tmp_file, $tmp_dir . '/' . $basename)) { return false; diff --git a/system/src/Grav/Common/GPM/AbstractCollection.php b/system/src/Grav/Common/GPM/AbstractCollection.php index b5b867e..91fa69e 100644 --- a/system/src/Grav/Common/GPM/AbstractCollection.php +++ b/system/src/Grav/Common/GPM/AbstractCollection.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\GPM * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Common/AbstractPackageCollection.php b/system/src/Grav/Common/GPM/Common/AbstractPackageCollection.php index 5d6ad9f..7b918e0 100644 --- a/system/src/Grav/Common/GPM/Common/AbstractPackageCollection.php +++ b/system/src/Grav/Common/GPM/Common/AbstractPackageCollection.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\GPM * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Common/CachedCollection.php b/system/src/Grav/Common/GPM/Common/CachedCollection.php index 172ba93..c3b62e5 100644 --- a/system/src/Grav/Common/GPM/Common/CachedCollection.php +++ b/system/src/Grav/Common/GPM/Common/CachedCollection.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\GPM * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Common/Package.php b/system/src/Grav/Common/GPM/Common/Package.php index e06ebb6..5d96011 100644 --- a/system/src/Grav/Common/GPM/Common/Package.php +++ b/system/src/Grav/Common/GPM/Common/Package.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\GPM * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -45,6 +45,7 @@ class Package * @param string $key * @return mixed */ + #[\ReturnTypeWillChange] public function __get($key) { return $this->data->get($key); @@ -55,6 +56,7 @@ class Package * @param mixed $value * @return void */ + #[\ReturnTypeWillChange] public function __set($key, $value) { $this->data->set($key, $value); @@ -64,6 +66,7 @@ class Package * @param string $key * @return bool */ + #[\ReturnTypeWillChange] public function __isset($key) { return isset($this->data->{$key}); @@ -72,6 +75,7 @@ class Package /** * @return string */ + #[\ReturnTypeWillChange] public function __toString() { return $this->toJson(); diff --git a/system/src/Grav/Common/GPM/GPM.php b/system/src/Grav/Common/GPM/GPM.php index 4505696..7031945 100644 --- a/system/src/Grav/Common/GPM/GPM.php +++ b/system/src/Grav/Common/GPM/GPM.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\GPM * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -74,6 +74,7 @@ class GPM extends Iterator * @param string $offset Asset name value * @return mixed Asset value */ + #[\ReturnTypeWillChange] public function __get($offset) { switch ($offset) { @@ -90,6 +91,7 @@ class GPM extends Iterator * @param string $offset Asset name value * @return bool True if the value is set */ + #[\ReturnTypeWillChange] public function __isset($offset) { switch ($offset) { @@ -629,7 +631,7 @@ class GPM extends Iterator throw new \RuntimeException("Malformed GPM URL: {$package_file}"); } - $filename = basename($package['path'] ?? ''); + $filename = Utils::basename($package['path'] ?? ''); if (Grav::instance()['config']->get('system.gpm.official_gpm_only') && ($package['host'] ?? null) !== 'getgrav.org') { throw new RuntimeException('Only official GPM URLs are allowed. You can modify this behavior in the System configuration.'); @@ -658,7 +660,7 @@ class GPM extends Iterator $package_file = realpath($package_file); if ($package_file && file_exists($package_file)) { - $filename = basename($package_file); + $filename = Utils::basename($package_file); Folder::create($tmp); copy($package_file, $tmp . DS . $filename); return $tmp . DS . $filename; @@ -690,7 +692,7 @@ class GPM extends Iterator } // either theme or plugin - $name = basename($source); + $name = Utils::basename($source); if (Utils::contains($name, 'theme')) { return 'theme'; } @@ -728,7 +730,7 @@ class GPM extends Iterator $glob = glob($source . '*.yaml') ?: []; foreach ($glob as $filename) { - $name = strtolower(basename($filename, '.yaml')); + $name = strtolower(Utils::basename($filename, '.yaml')); if (in_array($name, $ignore_yaml_files)) { continue; } diff --git a/system/src/Grav/Common/GPM/Installer.php b/system/src/Grav/Common/GPM/Installer.php index 639240b..8a1a530 100644 --- a/system/src/Grav/Common/GPM/Installer.php +++ b/system/src/Grav/Common/GPM/Installer.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\GPM * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -12,6 +12,7 @@ namespace Grav\Common\GPM; use DirectoryIterator; use Grav\Common\Filesystem\Folder; use Grav\Common\Grav; +use Grav\Common\Utils; use RuntimeException; use ZipArchive; use function count; @@ -192,7 +193,7 @@ class Installer $package_folder_name = $zip->getNameIndex(0); if ($package_folder_name === false) { - throw new \RuntimeException('Bad package file: ' . basename($zip_file)); + throw new \RuntimeException('Bad package file: ' . Utils::basename($zip_file)); } $package_folder_name = preg_replace('#\./$#', '', $package_folder_name); $zip->close(); diff --git a/system/src/Grav/Common/GPM/Licenses.php b/system/src/Grav/Common/GPM/Licenses.php index 825343d..e932bd4 100644 --- a/system/src/Grav/Common/GPM/Licenses.php +++ b/system/src/Grav/Common/GPM/Licenses.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\GPM * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Local/AbstractPackageCollection.php b/system/src/Grav/Common/GPM/Local/AbstractPackageCollection.php index d1f3e46..bc7ceab 100644 --- a/system/src/Grav/Common/GPM/Local/AbstractPackageCollection.php +++ b/system/src/Grav/Common/GPM/Local/AbstractPackageCollection.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\GPM * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Local/Package.php b/system/src/Grav/Common/GPM/Local/Package.php index ffe2c63..08444a7 100644 --- a/system/src/Grav/Common/GPM/Local/Package.php +++ b/system/src/Grav/Common/GPM/Local/Package.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\GPM * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Local/Packages.php b/system/src/Grav/Common/GPM/Local/Packages.php index 0290296..b63b93b 100644 --- a/system/src/Grav/Common/GPM/Local/Packages.php +++ b/system/src/Grav/Common/GPM/Local/Packages.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\GPM * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Local/Plugins.php b/system/src/Grav/Common/GPM/Local/Plugins.php index 8a6574b..a1626f8 100644 --- a/system/src/Grav/Common/GPM/Local/Plugins.php +++ b/system/src/Grav/Common/GPM/Local/Plugins.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\GPM * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Local/Themes.php b/system/src/Grav/Common/GPM/Local/Themes.php index 73253cc..d153794 100644 --- a/system/src/Grav/Common/GPM/Local/Themes.php +++ b/system/src/Grav/Common/GPM/Local/Themes.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\GPM * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Remote/AbstractPackageCollection.php b/system/src/Grav/Common/GPM/Remote/AbstractPackageCollection.php index 9e68d66..02e98e7 100644 --- a/system/src/Grav/Common/GPM/Remote/AbstractPackageCollection.php +++ b/system/src/Grav/Common/GPM/Remote/AbstractPackageCollection.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\GPM * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Remote/GravCore.php b/system/src/Grav/Common/GPM/Remote/GravCore.php index f93209e..2998975 100644 --- a/system/src/Grav/Common/GPM/Remote/GravCore.php +++ b/system/src/Grav/Common/GPM/Remote/GravCore.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\GPM * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Remote/Package.php b/system/src/Grav/Common/GPM/Remote/Package.php index c37d6a0..5193255 100644 --- a/system/src/Grav/Common/GPM/Remote/Package.php +++ b/system/src/Grav/Common/GPM/Remote/Package.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\GPM * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -32,6 +32,7 @@ class Package extends BasePackage implements \JsonSerializable /** * @return array */ + #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->data->toArray(); diff --git a/system/src/Grav/Common/GPM/Remote/Packages.php b/system/src/Grav/Common/GPM/Remote/Packages.php index f55c12d..0ac2ef9 100644 --- a/system/src/Grav/Common/GPM/Remote/Packages.php +++ b/system/src/Grav/Common/GPM/Remote/Packages.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\GPM * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Remote/Plugins.php b/system/src/Grav/Common/GPM/Remote/Plugins.php index a134a10..8383cd7 100644 --- a/system/src/Grav/Common/GPM/Remote/Plugins.php +++ b/system/src/Grav/Common/GPM/Remote/Plugins.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\GPM * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Remote/Themes.php b/system/src/Grav/Common/GPM/Remote/Themes.php index 160ac97..a8ee9fa 100644 --- a/system/src/Grav/Common/GPM/Remote/Themes.php +++ b/system/src/Grav/Common/GPM/Remote/Themes.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\GPM * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/GPM/Response.php b/system/src/Grav/Common/GPM/Response.php index 47cef7c..98654b6 100644 --- a/system/src/Grav/Common/GPM/Response.php +++ b/system/src/Grav/Common/GPM/Response.php @@ -1,143 +1,3 @@ 'Grav CMS' - ]; - - /** - * Makes a request to the URL by using the preferred method - * - * @param string $uri URL to call - * @param array $overrides An array of parameters for both `curl` and `fopen` - * @param callable|null $callback Either a function or callback in array notation - * @return string The response of the request - * @throws TransportExceptionInterface - */ - public static function get($uri = '', $overrides = [], $callback = null) - { - if (empty($uri)) { - throw new TransportException('missing URI'); - } - - // check if this function is available, if so use it to stop any timeouts - try { - if (Utils::functionExists('set_time_limit')) { - @set_time_limit(0); - } - } catch (Exception $e) { - } - - $config = Grav::instance()['config']; - $referer = defined('GRAV_CLI') ? 'grav_cli' : Grav::instance()['uri']->rootUrl(true); - $options = new HttpOptions(); - - // Set default Headers - $options->setHeaders(array_merge([ 'Referer' => $referer ], self::$headers)); - - // Disable verify Peer if required - $verify_peer = $config->get('system.gpm.verify_peer', true); - if ($verify_peer !== true) { - $options->verifyPeer($verify_peer); - } - - // Set proxy url if provided - $proxy_url = $config->get('system.gpm.proxy_url', false); - if ($proxy_url) { - $options->setProxy($proxy_url); - } - - // Use callback if provided - if ($callback) { - self::$callback = $callback; - $options->setOnProgress([Response::class, 'progress']); - } - - $preferred_method = $config->get('system.gpm.method', 'auto'); - - $settings = array_merge_recursive($options->toArray(), $overrides); - - switch ($preferred_method) { - case 'curl': - $client = new CurlHttpClient($settings); - break; - case 'fopen': - case 'native': - $client = new NativeHttpClient($settings); - break; - default: - $client = HttpClient::create($settings); - } - - $response = $client->request('GET', $uri); - - return $response->getContent(); - } - - - /** - * Is this a remote file or not - * - * @param string $file - * @return bool - */ - public static function isRemote($file) - { - return (bool) filter_var($file, FILTER_VALIDATE_URL); - } - - /** - * Progress normalized for cURL and Fopen - * Accepts a variable length of arguments passed in by stream method - * - * @return void - */ - public static function progress(int $bytes_transferred, int $filesize, array $info) - { - - if ($bytes_transferred > 0) { - $percent = $filesize <= 0 ? 0 : (int)(($bytes_transferred * 100) / $filesize); - - $progress = [ - 'code' => $info['http_code'], - 'filesize' => $filesize, - 'transferred' => $bytes_transferred, - 'percent' => $percent < 100 ? $percent : 100 - ]; - - if (self::$callback !== null) { - call_user_func(self::$callback, $progress); - } - } - } -} +// Create alias for the deprecated class. +class_alias(\Grav\Common\HTTP\Response::class, \Grav\Common\GPM\Response::class); diff --git a/system/src/Grav/Common/GPM/Upgrader.php b/system/src/Grav/Common/GPM/Upgrader.php index dfa6eb1..b3e6869 100644 --- a/system/src/Grav/Common/GPM/Upgrader.php +++ b/system/src/Grav/Common/GPM/Upgrader.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\GPM * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Getters.php b/system/src/Grav/Common/Getters.php index 8f3a73a..ed03499 100644 --- a/system/src/Grav/Common/Getters.php +++ b/system/src/Grav/Common/Getters.php @@ -3,7 +3,7 @@ /** * @package Grav\Common * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -28,6 +28,7 @@ abstract class Getters implements ArrayAccess, Countable * @param int|string $offset Medium name value * @param mixed $value Medium value */ + #[\ReturnTypeWillChange] public function __set($offset, $value) { $this->offsetSet($offset, $value); @@ -39,6 +40,7 @@ abstract class Getters implements ArrayAccess, Countable * @param int|string $offset Medium name value * @return mixed Medium value */ + #[\ReturnTypeWillChange] public function __get($offset) { return $this->offsetGet($offset); @@ -50,6 +52,7 @@ abstract class Getters implements ArrayAccess, Countable * @param int|string $offset Medium name value * @return boolean True if the value is set */ + #[\ReturnTypeWillChange] public function __isset($offset) { return $this->offsetExists($offset); @@ -60,6 +63,7 @@ abstract class Getters implements ArrayAccess, Countable * * @param int|string $offset The name value to unset */ + #[\ReturnTypeWillChange] public function __unset($offset) { $this->offsetUnset($offset); @@ -69,6 +73,7 @@ abstract class Getters implements ArrayAccess, Countable * @param int|string $offset * @return bool */ + #[\ReturnTypeWillChange] public function offsetExists($offset) { if ($this->gettersVariable) { @@ -84,6 +89,7 @@ abstract class Getters implements ArrayAccess, Countable * @param int|string $offset * @return mixed */ + #[\ReturnTypeWillChange] public function offsetGet($offset) { if ($this->gettersVariable) { @@ -99,6 +105,7 @@ abstract class Getters implements ArrayAccess, Countable * @param int|string $offset * @param mixed $value */ + #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { if ($this->gettersVariable) { @@ -112,6 +119,7 @@ abstract class Getters implements ArrayAccess, Countable /** * @param int|string $offset */ + #[\ReturnTypeWillChange] public function offsetUnset($offset) { if ($this->gettersVariable) { @@ -125,6 +133,7 @@ abstract class Getters implements ArrayAccess, Countable /** * @return int */ + #[\ReturnTypeWillChange] public function count() { if ($this->gettersVariable) { diff --git a/system/src/Grav/Common/Grav.php b/system/src/Grav/Common/Grav.php index f591961..18d4902 100644 --- a/system/src/Grav/Common/Grav.php +++ b/system/src/Grav/Common/Grav.php @@ -3,7 +3,7 @@ /** * @package Grav\Common * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -49,7 +49,9 @@ use Grav\Common\Twig\Twig; use Grav\Framework\DI\Container; use Grav\Framework\Psr7\Response; use Grav\Framework\RequestHandler\RequestHandler; +use Grav\Framework\Route\Route; use Grav\Framework\Session\Messages; +use InvalidArgumentException; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use RocketTheme\Toolbox\Event\Event; @@ -62,6 +64,7 @@ use function get_class; use function in_array; use function is_callable; use function is_int; +use function is_string; use function strlen; /** @@ -343,7 +346,7 @@ class Grav extends Container * Please use this method instead of calling `die();` or `exit();`. Note that you need to create a response object. * * @param ResponseInterface $response - * @return void + * @return never-return */ public function close(ResponseInterface $response): void { @@ -395,7 +398,7 @@ class Grav extends Container /** * @param ResponseInterface $response - * @return void + * @return never-return * @deprecated 1.7 Do not use */ public function exit(ResponseInterface $response): void @@ -408,9 +411,9 @@ class Grav extends Container * * Please use this method instead of calling `header("Location: {$url}", true, 302); exit();`. * - * @param string $route Internal route. + * @param Route|string $route Internal route. * @param int|null $code Redirection code (30x) - * @return void + * @return never-return */ public function redirect($route, $code = null): void { @@ -422,7 +425,7 @@ class Grav extends Container /** * Returns redirect response object from Grav. * - * @param string $route Internal route. + * @param Route|string $route Internal route. * @param int|null $code Redirection code (30x) * @return ResponseInterface */ @@ -431,37 +434,47 @@ class Grav extends Container /** @var Uri $uri */ $uri = $this['uri']; - // Clean route for redirect - $route = preg_replace("#^\/[\\\/]+\/#", '/', $route); + if (is_string($route)) { + // Clean route for redirect + $route = preg_replace("#^\/[\\\/]+\/#", '/', $route); + + if (null === $code) { + // Check for redirect code in the route: e.g. /new/[301], /new[301]/route or /new[301].html + $regex = '/.*(\[(30[1-7])\])(.\w+|\/.*?)?$/'; + preg_match($regex, $route, $matches); + if ($matches) { + $route = str_replace($matches[1], '', $matches[0]); + $code = $matches[2]; + } + } + + if ($uri::isExternal($route)) { + $url = $route; + } else { + $url = rtrim($uri->rootUrl(), '/') . '/'; + + if ($this['config']->get('system.pages.redirect_trailing_slash', true)) { + $url .= trim($route, '/'); // Remove trailing slash + } else { + $url .= ltrim($route, '/'); // Support trailing slash default routes + } + } + } elseif ($route instanceof Route) { + $url = $route->toString(true); + } else { + throw new InvalidArgumentException('Bad $route'); + } if ($code < 300 || $code > 399) { $code = null; } - if (null === $code) { - // Check for redirect code in the route: e.g. /new/[301], /new[301]/route or /new[301].html - $regex = '/.*(\[(30[1-7])\])(.\w+|\/.*?)?$/'; - preg_match($regex, $route, $matches); - if ($matches) { - $route = str_replace($matches[1], '', $matches[0]); - $code = $matches[2]; - } - } - if ($code === null) { $code = $this['config']->get('system.pages.redirect_default_code', 302); } - if ($uri::isExternal($route)) { - $url = $route; - } else { - $url = rtrim($uri->rootUrl(), '/') . '/'; - - if ($this['config']->get('system.pages.redirect_trailing_slash', true)) { - $url .= trim($route, '/'); // Remove trailing slash - } else { - $url .= ltrim($route, '/'); // Support trailing slash default routes - } + if ($uri->extension() === 'json') { + return new Response(200, ['Content-Type' => 'application/json'], json_encode(['code' => $code, 'redirect' => $url], JSON_THROW_ON_ERROR)); } return new Response($code, ['Location' => $url]); @@ -639,6 +652,7 @@ class Grav extends Container * @param array $args * @return mixed|null */ + #[\ReturnTypeWillChange] public function __call($method, $args) { $closure = $this->{$method} ?? null; @@ -715,18 +729,36 @@ class Grav extends Container */ public function fallbackUrl($path) { - $this->fireEvent('onPageFallBackUrl'); - /** @var Uri $uri */ $uri = $this['uri']; /** @var Config $config */ $config = $this['config']; - $uri_extension = strtolower($uri->extension()); - $fallback_types = $config->get('system.media.allowed_fallback_types', null); + $path_parts = Utils::pathinfo($path); + + /** @var Pages $pages */ + $pages = $this['pages']; + $page = $pages->find($path_parts['dirname'], true); + + $uri_extension = strtolower($uri->extension() ?? ''); + $fallback_types = $config->get('system.media.allowed_fallback_types'); $supported_types = $config->get('media.types'); + $parsed_url = parse_url(rawurldecode($uri->basename())); + $media_file = $parsed_url['path']; + + $event = new Event([ + 'uri' => $uri, + 'page' => &$page, + 'filename' => &$media_file, + 'extension' => $uri_extension, + 'allowed_fallback_types' => &$fallback_types, + 'media_types' => &$supported_types + ]); + + $this->fireEvent('onPageFallBackUrl', $event); + // Check whitelist first, then ensure extension is a valid media type if (!empty($fallback_types) && !in_array($uri_extension, $fallback_types, true)) { return false; @@ -735,16 +767,8 @@ class Grav extends Container return false; } - $path_parts = pathinfo($path); - - /** @var Pages $pages */ - $pages = $this['pages']; - $page = $pages->find($path_parts['dirname'], true); - if ($page) { $media = $page->media()->all(); - $parsed_url = parse_url(rawurldecode($uri->basename())); - $media_file = $parsed_url['path']; // if this is a media object, try actions first if (isset($media[$media_file])) { @@ -774,11 +798,9 @@ class Grav extends Container } Utils::download($page->path() . DIRECTORY_SEPARATOR . $uri->basename(), $download); } - - // Nothing found - return false; } - return $page ?? false; + // Nothing found + return false; } } diff --git a/system/src/Grav/Common/GravTrait.php b/system/src/Grav/Common/GravTrait.php index 4638905..5416286 100644 --- a/system/src/Grav/Common/GravTrait.php +++ b/system/src/Grav/Common/GravTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Common * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/HTTP/Client.php b/system/src/Grav/Common/HTTP/Client.php new file mode 100644 index 0000000..c85d6b2 --- /dev/null +++ b/system/src/Grav/Common/HTTP/Client.php @@ -0,0 +1,130 @@ + 'Grav CMS' + ]; + + public static function getClient(array $overrides = [], int $connections = 6, callable $callback = null): HttpClientInterface + { + $config = Grav::instance()['config']; + $options = static::getOptions(); + + // Use callback if provided + if ($callback) { + self::$callback = $callback; + $options->setOnProgress([Client::class, 'progress']); + } + + $settings = array_merge($options->toArray(), $overrides); + $preferred_method = $config->get('system.http.method'); + // Try old GPM setting if value is the same as system default + if ($preferred_method === 'auto') { + $preferred_method = $config->get('system.gpm.method', 'auto'); + } + + switch ($preferred_method) { + case 'curl': + $client = new CurlHttpClient($settings, $connections); + break; + case 'fopen': + case 'native': + $client = new NativeHttpClient($settings, $connections); + break; + default: + $client = HttpClient::create($settings, $connections); + } + + return $client; + } + + /** + * Get HTTP Options + * + * @return HttpOptions + */ + public static function getOptions(): HttpOptions + { + $config = Grav::instance()['config']; + $referer = defined('GRAV_CLI') ? 'grav_cli' : Grav::instance()['uri']->rootUrl(true); + + $options = new HttpOptions(); + + // Set default Headers + $options->setHeaders(array_merge([ 'Referer' => $referer ], self::$headers)); + + // Disable verify Peer if required + $verify_peer = $config->get('system.http.verify_peer'); + // Try old GPM setting if value is default + if ($verify_peer === true) { + $verify_peer = $config->get('system.gpm.verify_peer', null) ?? $verify_peer; + } + $options->verifyPeer($verify_peer); + + // Set verify Host + $verify_host = $config->get('system.http.verify_host', true); + $options->verifyHost($verify_host); + + // New setting and must be enabled for Proxy to work + if ($config->get('system.http.enable_proxy', true)) { + // Set proxy url if provided + $proxy_url = $config->get('system.http.proxy_url', $config->get('system.gpm.proxy_url', null)); + if ($proxy_url !== null) { + $options->setProxy($proxy_url); + } + + // Certificate + $proxy_cert = $config->get('system.http.proxy_cert_path', null); + if ($proxy_cert !== null) { + $options->setCaPath($proxy_cert); + } + } + + return $options; + } + + /** + * Progress normalized for cURL and Fopen + * Accepts a variable length of arguments passed in by stream method + * + * @return void + */ + public static function progress(int $bytes_transferred, int $filesize, array $info) + { + + if ($bytes_transferred > 0) { + $percent = $filesize <= 0 ? 0 : (int)(($bytes_transferred * 100) / $filesize); + + $progress = [ + 'code' => $info['http_code'], + 'filesize' => $filesize, + 'transferred' => $bytes_transferred, + 'percent' => $percent < 100 ? $percent : 100 + ]; + + if (self::$callback !== null) { + call_user_func(self::$callback, $progress); + } + } + } +} diff --git a/system/src/Grav/Common/HTTP/Response.php b/system/src/Grav/Common/HTTP/Response.php new file mode 100644 index 0000000..4a5bc8b --- /dev/null +++ b/system/src/Grav/Common/HTTP/Response.php @@ -0,0 +1,96 @@ +getContent(); + } + + + /** + * Makes a request to the URL by using the preferred method + * + * @param string $method method to call such as GET, PUT, etc + * @param string $uri URL to call + * @param array $overrides An array of parameters for both `curl` and `fopen` + * @param callable|null $callback Either a function or callback in array notation + * @return ResponseInterface + * @throws TransportExceptionInterface + */ + public static function request(string $method, string $uri, array $overrides = [], callable $callback = null): ResponseInterface + { + if (empty($method)) { + throw new TransportException('missing method (GET, PUT, etc.)'); + } + + if (empty($uri)) { + throw new TransportException('missing URI'); + } + + // check if this function is available, if so use it to stop any timeouts + try { + if (Utils::functionExists('set_time_limit')) { + @set_time_limit(0); + } + } catch (Exception $e) {} + + $client = Client::getClient($overrides, 6, $callback); + + return $client->request($method, $uri); + } + + + /** + * Is this a remote file or not + * + * @param string $file + * @return bool + */ + public static function isRemote($file): bool + { + return (bool) filter_var($file, FILTER_VALIDATE_URL); + } + + +} diff --git a/system/src/Grav/Common/Helpers/Base32.php b/system/src/Grav/Common/Helpers/Base32.php index 825c1ec..0d1c21a 100644 --- a/system/src/Grav/Common/Helpers/Base32.php +++ b/system/src/Grav/Common/Helpers/Base32.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Helpers * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Helpers/Excerpts.php b/system/src/Grav/Common/Helpers/Excerpts.php index 173850a..a62c7e5 100644 --- a/system/src/Grav/Common/Helpers/Excerpts.php +++ b/system/src/Grav/Common/Helpers/Excerpts.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Helpers * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Helpers/Exif.php b/system/src/Grav/Common/Helpers/Exif.php index 36e391f..a7dc973 100644 --- a/system/src/Grav/Common/Helpers/Exif.php +++ b/system/src/Grav/Common/Helpers/Exif.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Helpers * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Helpers/LogViewer.php b/system/src/Grav/Common/Helpers/LogViewer.php index 187828a..cf5ffe9 100644 --- a/system/src/Grav/Common/Helpers/LogViewer.php +++ b/system/src/Grav/Common/Helpers/LogViewer.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Helpers * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -145,7 +145,7 @@ class LogViewer 'logger' => $data['logger'], 'level' => $data['level'], 'message' => $data['message'], - 'trace' => isset($data['trace']) ? $this->parseTrace($data['trace']) : null, + 'trace' => isset($data['trace']) ? self::parseTrace($data['trace']) : null, 'context' => json_decode($data['context'], true), 'extra' => json_decode($data['extra'], true) ]; diff --git a/system/src/Grav/Common/Helpers/Truncator.php b/system/src/Grav/Common/Helpers/Truncator.php index 0a701b6..03c3eea 100644 --- a/system/src/Grav/Common/Helpers/Truncator.php +++ b/system/src/Grav/Common/Helpers/Truncator.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Helpers * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Helpers/YamlLinter.php b/system/src/Grav/Common/Helpers/YamlLinter.php index 45b5144..b31180f 100644 --- a/system/src/Grav/Common/Helpers/YamlLinter.php +++ b/system/src/Grav/Common/Helpers/YamlLinter.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Helpers * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -11,6 +11,7 @@ namespace Grav\Common\Helpers; use Exception; use Grav\Common\Grav; +use Grav\Common\Utils; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; use RegexIterator; @@ -108,7 +109,7 @@ class YamlLinter */ protected static function extractYaml($path) { - $extension = pathinfo($path, PATHINFO_EXTENSION); + $extension = Utils::pathinfo($path, PATHINFO_EXTENSION); if ($extension === 'md') { $file = MarkdownFile::instance($path); $contents = $file->frontmatter(); diff --git a/system/src/Grav/Common/Inflector.php b/system/src/Grav/Common/Inflector.php index 50c218f..216b733 100644 --- a/system/src/Grav/Common/Inflector.php +++ b/system/src/Grav/Common/Inflector.php @@ -3,7 +3,7 @@ /** * @package Grav\Common * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Iterator.php b/system/src/Grav/Common/Iterator.php index 6e80b94..5db7bd0 100644 --- a/system/src/Grav/Common/Iterator.php +++ b/system/src/Grav/Common/Iterator.php @@ -3,7 +3,7 @@ /** * @package Grav\Common * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -38,6 +38,7 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable * @param mixed $args * @return mixed */ + #[\ReturnTypeWillChange] public function __call($key, $args) { return $this->items[$key] ?? null; @@ -46,6 +47,7 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable /** * Clone the iterator. */ + #[\ReturnTypeWillChange] public function __clone() { foreach ($this as $key => $value) { @@ -60,6 +62,7 @@ class Iterator implements \ArrayAccess, \Iterator, \Countable, \Serializable * * @return string */ + #[\ReturnTypeWillChange] public function __toString() { return implode(',', $this->items); diff --git a/system/src/Grav/Common/Language/Language.php b/system/src/Grav/Common/Language/Language.php index 6709754..fe006ed 100644 --- a/system/src/Grav/Common/Language/Language.php +++ b/system/src/Grav/Common/Language/Language.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Language * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -636,6 +636,7 @@ class Language /** * @return array */ + #[\ReturnTypeWillChange] public function __debugInfo() { $vars = get_object_vars($this); diff --git a/system/src/Grav/Common/Language/LanguageCodes.php b/system/src/Grav/Common/Language/LanguageCodes.php index f0a3532..3fa4948 100644 --- a/system/src/Grav/Common/Language/LanguageCodes.php +++ b/system/src/Grav/Common/Language/LanguageCodes.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Language * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -86,12 +86,14 @@ class LanguageCodes 'ja-JP' => [ 'name' => 'Japanese', 'nativeName' => '日本語' ], // not iso-639-1 'ka' => [ 'name' => 'Georgian', 'nativeName' => 'ქართული' ], 'kk' => [ 'name' => 'Kazakh', 'nativeName' => 'Қазақ' ], + 'km' => [ 'name' => 'Khmer', 'nativeName' => 'Khmer' ], 'kn' => [ 'name' => 'Kannada', 'nativeName' => 'ಕನ್ನಡ' ], 'ko' => [ 'name' => 'Korean', 'nativeName' => '한국어' ], 'ku' => [ 'name' => 'Kurdish', 'nativeName' => 'Kurdî' ], 'la' => [ 'name' => 'Latin', 'nativeName' => 'Latina' ], 'lb' => [ 'name' => 'Luxembourgish', 'nativeName' => 'Lëtzebuergesch' ], 'lg' => [ 'name' => 'Luganda', 'nativeName' => 'Luganda' ], + 'lo' => [ 'name' => 'Lao', 'nativeName' => 'Lao' ], 'lt' => [ 'name' => 'Lithuanian', 'nativeName' => 'Lietuvių' ], 'lv' => [ 'name' => 'Latvian', 'nativeName' => 'Latviešu' ], 'mai' => [ 'name' => 'Maithili', 'nativeName' => 'मैथिली মৈথিলী' ], @@ -101,6 +103,7 @@ class LanguageCodes 'ml' => [ 'name' => 'Malayalam', 'nativeName' => 'മലയാളം' ], 'mn' => [ 'name' => 'Mongolian', 'nativeName' => 'Монгол' ], 'mr' => [ 'name' => 'Marathi', 'nativeName' => 'मराठी' ], + 'my' => [ 'name' => 'Myanmar (Burmese)', 'nativeName' => 'ဗမာी' ], 'no' => [ 'name' => 'Norwegian', 'nativeName' => 'Norsk' ], 'nb' => [ 'name' => 'Norwegian', 'nativeName' => 'Norsk' ], 'nb-NO' => [ 'name' => 'Norwegian (Bokmål)', 'nativeName' => 'Norsk bokmål' ], @@ -132,6 +135,7 @@ class LanguageCodes 'st' => [ 'name' => 'Southern Sotho', 'nativeName' => 'Sesotho' ], 'sv' => [ 'name' => 'Swedish', 'nativeName' => 'Svenska' ], 'sv-SE' => [ 'name' => 'Swedish', 'nativeName' => 'Svenska' ], + 'sw' => [ 'name' => 'Swahili', 'nativeName' => 'Swahili' ], 'ta' => [ 'name' => 'Tamil', 'nativeName' => 'தமிழ்' ], 'ta-IN' => [ 'name' => 'Tamil (India)', 'nativeName' => 'தமிழ் (இந்தியா)' ], 'ta-LK' => [ 'name' => 'Tamil (Sri Lanka)', 'nativeName' => 'தமிழ் (இலங்கை)' ], diff --git a/system/src/Grav/Common/Markdown/Parsedown.php b/system/src/Grav/Common/Markdown/Parsedown.php index bdc5bdc..f0d8a48 100644 --- a/system/src/Grav/Common/Markdown/Parsedown.php +++ b/system/src/Grav/Common/Markdown/Parsedown.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Markdown * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Markdown/ParsedownExtra.php b/system/src/Grav/Common/Markdown/ParsedownExtra.php index b8e760e..ef450fe 100644 --- a/system/src/Grav/Common/Markdown/ParsedownExtra.php +++ b/system/src/Grav/Common/Markdown/ParsedownExtra.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Markdown * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Markdown/ParsedownGravTrait.php b/system/src/Grav/Common/Markdown/ParsedownGravTrait.php index 7c2b0d6..a593dd7 100644 --- a/system/src/Grav/Common/Markdown/ParsedownGravTrait.php +++ b/system/src/Grav/Common/Markdown/ParsedownGravTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Markdown * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -289,6 +289,7 @@ trait ParsedownGravTrait * @param array $args * @return mixed|null */ + #[\ReturnTypeWillChange] public function __call($method, $args) { if (isset($this->{$method}) === true) { diff --git a/system/src/Grav/Common/Media/Interfaces/AudioMediaInterface.php b/system/src/Grav/Common/Media/Interfaces/AudioMediaInterface.php index d9c69d2..6465d6b 100644 --- a/system/src/Grav/Common/Media/Interfaces/AudioMediaInterface.php +++ b/system/src/Grav/Common/Media/Interfaces/AudioMediaInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Media/Interfaces/ImageManipulateInterface.php b/system/src/Grav/Common/Media/Interfaces/ImageManipulateInterface.php index 702cfda..eb38ea8 100644 --- a/system/src/Grav/Common/Media/Interfaces/ImageManipulateInterface.php +++ b/system/src/Grav/Common/Media/Interfaces/ImageManipulateInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Media/Interfaces/ImageMediaInterface.php b/system/src/Grav/Common/Media/Interfaces/ImageMediaInterface.php index 377258c..cbf8f1d 100644 --- a/system/src/Grav/Common/Media/Interfaces/ImageMediaInterface.php +++ b/system/src/Grav/Common/Media/Interfaces/ImageMediaInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Media/Interfaces/MediaCollectionInterface.php b/system/src/Grav/Common/Media/Interfaces/MediaCollectionInterface.php index ad9fe96..62c531c 100644 --- a/system/src/Grav/Common/Media/Interfaces/MediaCollectionInterface.php +++ b/system/src/Grav/Common/Media/Interfaces/MediaCollectionInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Media/Interfaces/MediaFileInterface.php b/system/src/Grav/Common/Media/Interfaces/MediaFileInterface.php index 07a5c26..c20e4de 100644 --- a/system/src/Grav/Common/Media/Interfaces/MediaFileInterface.php +++ b/system/src/Grav/Common/Media/Interfaces/MediaFileInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Media/Interfaces/MediaInterface.php b/system/src/Grav/Common/Media/Interfaces/MediaInterface.php index 436fe16..273556a 100644 --- a/system/src/Grav/Common/Media/Interfaces/MediaInterface.php +++ b/system/src/Grav/Common/Media/Interfaces/MediaInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Media/Interfaces/MediaLinkInterface.php b/system/src/Grav/Common/Media/Interfaces/MediaLinkInterface.php index 4f229c2..41747be 100644 --- a/system/src/Grav/Common/Media/Interfaces/MediaLinkInterface.php +++ b/system/src/Grav/Common/Media/Interfaces/MediaLinkInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Media/Interfaces/MediaObjectInterface.php b/system/src/Grav/Common/Media/Interfaces/MediaObjectInterface.php index 6438881..4af3052 100644 --- a/system/src/Grav/Common/Media/Interfaces/MediaObjectInterface.php +++ b/system/src/Grav/Common/Media/Interfaces/MediaObjectInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -63,6 +63,14 @@ interface MediaObjectInterface extends \Grav\Framework\Media\Interfaces\MediaObj */ public function addAlternative($ratio, MediaObjectInterface $alternative); + /** + * Get list of image alternatives. Includes the current media image as well. + * + * @param bool $withDerived If true, include generated images as well. If false, only return existing files. + * @return array + */ + public function getAlternatives(bool $withDerived = true): array; + /** * Return string representation of the object (html). * @@ -208,6 +216,7 @@ interface MediaObjectInterface extends \Grav\Framework\Media\Interfaces\MediaObj * @param mixed $args * @return $this */ + #[\ReturnTypeWillChange] public function __call($method, $args); /** diff --git a/system/src/Grav/Common/Media/Interfaces/MediaPlayerInterface.php b/system/src/Grav/Common/Media/Interfaces/MediaPlayerInterface.php index 9307915..fbe6f65 100644 --- a/system/src/Grav/Common/Media/Interfaces/MediaPlayerInterface.php +++ b/system/src/Grav/Common/Media/Interfaces/MediaPlayerInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Media/Interfaces/MediaUploadInterface.php b/system/src/Grav/Common/Media/Interfaces/MediaUploadInterface.php index 8bfef84..7362599 100644 --- a/system/src/Grav/Common/Media/Interfaces/MediaUploadInterface.php +++ b/system/src/Grav/Common/Media/Interfaces/MediaUploadInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Media/Interfaces/VideoMediaInterface.php b/system/src/Grav/Common/Media/Interfaces/VideoMediaInterface.php index ff0655a..6d42fef 100644 --- a/system/src/Grav/Common/Media/Interfaces/VideoMediaInterface.php +++ b/system/src/Grav/Common/Media/Interfaces/VideoMediaInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Media/Traits/AudioMediaTrait.php b/system/src/Grav/Common/Media/Traits/AudioMediaTrait.php index 160837d..a083472 100644 --- a/system/src/Grav/Common/Media/Traits/AudioMediaTrait.php +++ b/system/src/Grav/Common/Media/Traits/AudioMediaTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Media/Traits/ImageLoadingTrait.php b/system/src/Grav/Common/Media/Traits/ImageLoadingTrait.php index c023763..75e23ca 100644 --- a/system/src/Grav/Common/Media/Traits/ImageLoadingTrait.php +++ b/system/src/Grav/Common/Media/Traits/ImageLoadingTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Media/Traits/ImageMediaTrait.php b/system/src/Grav/Common/Media/Traits/ImageMediaTrait.php index 06056eb..c1c46dc 100644 --- a/system/src/Grav/Common/Media/Traits/ImageMediaTrait.php +++ b/system/src/Grav/Common/Media/Traits/ImageMediaTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -50,6 +50,8 @@ trait ImageMediaTrait /** @var integer */ protected $retina_scale; + /** @var bool */ + protected $watermark; /** @var array */ public static $magic_actions = [ @@ -379,6 +381,8 @@ trait ImageMediaTrait $this->aspect_ratio = $config->get('system.images.cls.aspect_ratio', false); $this->retina_scale = $config->get('system.images.cls.retina_scale', 1); + $this->watermark = $config->get('system.images.watermark.watermark_all', false); + return $this; } @@ -415,6 +419,10 @@ trait ImageMediaTrait $this->image->merge(ImageFile::open($overlay)); } + if ($this->watermark) { + $this->watermark(); + } + return $this->image->cacheFile($this->format, $this->quality, false, [$this->get('width'), $this->get('height'), $this->get('modified')]); } } diff --git a/system/src/Grav/Common/Media/Traits/MediaFileTrait.php b/system/src/Grav/Common/Media/Traits/MediaFileTrait.php index 7198133..63906fc 100644 --- a/system/src/Grav/Common/Media/Traits/MediaFileTrait.php +++ b/system/src/Grav/Common/Media/Traits/MediaFileTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Media/Traits/MediaObjectTrait.php b/system/src/Grav/Common/Media/Traits/MediaObjectTrait.php index 3c2a38b..85ed6d2 100644 --- a/system/src/Grav/Common/Media/Traits/MediaObjectTrait.php +++ b/system/src/Grav/Common/Media/Traits/MediaObjectTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -128,16 +128,35 @@ trait MediaObjectTrait } $alternative->set('ratio', $ratio); - $width = $alternative->get('width'); + $width = $alternative->get('width', 0); $this->alternatives[$width] = $alternative; } + /** + * @param bool $withDerived + * @return array + */ + public function getAlternatives(bool $withDerived = true): array + { + $alternatives = []; + foreach ($this->alternatives + [$this->get('width', 0) => $this] as $size => $alternative) { + if ($withDerived || $alternative->filename === Utils::basename($alternative->filepath)) { + $alternatives[$size] = $alternative; + } + } + + ksort($alternatives, SORT_NUMERIC); + + return $alternatives; + } + /** * Return string representation of the object (html). * * @return string */ + #[\ReturnTypeWillChange] abstract public function __toString(); /** @@ -478,6 +497,7 @@ trait MediaObjectTrait * @param array $args * @return $this */ + #[\ReturnTypeWillChange] public function __call($method, $args) { $count = count($args); @@ -554,11 +574,12 @@ trait MediaObjectTrait foreach ($types as $type) { $thumb = $this->get("thumbnails.{$type}", false); - if ($thumb) { - $thumb = $thumb instanceof ThumbnailImageMedium ? $thumb : $this->createThumbnail($thumb); - $thumb->parent = $this; - $this->_thumbnail = $thumb; + $image = $thumb instanceof ThumbnailImageMedium ? $thumb : $this->createThumbnail($thumb); + if($image) { + $image->parent = $this; + $this->_thumbnail = $image; + } break; } } diff --git a/system/src/Grav/Common/Media/Traits/MediaPlayerTrait.php b/system/src/Grav/Common/Media/Traits/MediaPlayerTrait.php index 66e9f47..06cb99a 100644 --- a/system/src/Grav/Common/Media/Traits/MediaPlayerTrait.php +++ b/system/src/Grav/Common/Media/Traits/MediaPlayerTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Media/Traits/MediaTrait.php b/system/src/Grav/Common/Media/Traits/MediaTrait.php index 5faba82..14bfa90 100644 --- a/system/src/Grav/Common/Media/Traits/MediaTrait.php +++ b/system/src/Grav/Common/Media/Traits/MediaTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Media/Traits/MediaUploadTrait.php b/system/src/Grav/Common/Media/Traits/MediaUploadTrait.php index 7a1f55d..36a4503 100644 --- a/system/src/Grav/Common/Media/Traits/MediaUploadTrait.php +++ b/system/src/Grav/Common/Media/Traits/MediaUploadTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -132,9 +132,9 @@ trait MediaUploadTrait if ($folder === '.') { $folder = ''; } - $filename = basename($filename); + $filename = Utils::basename($filename); } - $extension = pathinfo($filename, PATHINFO_EXTENSION); + $extension = Utils::pathinfo($filename, PATHINFO_EXTENSION); // Decide which filename to use. if ($settings['random_name']) { @@ -573,6 +573,8 @@ trait MediaUploadTrait } } } + + $this->hide($filename); } /** diff --git a/system/src/Grav/Common/Media/Traits/StaticResizeTrait.php b/system/src/Grav/Common/Media/Traits/StaticResizeTrait.php index 49d75be..d8a4b08 100644 --- a/system/src/Grav/Common/Media/Traits/StaticResizeTrait.php +++ b/system/src/Grav/Common/Media/Traits/StaticResizeTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Media/Traits/ThumbnailMediaTrait.php b/system/src/Grav/Common/Media/Traits/ThumbnailMediaTrait.php index eab7320..100b532 100644 --- a/system/src/Grav/Common/Media/Traits/ThumbnailMediaTrait.php +++ b/system/src/Grav/Common/Media/Traits/ThumbnailMediaTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Media/Traits/VideoMediaTrait.php b/system/src/Grav/Common/Media/Traits/VideoMediaTrait.php index e03fbbd..b16bb53 100644 --- a/system/src/Grav/Common/Media/Traits/VideoMediaTrait.php +++ b/system/src/Grav/Common/Media/Traits/VideoMediaTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Page/Collection.php b/system/src/Grav/Common/Page/Collection.php index 32b917c..987b8d9 100644 --- a/system/src/Grav/Common/Page/Collection.php +++ b/system/src/Grav/Common/Page/Collection.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -27,6 +27,7 @@ use function is_string; /** * Class Collection * @package Grav\Common\Page + * @implements PageCollectionInterface */ class Collection extends Iterator implements PageCollectionInterface { @@ -162,6 +163,7 @@ class Collection extends Iterator implements PageCollectionInterface * * @return PageInterface */ + #[\ReturnTypeWillChange] public function current() { $current = parent::key(); @@ -174,6 +176,7 @@ class Collection extends Iterator implements PageCollectionInterface * * @return mixed */ + #[\ReturnTypeWillChange] public function key() { $current = parent::current(); @@ -187,6 +190,7 @@ class Collection extends Iterator implements PageCollectionInterface * @param string $offset * @return PageInterface|null */ + #[\ReturnTypeWillChange] public function offsetGet($offset) { return $this->pages->get($offset) ?: null; diff --git a/system/src/Grav/Common/Page/Header.php b/system/src/Grav/Common/Page/Header.php index 71c7b16..c18b58d 100644 --- a/system/src/Grav/Common/Page/Header.php +++ b/system/src/Grav/Common/Page/Header.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -30,6 +30,7 @@ class Header implements ArrayAccess, ExportInterface, JsonSerializable /** * @return array */ + #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->toArray(); diff --git a/system/src/Grav/Common/Page/Interfaces/PageCollectionInterface.php b/system/src/Grav/Common/Page/Interfaces/PageCollectionInterface.php index 5002911..493ab43 100644 --- a/system/src/Grav/Common/Page/Interfaces/PageCollectionInterface.php +++ b/system/src/Grav/Common/Page/Interfaces/PageCollectionInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -19,6 +19,11 @@ use Traversable; /** * Interface PageCollectionInterface * @package Grav\Common\Page\Interfaces + * + * @template TKey of array-key + * @template T + * @extends Traversable + * @extends ArrayAccess */ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, Serializable { @@ -68,6 +73,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S * * @param PageCollectionInterface $collection * @return PageCollectionInterface + * @phpstan-return PageCollectionInterface */ public function merge(PageCollectionInterface $collection); @@ -76,6 +82,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S * * @param PageCollectionInterface $collection * @return PageCollectionInterface + * @phpstan-return PageCollectionInterface */ public function intersect(PageCollectionInterface $collection); @@ -84,6 +91,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S * * @param int $size * @return PageCollectionInterface[] + * @phpstan-return array> */ public function batch($size); @@ -92,6 +100,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S * * @param PageInterface|string|null $key * @return PageCollectionInterface + * @phpstan-return PageCollectionInterface * @throws InvalidArgumentException */ //public function remove($key = null); @@ -104,6 +113,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S * @param array|null $manual * @param string|null $sort_flags * @return PageCollectionInterface + * @phpstan-return PageCollectionInterface */ public function order($by, $dir = 'asc', $manual = null, $sort_flags = null); @@ -128,6 +138,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S * * @param string $path * @return PageInterface The previous item. + * @phpstan-return T */ public function prevSibling($path); @@ -136,6 +147,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S * * @param string $path * @return PageInterface The next item. + * @phpstan-return T */ public function nextSibling($path); @@ -145,6 +157,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S * @param string $path * @param int $direction either -1 or +1 * @return PageInterface|PageCollectionInterface|false The sibling item. + * @phpstan-return T|false */ public function adjacentSibling($path, $direction = 1); @@ -166,6 +179,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S * @param string|null $endDate * @param string|null $field * @return PageCollectionInterface + * @phpstan-return PageCollectionInterface * @throws Exception */ public function dateRange($startDate = null, $endDate = null, $field = null); @@ -174,6 +188,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S * Creates new collection with only visible pages * * @return PageCollectionInterface The collection with only visible pages + * @phpstan-return PageCollectionInterface */ public function visible(); @@ -181,6 +196,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S * Creates new collection with only non-visible pages * * @return PageCollectionInterface The collection with only non-visible pages + * @phpstan-return PageCollectionInterface */ public function nonVisible(); @@ -188,6 +204,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S * Creates new collection with only pages * * @return PageCollectionInterface The collection with only pages + * @phpstan-return PageCollectionInterface */ public function pages(); @@ -195,6 +212,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S * Creates new collection with only modules * * @return PageCollectionInterface The collection with only modules + * @phpstan-return PageCollectionInterface */ public function modules(); @@ -202,6 +220,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S * Creates new collection with only modules * * @return PageCollectionInterface The collection with only modules + * @phpstan-return PageCollectionInterface * @deprecated 1.7 Use $this->modules() instead */ public function modular(); @@ -210,6 +229,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S * Creates new collection with only non-module pages * * @return PageCollectionInterface The collection with only non-module pages + * @phpstan-return PageCollectionInterface * @deprecated 1.7 Use $this->pages() instead */ public function nonModular(); @@ -218,6 +238,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S * Creates new collection with only published pages * * @return PageCollectionInterface The collection with only published pages + * @phpstan-return PageCollectionInterface */ public function published(); @@ -225,6 +246,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S * Creates new collection with only non-published pages * * @return PageCollectionInterface The collection with only non-published pages + * @phpstan-return PageCollectionInterface */ public function nonPublished(); @@ -232,6 +254,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S * Creates new collection with only routable pages * * @return PageCollectionInterface The collection with only routable pages + * @phpstan-return PageCollectionInterface */ public function routable(); @@ -239,6 +262,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S * Creates new collection with only non-routable pages * * @return PageCollectionInterface The collection with only non-routable pages + * @phpstan-return PageCollectionInterface */ public function nonRoutable(); @@ -247,6 +271,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S * * @param string $type * @return PageCollectionInterface The collection + * @phpstan-return PageCollectionInterface */ public function ofType($type); @@ -255,6 +280,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S * * @param string[] $types * @return PageCollectionInterface The collection + * @phpstan-return PageCollectionInterface */ public function ofOneOfTheseTypes($types); @@ -263,6 +289,7 @@ interface PageCollectionInterface extends Traversable, ArrayAccess, Countable, S * * @param array $accessLevels * @return PageCollectionInterface The collection + * @phpstan-return PageCollectionInterface */ public function ofOneOfTheseAccessLevels($accessLevels); diff --git a/system/src/Grav/Common/Page/Interfaces/PageContentInterface.php b/system/src/Grav/Common/Page/Interfaces/PageContentInterface.php index 5156ede..a80972b 100644 --- a/system/src/Grav/Common/Page/Interfaces/PageContentInterface.php +++ b/system/src/Grav/Common/Page/Interfaces/PageContentInterface.php @@ -3,13 +3,15 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Common\Page\Interfaces; +use Grav\Common\Data\Blueprint; use Grav\Common\Media\Interfaces\MediaCollectionInterface; +use Grav\Common\Page\Header; /** * Methods currently implemented in Flex Page emulation layer. @@ -20,7 +22,7 @@ interface PageContentInterface * Gets and Sets the header based on the YAML configuration at the top of the .md file * * @param object|array|null $var a YAML object representing the configuration for the file - * @return object the current YAML configuration + * @return \stdClass|Header The current YAML configuration */ public function header($var = null); @@ -58,7 +60,7 @@ interface PageContentInterface /** * Needed by the onPageContentProcessed event to set the raw page content * - * @param string $content + * @param string|null $content */ public function setRawContent($content); @@ -254,4 +256,12 @@ interface PageContentInterface * @return bool */ public function exists(); + + /** + * Returns the blueprint from the page. + * + * @param string $name Name of the Blueprint form. Used by flex only. + * @return Blueprint Returns a Blueprint. + */ + public function getBlueprint(string $name = ''); } diff --git a/system/src/Grav/Common/Page/Interfaces/PageInterface.php b/system/src/Grav/Common/Page/Interfaces/PageInterface.php index e5ea20c..56fc891 100644 --- a/system/src/Grav/Common/Page/Interfaces/PageInterface.php +++ b/system/src/Grav/Common/Page/Interfaces/PageInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Page/Interfaces/PageTranslateInterface.php b/system/src/Grav/Common/Page/Interfaces/PageTranslateInterface.php index be7ba15..5fba751 100644 --- a/system/src/Grav/Common/Page/Interfaces/PageTranslateInterface.php +++ b/system/src/Grav/Common/Page/Interfaces/PageTranslateInterface.php @@ -7,6 +7,11 @@ namespace Grav\Common\Page\Interfaces; */ interface PageTranslateInterface { + /** + * @return bool + */ + public function translated(): bool; + /** * Return an array with the routes of other translated languages * diff --git a/system/src/Grav/Common/Page/Interfaces/PagesSourceInterface.php b/system/src/Grav/Common/Page/Interfaces/PagesSourceInterface.php index ef5cfba..ff4ebda 100644 --- a/system/src/Grav/Common/Page/Interfaces/PagesSourceInterface.php +++ b/system/src/Grav/Common/Page/Interfaces/PagesSourceInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Page/Markdown/Excerpts.php b/system/src/Grav/Common/Page/Markdown/Excerpts.php index 9b0b996..17f9cbf 100644 --- a/system/src/Grav/Common/Page/Markdown/Excerpts.php +++ b/system/src/Grav/Common/Page/Markdown/Excerpts.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -206,7 +206,7 @@ class Excerpts && (empty($url_parts['host']) || $url_parts['host'] === $grav['uri']->host()); if ($local_file) { - $filename = basename($url_parts['path']); + $filename = Utils::basename($url_parts['path']); $folder = dirname($url_parts['path']); // Get the local path to page media if possible. diff --git a/system/src/Grav/Common/Page/Media.php b/system/src/Grav/Common/Page/Media.php index 94fd2c4..6c0a6c3 100644 --- a/system/src/Grav/Common/Page/Media.php +++ b/system/src/Grav/Common/Page/Media.php @@ -3,13 +3,14 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Common\Page; use FilesystemIterator; +use Grav\Common\Config\Config; use Grav\Common\Grav; use Grav\Common\Media\Interfaces\MediaObjectInterface; use Grav\Common\Yaml; @@ -59,10 +60,51 @@ class Media extends AbstractMedia } } + /** + * Return raw route to the page. + * + * @return string|null Route to the page or null if media isn't for a page. + */ + public function getRawRoute(): ?string + { + $path = $this->getPath(); + if ($path) { + /** @var Pages $pages */ + $pages = $this->getGrav()['pages']; + $page = $pages->get($path); + if ($page) { + return $page->rawRoute(); + } + } + + return null; + } + + /** + * Return page route. + * + * @return string|null Route to the page or null if media isn't for a page. + */ + public function getRoute(): ?string + { + $path = $this->getPath(); + if ($path) { + /** @var Pages $pages */ + $pages = $this->getGrav()['pages']; + $page = $pages->get($path); + if ($page) { + return $page->route(); + } + } + + return null; + } + /** * @param string $offset * @return bool */ + #[\ReturnTypeWillChange] public function offsetExists($offset) { return parent::offsetExists($offset) ?: isset(static::$global[$offset]); @@ -72,6 +114,7 @@ class Media extends AbstractMedia * @param string $offset * @return MediaObjectInterface|null */ + #[\ReturnTypeWillChange] public function offsetGet($offset) { return parent::offsetGet($offset) ?: static::$global[$offset]; @@ -84,11 +127,6 @@ class Media extends AbstractMedia */ protected function init() { - /** @var UniformResourceLocator $locator */ - $locator = Grav::instance()['locator']; - $config = Grav::instance()['config']; - $exif_reader = isset(Grav::instance()['exif']) ? Grav::instance()['exif']->getReader() : false; - $media_types = array_keys(Grav::instance()['config']->get('media.types')); $path = $this->getPath(); // Handle special cases where page doesn't exist in filesystem. @@ -96,6 +134,17 @@ class Media extends AbstractMedia return; } + $grav = Grav::instance(); + + /** @var UniformResourceLocator $locator */ + $locator = $grav['locator']; + + /** @var Config $config */ + $config = $grav['config']; + + $exif_reader = isset($grav['exif']) ? $grav['exif']->getReader() : null; + $media_types = array_keys($config->get('media.types', [])); + $iterator = new FilesystemIterator($path, FilesystemIterator::UNIX_PATHS | FilesystemIterator::SKIP_DOTS); $media = []; diff --git a/system/src/Grav/Common/Page/Medium/AbstractMedia.php b/system/src/Grav/Common/Page/Medium/AbstractMedia.php index db81ddd..9b3650b 100644 --- a/system/src/Grav/Common/Page/Medium/AbstractMedia.php +++ b/system/src/Grav/Common/Page/Medium/AbstractMedia.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -90,6 +90,7 @@ abstract class AbstractMedia implements ExportInterface, MediaCollectionInterfac * @param string $filename * @return mixed */ + #[\ReturnTypeWillChange] public function __invoke($filename) { return $this->offsetGet($filename); diff --git a/system/src/Grav/Common/Page/Medium/AudioMedium.php b/system/src/Grav/Common/Page/Medium/AudioMedium.php index f34f0a9..cc53f81 100644 --- a/system/src/Grav/Common/Page/Medium/AudioMedium.php +++ b/system/src/Grav/Common/Page/Medium/AudioMedium.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Page/Medium/GlobalMedia.php b/system/src/Grav/Common/Page/Medium/GlobalMedia.php index 4097c8f..20f63ad 100644 --- a/system/src/Grav/Common/Page/Medium/GlobalMedia.php +++ b/system/src/Grav/Common/Page/Medium/GlobalMedia.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -11,6 +11,7 @@ namespace Grav\Common\Page\Medium; use Grav\Common\Grav; use Grav\Common\Media\Interfaces\MediaObjectInterface; +use Grav\Common\Utils; use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; use function dirname; @@ -46,6 +47,7 @@ class GlobalMedia extends AbstractMedia * @param string $offset * @return bool */ + #[\ReturnTypeWillChange] public function offsetExists($offset) { return parent::offsetExists($offset) ?: !empty($this->resolveStream($offset)); @@ -55,6 +57,7 @@ class GlobalMedia extends AbstractMedia * @param string $offset * @return MediaObjectInterface|null */ + #[\ReturnTypeWillChange] public function offsetGet($offset) { return parent::offsetGet($offset) ?: $this->addMedium($offset); @@ -87,7 +90,7 @@ class GlobalMedia extends AbstractMedia } $path = dirname($filename); - [$basename, $ext,, $extra] = $this->getFileParts(basename($filename)); + [$basename, $ext,, $extra] = $this->getFileParts(Utils::basename($filename)); $medium = MediumFactory::fromFile($filename); if (null === $medium) { diff --git a/system/src/Grav/Common/Page/Medium/ImageFile.php b/system/src/Grav/Common/Page/Medium/ImageFile.php index bd853bf..1aae7ee 100644 --- a/system/src/Grav/Common/Page/Medium/ImageFile.php +++ b/system/src/Grav/Common/Page/Medium/ImageFile.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -34,6 +34,7 @@ class ImageFile extends Image /** * Destruct also image object. */ + #[\ReturnTypeWillChange] public function __destruct() { $adapter = $this->adapter; diff --git a/system/src/Grav/Common/Page/Medium/ImageMedium.php b/system/src/Grav/Common/Page/Medium/ImageMedium.php index 70d13f8..42d8f0e 100644 --- a/system/src/Grav/Common/Page/Medium/ImageMedium.php +++ b/system/src/Grav/Common/Page/Medium/ImageMedium.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -17,6 +17,7 @@ use Grav\Common\Media\Interfaces\MediaLinkInterface; use Grav\Common\Media\Traits\ImageLoadingTrait; use Grav\Common\Media\Traits\ImageMediaTrait; use Grav\Common\Utils; +use Gregwar\Image\Image; use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; use function func_get_args; use function in_array; @@ -88,6 +89,7 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate /** * Also unset the image on destruct. */ + #[\ReturnTypeWillChange] public function __destruct() { unset($this->image); @@ -96,6 +98,7 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate /** * Also clone image. */ + #[\ReturnTypeWillChange] public function __clone() { if ($this->image) { @@ -246,12 +249,12 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate if ($this->saved_image_path && $this->auto_sizes) { if (!array_key_exists('height', $this->attributes) && !array_key_exists('width', $this->attributes)) { $info = getimagesize($this->saved_image_path); - $width = intval($info[0]); - $height = intval($info[1]); + $width = (int)$info[0]; + $height = (int)$info[1]; $scaling_factor = $this->retina_scale > 0 ? $this->retina_scale : 1; - $attributes['width'] = intval($width / $scaling_factor); - $attributes['height'] = intval($height / $scaling_factor); + $attributes['width'] = (int)($width / $scaling_factor); + $attributes['height'] = (int)($height / $scaling_factor); if ($this->aspect_ratio) { $style = ($attributes['style'] ?? ' ') . "--aspect-ratio: $width/$height;"; @@ -302,25 +305,102 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate return parent::lightbox($width, $height, $reset); } + /** + * @param string $enabled + * @return $this + */ public function autoSizes($enabled = 'true') { - $enabled = $enabled === 'true' ?: false; - $this->auto_sizes = $enabled; + $this->auto_sizes = $enabled === 'true' ?: false; return $this; } + /** + * @param string $enabled + * @return $this + */ public function aspectRatio($enabled = 'true') { - $enabled = $enabled === 'true' ?: false; - $this->aspect_ratio = $enabled; + $this->aspect_ratio = $enabled === 'true' ?: false; return $this; } + /** + * @param int $scale + * @return $this + */ public function retinaScale($scale = 1) { - $this->retina_scale = intval($scale); + $this->retina_scale = (int)$scale; + + return $this; + } + + /** + * @param string|null $image + * @param string|null $position + * @param int|float|null $scale + * @return $this + */ + public function watermark($image = null, $position = null, $scale = null) + { + $grav = $this->getGrav(); + + $locator = $grav['locator']; + $config = $grav['config']; + + $args = func_get_args(); + + $file = $args[0] ?? '1'; // using '1' because of markdown. doing ![](image.jpg?watermark) returns $args[0]='1'; + $file = $file === '1' ? $config->get('system.images.watermark.image') : $args[0]; + + $watermark = $locator->findResource($file); + $watermark = ImageFile::open($watermark); + + // Scaling operations + $scale = ($scale ?? $config->get('system.images.watermark.scale', 100)) / 100; + $wwidth = $this->get('width') * $scale; + $wheight = $this->get('height') * $scale; + $watermark->resize($wwidth, $wheight); + + // Position operations + $position = !empty($args[1]) ? explode('-', $args[1]) : ['center', 'center']; // todo change to config + $positionY = $position[0] ?? $config->get('system.images.watermark.position_y', 'center'); + $positionX = $position[1] ?? $config->get('system.images.watermark.position_x', 'center'); + + switch ($positionY) + { + case 'top': + $positionY = 0; + break; + + case 'bottom': + $positionY = $this->get('height')-$wheight; + break; + + case 'center': + $positionY = ($this->get('height')/2) - ($wheight/2); + break; + } + + switch ($positionX) + { + case 'left': + $positionX = 0; + break; + + case 'right': + $positionX = $this->get('width')-$wwidth; + break; + + case 'center': + $positionX = ($this->get('width')/2) - ($wwidth/2); + break; + } + + $this->__call('merge', [$watermark,$positionX, $positionY]); return $this; } @@ -344,7 +424,7 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate */ public function addFrame(int $border = 10, string $color = '0x000000') { - if(is_int(intval($border)) && $border>0 && preg_match('/^0x[a-f0-9]{6}$/i', $color)) { // $border must be an integer and bigger than 0; $color must be formatted as an HEX value (0x??????). + if($border > 0 && preg_match('/^0x[a-f0-9]{6}$/i', $color)) { // $border must be an integer and bigger than 0; $color must be formatted as an HEX value (0x??????). $image = ImageFile::open($this->path()); } else { @@ -375,7 +455,7 @@ class ImageMedium extends Medium implements ImageMediaInterface, ImageManipulate * @param mixed $args * @return $this|mixed */ - + #[\ReturnTypeWillChange] public function __call($method, $args) { if (!in_array($method, static::$magic_actions, true)) { diff --git a/system/src/Grav/Common/Page/Medium/Link.php b/system/src/Grav/Common/Page/Medium/Link.php index d576875..c76d8c1 100644 --- a/system/src/Grav/Common/Page/Medium/Link.php +++ b/system/src/Grav/Common/Page/Medium/Link.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -28,7 +28,7 @@ class Link implements RenderableInterface, MediaLinkInterface /** @var array */ protected $attributes = []; - /** @var MediaObjectInterface */ + /** @var MediaObjectInterface|MediaLinkInterface */ protected $source; /** @@ -79,6 +79,7 @@ class Link implements RenderableInterface, MediaLinkInterface * @param mixed $args * @return mixed */ + #[\ReturnTypeWillChange] public function __call($method, $args) { $object = $this->source; diff --git a/system/src/Grav/Common/Page/Medium/Medium.php b/system/src/Grav/Common/Page/Medium/Medium.php index 87009ac..0891c0c 100644 --- a/system/src/Grav/Common/Page/Medium/Medium.php +++ b/system/src/Grav/Common/Page/Medium/Medium.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -22,7 +22,14 @@ use Grav\Common\Media\Traits\MediaObjectTrait; * Class Medium * @package Grav\Common\Page\Medium * + * @property string $filepath + * @property string $filename + * @property string $basename * @property string $mime + * @property int $size + * @property int $modified + * @property array $metadata + * @property int|string $timestamp */ class Medium extends Data implements RenderableInterface, MediaFileInterface { @@ -57,6 +64,7 @@ class Medium extends Data implements RenderableInterface, MediaFileInterface /** * Clone medium. */ + #[\ReturnTypeWillChange] public function __clone() { // Allows future compatibility as parent::__clone() works. @@ -90,6 +98,7 @@ class Medium extends Data implements RenderableInterface, MediaFileInterface * * @return string */ + #[\ReturnTypeWillChange] public function __toString() { return $this->html(); @@ -97,6 +106,7 @@ class Medium extends Data implements RenderableInterface, MediaFileInterface /** * @param string $thumb + * @return Medium|null */ protected function createThumbnail($thumb) { diff --git a/system/src/Grav/Common/Page/Medium/MediumFactory.php b/system/src/Grav/Common/Page/Medium/MediumFactory.php index 7dee4ea..620446b 100644 --- a/system/src/Grav/Common/Page/Medium/MediumFactory.php +++ b/system/src/Grav/Common/Page/Medium/MediumFactory.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -13,6 +13,7 @@ use Grav\Common\Grav; use Grav\Common\Data\Blueprint; use Grav\Common\Media\Interfaces\ImageMediaInterface; use Grav\Common\Media\Interfaces\MediaObjectInterface; +use Grav\Common\Utils; use Grav\Framework\Form\FormFlashFile; use Psr\Http\Message\UploadedFileInterface; use function dirname; @@ -37,7 +38,7 @@ class MediumFactory return null; } - $parts = pathinfo($file); + $parts = Utils::pathinfo($file); $path = $parts['dirname']; $filename = $parts['basename']; $ext = $parts['extension'] ?? ''; @@ -101,7 +102,7 @@ class MediumFactory return null; } - $parts = pathinfo($clientName); + $parts = Utils::pathinfo($clientName); $filename = $parts['basename']; $ext = $parts['extension'] ?? ''; $basename = $parts['filename']; @@ -193,7 +194,7 @@ class MediumFactory $height = $medium->get('height') * $ratio; $prev_basename = $medium->get('basename'); - $basename = str_replace('@'.$from.'x', '@'.$to.'x', $prev_basename); + $basename = str_replace('@' . $from . 'x', $to !== 1 ? '@' . $to . 'x' : '', $prev_basename); $debug = $medium->get('debug'); $medium->set('debug', false); @@ -208,6 +209,8 @@ class MediumFactory $medium = self::fromFile($file); if ($medium) { + $medium->set('basename', $basename); + $medium->set('filename', $basename . '.' . $medium->extension); $medium->set('size', $size); } diff --git a/system/src/Grav/Common/Page/Medium/ParsedownHtmlTrait.php b/system/src/Grav/Common/Page/Medium/ParsedownHtmlTrait.php index 44ab952..4ab2fb5 100644 --- a/system/src/Grav/Common/Page/Medium/ParsedownHtmlTrait.php +++ b/system/src/Grav/Common/Page/Medium/ParsedownHtmlTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Page/Medium/RenderableInterface.php b/system/src/Grav/Common/Page/Medium/RenderableInterface.php index ac72447..1ee6a96 100644 --- a/system/src/Grav/Common/Page/Medium/RenderableInterface.php +++ b/system/src/Grav/Common/Page/Medium/RenderableInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Page/Medium/StaticImageMedium.php b/system/src/Grav/Common/Page/Medium/StaticImageMedium.php index ba92fa1..9780100 100644 --- a/system/src/Grav/Common/Page/Medium/StaticImageMedium.php +++ b/system/src/Grav/Common/Page/Medium/StaticImageMedium.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -37,4 +37,12 @@ class StaticImageMedium extends Medium implements ImageMediaInterface return ['name' => 'img', 'attributes' => $attributes]; } + + /** + * @return $this + */ + public function higherQualityAlternative() + { + return $this; + } } diff --git a/system/src/Grav/Common/Page/Medium/StaticResizeTrait.php b/system/src/Grav/Common/Page/Medium/StaticResizeTrait.php index d95b2d6..caaa7e2 100644 --- a/system/src/Grav/Common/Page/Medium/StaticResizeTrait.php +++ b/system/src/Grav/Common/Page/Medium/StaticResizeTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Page/Medium/ThumbnailImageMedium.php b/system/src/Grav/Common/Page/Medium/ThumbnailImageMedium.php index a56a20d..1e4d862 100644 --- a/system/src/Grav/Common/Page/Medium/ThumbnailImageMedium.php +++ b/system/src/Grav/Common/Page/Medium/ThumbnailImageMedium.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Page/Medium/VideoMedium.php b/system/src/Grav/Common/Page/Medium/VideoMedium.php index bfcf550..0e629dc 100644 --- a/system/src/Grav/Common/Page/Medium/VideoMedium.php +++ b/system/src/Grav/Common/Page/Medium/VideoMedium.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Page/Page.php b/system/src/Grav/Common/Page/Page.php index 2099be2..25f8ff6 100644 --- a/system/src/Grav/Common/Page/Page.php +++ b/system/src/Grav/Common/Page/Page.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -84,7 +84,7 @@ class Page implements PageInterface protected $unpublish_date; /** @var string */ protected $slug; - /** @var string */ + /** @var string|null */ protected $route; /** @var string|null */ protected $raw_route; @@ -196,7 +196,7 @@ class Page implements PageInterface } // extract page language from page extension - $language = trim(basename($this->extension(), 'md'), '.') ?: null; + $language = trim(Utils::basename($this->extension(), 'md'), '.') ?: null; $this->language($language); $this->hide_home_route = $config->get('system.home.hide_in_urls', false); @@ -218,6 +218,26 @@ class Page implements PageInterface return $this; } + #[\ReturnTypeWillChange] + public function __clone() + { + $this->initialized = false; + $this->header = $this->header ? clone $this->header : null; + } + + /** + * @return void + */ + public function initialize(): void + { + if (!$this->initialized) { + $this->initialized = true; + $this->route = null; + $this->raw_route = null; + $this->_forms = null; + } + } + /** * @return void */ @@ -368,7 +388,7 @@ class Page implements PageInterface * Gets and Sets the header based on the YAML configuration at the top of the .md file * * @param object|array|null $var a YAML object representing the configuration for the file - * @return object the current YAML configuration + * @return \stdClass the current YAML configuration */ public function header($var = null) { @@ -975,7 +995,7 @@ class Page implements PageInterface /** * Needed by the onPageContentProcessed event to set the raw page content * - * @param string $content + * @param string|null $content * @return void */ public function setRawContent($content) @@ -1227,6 +1247,17 @@ class Page implements PageInterface return $blueprint; } + /** + * Returns the blueprint from the page. + * + * @param string $name Not used. + * @return Blueprint Returns a Blueprint. + */ + public function getBlueprint(string $name = '') + { + return $this->blueprints(); + } + /** * Get the blueprint name for this page. Use the blueprint form field if set * @@ -1434,7 +1465,7 @@ class Page implements PageInterface $this->extension = $var; } if (empty($this->extension)) { - $this->extension = '.' . pathinfo($this->name(), PATHINFO_EXTENSION); + $this->extension = '.' . Utils::pathinfo($this->name(), PATHINFO_EXTENSION); } return $this->extension; @@ -2078,9 +2109,9 @@ class Page implements PageInterface { if ($var !== null) { // Filename of the page. - $this->name = basename($var); + $this->name = Utils::basename($var); // Folder of the page. - $this->folder = basename(dirname($var)); + $this->folder = Utils::basename(dirname($var)); // Path to the page. $this->path = dirname($var, 2); } @@ -2119,7 +2150,7 @@ class Page implements PageInterface { if ($var !== null) { // Folder of the page. - $this->folder = basename($var); + $this->folder = Utils::basename($var); // Path to the page. $this->path = dirname($var); } diff --git a/system/src/Grav/Common/Page/Pages.php b/system/src/Grav/Common/Page/Pages.php index 941c9a7..8ad2ccd 100644 --- a/system/src/Grav/Common/Page/Pages.php +++ b/system/src/Grav/Common/Page/Pages.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -649,7 +649,7 @@ class Pages $cmd = $value; $params = []; } elseif (is_array($value) && count($value) === 1 && !is_int(key($value))) { - // Format: @command.param: { attr1: value1, attr2: value2 } + // Format: @command.param: { attr1: value1, attr2: value2 } $cmd = (string)key($value); $params = (array)current($value); } else { @@ -2065,7 +2065,7 @@ class Pages $list[$key] = $child->slug(); break; case 'basename': - $list[$key] = basename($key); + $list[$key] = Utils::basename($key); break; case 'folder': $list[$key] = $child->folder(); diff --git a/system/src/Grav/Common/Page/Types.php b/system/src/Grav/Common/Page/Types.php index c718368..420c588 100644 --- a/system/src/Grav/Common/Page/Types.php +++ b/system/src/Grav/Common/Page/Types.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Page * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -12,6 +12,7 @@ namespace Grav\Common\Page; use Grav\Common\Data\Blueprint; use Grav\Common\Filesystem\Folder; use Grav\Common\Grav; +use Grav\Common\Utils; use InvalidArgumentException; use RocketTheme\Toolbox\ArrayTraits\ArrayAccess; use RocketTheme\Toolbox\ArrayTraits\Constructor; @@ -144,7 +145,7 @@ class Types implements \ArrayAccess, \Iterator, \Countable if (strpos($name, 'modular/') !== 0) { continue; } - $list[$name] = ucfirst(trim(str_replace('_', ' ', basename($name)))); + $list[$name] = ucfirst(trim(str_replace('_', ' ', Utils::basename($name)))); } ksort($list); diff --git a/system/src/Grav/Common/Plugin.php b/system/src/Grav/Common/Plugin.php index b32e39e..30e5344 100644 --- a/system/src/Grav/Common/Plugin.php +++ b/system/src/Grav/Common/Plugin.php @@ -3,7 +3,7 @@ /** * @package Grav\Common * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -118,7 +118,7 @@ class Plugin implements EventSubscriberInterface, ArrayAccess */ public function config() { - return null !== $this->config ? $this->config["plugins.{$this->name}"] : []; + return $this->config["plugins.{$this->name}"] ?? []; } /** @@ -227,6 +227,7 @@ class Plugin implements EventSubscriberInterface, ArrayAccess * @param string $offset An offset to check for. * @return bool Returns TRUE on success or FALSE on failure. */ + #[\ReturnTypeWillChange] public function offsetExists($offset) { if ($offset === 'title') { @@ -244,6 +245,7 @@ class Plugin implements EventSubscriberInterface, ArrayAccess * @param string $offset The offset to retrieve. * @return mixed Can return all value types. */ + #[\ReturnTypeWillChange] public function offsetGet($offset) { if ($offset === 'title') { @@ -262,6 +264,7 @@ class Plugin implements EventSubscriberInterface, ArrayAccess * @param mixed $value The value to set. * @throws LogicException */ + #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { throw new LogicException(__CLASS__ . ' blueprints cannot be modified.'); @@ -273,6 +276,7 @@ class Plugin implements EventSubscriberInterface, ArrayAccess * @param string $offset The offset to unset. * @throws LogicException */ + #[\ReturnTypeWillChange] public function offsetUnset($offset) { throw new LogicException(__CLASS__ . ' blueprints cannot be modified.'); @@ -410,6 +414,30 @@ class Plugin implements EventSubscriberInterface, ArrayAccess return true; } + public static function inheritedConfigOption(string $plugin, string $var, PageInterface $page = null, $default = null) + { + if (Utils::isAdminPlugin()) { + $page = Grav::instance()['admin']->page() ?? null; + } else { + $page = $page ?? Grav::instance()['page'] ?? null; + } + + // Try to find var in the page headers + if ($page instanceof PageInterface && $page->exists()) { + // Loop over pages and look for header vars + while ($page && !$page->root()) { + $header = new Data((array)$page->header()); + $value = $header->get("$plugin.$var"); + if (isset($value)) { + return $value; + } + $page = $page->parent(); + } + } + + return Grav::instance()['config']->get("plugins.$plugin.$var", $default); + } + /** * Simpler getter for the plugin blueprint * diff --git a/system/src/Grav/Common/Plugins.php b/system/src/Grav/Common/Plugins.php index 88b5e52..42b1593 100644 --- a/system/src/Grav/Common/Plugins.php +++ b/system/src/Grav/Common/Plugins.php @@ -3,7 +3,7 @@ /** * @package Grav\Common * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/AssetsProcessor.php b/system/src/Grav/Common/Processors/AssetsProcessor.php index 66bb5e3..e460fb0 100644 --- a/system/src/Grav/Common/Processors/AssetsProcessor.php +++ b/system/src/Grav/Common/Processors/AssetsProcessor.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Processors * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/BackupsProcessor.php b/system/src/Grav/Common/Processors/BackupsProcessor.php index 6e960b4..83e8a0c 100644 --- a/system/src/Grav/Common/Processors/BackupsProcessor.php +++ b/system/src/Grav/Common/Processors/BackupsProcessor.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Processors * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/DebuggerAssetsProcessor.php b/system/src/Grav/Common/Processors/DebuggerAssetsProcessor.php index de7cafc..a3d5a51 100644 --- a/system/src/Grav/Common/Processors/DebuggerAssetsProcessor.php +++ b/system/src/Grav/Common/Processors/DebuggerAssetsProcessor.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Processors * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/Events/RequestHandlerEvent.php b/system/src/Grav/Common/Processors/Events/RequestHandlerEvent.php index 32908ba..30f6195 100644 --- a/system/src/Grav/Common/Processors/Events/RequestHandlerEvent.php +++ b/system/src/Grav/Common/Processors/Events/RequestHandlerEvent.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Processors * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/InitializeProcessor.php b/system/src/Grav/Common/Processors/InitializeProcessor.php index 6114464..04e06ed 100644 --- a/system/src/Grav/Common/Processors/InitializeProcessor.php +++ b/system/src/Grav/Common/Processors/InitializeProcessor.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Processors * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -44,7 +44,7 @@ class InitializeProcessor extends ProcessorBase public $title = 'Initialize'; /** @var bool */ - private static $cli_initialized = false; + protected static $cli_initialized = false; /** * @param Grav $grav @@ -105,12 +105,12 @@ class InitializeProcessor extends ProcessorBase // TODO: remove in 2.0. $this->container['accounts']; + // Initialize session (used by URI, see issue #3269). + $this->initializeSession($config); + // Initialize URI (uses session, see issue #3269). $this->initializeUri($config); - // Initialize session. - $this->initializeSession($config); - // Grav may return redirect response right away. $redirectCode = (int)$config->get('system.pages.redirect_trailing_slash', 1); if ($redirectCode) { diff --git a/system/src/Grav/Common/Processors/PagesProcessor.php b/system/src/Grav/Common/Processors/PagesProcessor.php index 470ca90..3338e0c 100644 --- a/system/src/Grav/Common/Processors/PagesProcessor.php +++ b/system/src/Grav/Common/Processors/PagesProcessor.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Processors * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -42,15 +42,29 @@ class PagesProcessor extends ProcessorBase $this->container['debugger']->addMessage($this->container['cache']->getCacheStatus()); $this->container['pages']->init(); - $this->container->fireEvent('onPagesInitialized', new Event(['pages' => $this->container['pages']])); - $this->container->fireEvent('onPageInitialized', new Event(['page' => $this->container['page']])); + + $route = $this->container['route']; + + $this->container->fireEvent('onPagesInitialized', new Event( + [ + 'pages' => $this->container['pages'], + 'route' => $route, + 'request' => $request + ] + )); + $this->container->fireEvent('onPageInitialized', new Event( + [ + 'page' => $this->container['page'], + 'route' => $route, + 'request' => $request + ] + )); /** @var PageInterface $page */ $page = $this->container['page']; if (!$page->routable()) { $exception = new RequestException($request, 'Page Not Found', 404); - $route = $this->container['route']; // If no page found, fire event $event = new Event([ 'page' => $page, diff --git a/system/src/Grav/Common/Processors/PluginsProcessor.php b/system/src/Grav/Common/Processors/PluginsProcessor.php index 485578e..ae87901 100644 --- a/system/src/Grav/Common/Processors/PluginsProcessor.php +++ b/system/src/Grav/Common/Processors/PluginsProcessor.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Processors * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/ProcessorBase.php b/system/src/Grav/Common/Processors/ProcessorBase.php index a3506f5..9353391 100644 --- a/system/src/Grav/Common/Processors/ProcessorBase.php +++ b/system/src/Grav/Common/Processors/ProcessorBase.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Processors * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/ProcessorInterface.php b/system/src/Grav/Common/Processors/ProcessorInterface.php index 8a6edbe..0aed195 100644 --- a/system/src/Grav/Common/Processors/ProcessorInterface.php +++ b/system/src/Grav/Common/Processors/ProcessorInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Processors * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/RenderProcessor.php b/system/src/Grav/Common/Processors/RenderProcessor.php index 329da22..e12cc45 100644 --- a/system/src/Grav/Common/Processors/RenderProcessor.php +++ b/system/src/Grav/Common/Processors/RenderProcessor.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Processors * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/RequestProcessor.php b/system/src/Grav/Common/Processors/RequestProcessor.php index 971fb67..bad410c 100644 --- a/system/src/Grav/Common/Processors/RequestProcessor.php +++ b/system/src/Grav/Common/Processors/RequestProcessor.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Processors * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -11,6 +11,7 @@ namespace Grav\Common\Processors; use Grav\Common\Processors\Events\RequestHandlerEvent; use Grav\Common\Uri; +use Grav\Common\Utils; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; @@ -42,7 +43,7 @@ class RequestProcessor extends ProcessorBase } $uri = $request->getUri(); - $ext = mb_strtolower(pathinfo($uri->getPath(), PATHINFO_EXTENSION)); + $ext = mb_strtolower(Utils::pathinfo($uri->getPath(), PATHINFO_EXTENSION)); $request = $request ->withAttribute('grav', $this->container) diff --git a/system/src/Grav/Common/Processors/SchedulerProcessor.php b/system/src/Grav/Common/Processors/SchedulerProcessor.php index 69cc163..722bfcc 100644 --- a/system/src/Grav/Common/Processors/SchedulerProcessor.php +++ b/system/src/Grav/Common/Processors/SchedulerProcessor.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Processors * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/TasksProcessor.php b/system/src/Grav/Common/Processors/TasksProcessor.php index 07e0934..29f3458 100644 --- a/system/src/Grav/Common/Processors/TasksProcessor.php +++ b/system/src/Grav/Common/Processors/TasksProcessor.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Processors * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/ThemesProcessor.php b/system/src/Grav/Common/Processors/ThemesProcessor.php index 951dc79..60d089e 100644 --- a/system/src/Grav/Common/Processors/ThemesProcessor.php +++ b/system/src/Grav/Common/Processors/ThemesProcessor.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Processors * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Processors/TwigProcessor.php b/system/src/Grav/Common/Processors/TwigProcessor.php index 6604b5c..ffc1032 100644 --- a/system/src/Grav/Common/Processors/TwigProcessor.php +++ b/system/src/Grav/Common/Processors/TwigProcessor.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Processors * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Scheduler/Cron.php b/system/src/Grav/Common/Scheduler/Cron.php index 5127a99..0103839 100644 --- a/system/src/Grav/Common/Scheduler/Cron.php +++ b/system/src/Grav/Common/Scheduler/Cron.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Scheduler * @author Originally based on jqCron by Arnaud Buathier modified for Grav integration - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Scheduler/IntervalTrait.php b/system/src/Grav/Common/Scheduler/IntervalTrait.php index cc5c165..5f7406b 100644 --- a/system/src/Grav/Common/Scheduler/IntervalTrait.php +++ b/system/src/Grav/Common/Scheduler/IntervalTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Scheduler * @author Originally based on peppeocchi/php-cron-scheduler modified for Grav integration - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Scheduler/Job.php b/system/src/Grav/Common/Scheduler/Job.php index 13059a5..4156119 100644 --- a/system/src/Grav/Common/Scheduler/Job.php +++ b/system/src/Grav/Common/Scheduler/Job.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Scheduler * @author Originally based on peppeocchi/php-cron-scheduler modified for Grav integration - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Scheduler/Scheduler.php b/system/src/Grav/Common/Scheduler/Scheduler.php index 73a0712..19eb9ea 100644 --- a/system/src/Grav/Common/Scheduler/Scheduler.php +++ b/system/src/Grav/Common/Scheduler/Scheduler.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Scheduler * @author Originally based on peppeocchi/php-cron-scheduler modified for Grav integration - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Security.php b/system/src/Grav/Common/Security.php index 833e648..017720c 100644 --- a/system/src/Grav/Common/Security.php +++ b/system/src/Grav/Common/Security.php @@ -3,7 +3,7 @@ /** * @package Grav\Common * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -61,7 +61,7 @@ class Security if ($clean_svg !== false ) { file_put_contents($file, $clean_svg); } else { - $quarantine_file = basename($file); + $quarantine_file = Utils::basename($file); $quarantine_dir = 'log://quarantine'; Folder::mkdir($quarantine_dir); file_put_contents("$quarantine_dir/$quarantine_file", $original_svg); @@ -138,7 +138,7 @@ class Security $options = static::getXssDefaults(); } - $list = []; + $list = [[]]; foreach ($array as $key => $value) { if (is_array($value)) { $list[] = static::detectXssFromArray($value, $prefix . $key . '.', $options); @@ -148,11 +148,7 @@ class Security } } - if (!empty($list)) { - return array_merge(...$list); - } - - return $list; + return array_merge(...$list); } /** @@ -207,7 +203,7 @@ class Security $string = preg_replace('!(�+[0-9]+)!u', '$1;', $string); // Decode entities - $string = html_entity_decode($string, ENT_NOQUOTES, 'UTF-8'); + $string = html_entity_decode($string, ENT_NOQUOTES | ENT_HTML5, 'UTF-8'); // Strip whitespace characters $string = preg_replace('!\s!u', '', $string); @@ -218,7 +214,7 @@ class Security 'on_events' => '#(<[^>]+[[a-z\x00-\x20\"\'\/])([\s\/]on|\sxmlns)[a-z].*=>?#iUu', // Match javascript:, livescript:, vbscript:, mocha:, feed: and data: protocols - 'invalid_protocols' => '#(' . implode('|', array_map('preg_quote', $invalid_protocols, ['#'])) . '):\S.*?#iUu', + 'invalid_protocols' => '#(' . implode('|', array_map('preg_quote', $invalid_protocols, ['#'])) . ')(:|\&\#58)\S.*?#iUu', // Match -moz-bindings 'moz_binding' => '#-moz-binding[a-z\x00-\x20]*:#u', diff --git a/system/src/Grav/Common/Service/AccountsServiceProvider.php b/system/src/Grav/Common/Service/AccountsServiceProvider.php index 8b158c9..60f26e5 100644 --- a/system/src/Grav/Common/Service/AccountsServiceProvider.php +++ b/system/src/Grav/Common/Service/AccountsServiceProvider.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Service * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Service/AssetsServiceProvider.php b/system/src/Grav/Common/Service/AssetsServiceProvider.php index 1e4e647..54a44a1 100644 --- a/system/src/Grav/Common/Service/AssetsServiceProvider.php +++ b/system/src/Grav/Common/Service/AssetsServiceProvider.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Service * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Service/BackupsServiceProvider.php b/system/src/Grav/Common/Service/BackupsServiceProvider.php index 00fa963..58f5021 100644 --- a/system/src/Grav/Common/Service/BackupsServiceProvider.php +++ b/system/src/Grav/Common/Service/BackupsServiceProvider.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Service * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Service/ConfigServiceProvider.php b/system/src/Grav/Common/Service/ConfigServiceProvider.php index b56f62f..e65e228 100644 --- a/system/src/Grav/Common/Service/ConfigServiceProvider.php +++ b/system/src/Grav/Common/Service/ConfigServiceProvider.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Service * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -179,7 +179,7 @@ class ConfigServiceProvider implements ServiceProviderInterface * @param string $folder_path * @return array */ - private static function pluginFolderPaths($plugins, $folder_path) + protected static function pluginFolderPaths($plugins, $folder_path) { $paths = []; diff --git a/system/src/Grav/Common/Service/ErrorServiceProvider.php b/system/src/Grav/Common/Service/ErrorServiceProvider.php index 3227365..02d38c9 100644 --- a/system/src/Grav/Common/Service/ErrorServiceProvider.php +++ b/system/src/Grav/Common/Service/ErrorServiceProvider.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Service * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Service/FilesystemServiceProvider.php b/system/src/Grav/Common/Service/FilesystemServiceProvider.php index 62dad5d..1dde5b3 100644 --- a/system/src/Grav/Common/Service/FilesystemServiceProvider.php +++ b/system/src/Grav/Common/Service/FilesystemServiceProvider.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Service * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Service/FlexServiceProvider.php b/system/src/Grav/Common/Service/FlexServiceProvider.php index 9c8cd49..e7b283a 100644 --- a/system/src/Grav/Common/Service/FlexServiceProvider.php +++ b/system/src/Grav/Common/Service/FlexServiceProvider.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Service * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Service/InflectorServiceProvider.php b/system/src/Grav/Common/Service/InflectorServiceProvider.php index 861a69e..f43a446 100644 --- a/system/src/Grav/Common/Service/InflectorServiceProvider.php +++ b/system/src/Grav/Common/Service/InflectorServiceProvider.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Service * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Service/LoggerServiceProvider.php b/system/src/Grav/Common/Service/LoggerServiceProvider.php index 5043cfb..43a67ca 100644 --- a/system/src/Grav/Common/Service/LoggerServiceProvider.php +++ b/system/src/Grav/Common/Service/LoggerServiceProvider.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Service * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Service/OutputServiceProvider.php b/system/src/Grav/Common/Service/OutputServiceProvider.php index 9a49185..2dbd343 100644 --- a/system/src/Grav/Common/Service/OutputServiceProvider.php +++ b/system/src/Grav/Common/Service/OutputServiceProvider.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Service * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Service/PagesServiceProvider.php b/system/src/Grav/Common/Service/PagesServiceProvider.php index 55f6a45..fa6631e 100644 --- a/system/src/Grav/Common/Service/PagesServiceProvider.php +++ b/system/src/Grav/Common/Service/PagesServiceProvider.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Service * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -99,7 +99,8 @@ class PagesServiceProvider implements ServiceProviderInterface /** @var Language $language */ $language = $grav['language']; - $redirectCode = (int)$config->get('system.pages.redirect_default_route', 0); + $redirect_default_route = $page->header()->redirect_default_route ?? $config->get('system.pages.redirect_default_route', 0); + $redirectCode = (int) $redirect_default_route; // Language-specific redirection scenarios if ($language->enabled() && ($language->isLanguageInUrl() xor $language->isIncludeDefaultLanguage())) { diff --git a/system/src/Grav/Common/Service/RequestServiceProvider.php b/system/src/Grav/Common/Service/RequestServiceProvider.php index 17b3149..a44c735 100644 --- a/system/src/Grav/Common/Service/RequestServiceProvider.php +++ b/system/src/Grav/Common/Service/RequestServiceProvider.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Service * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Service/SchedulerServiceProvider.php b/system/src/Grav/Common/Service/SchedulerServiceProvider.php index a952725..13918fb 100644 --- a/system/src/Grav/Common/Service/SchedulerServiceProvider.php +++ b/system/src/Grav/Common/Service/SchedulerServiceProvider.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Service * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Service/SessionServiceProvider.php b/system/src/Grav/Common/Service/SessionServiceProvider.php index 88c833f..80e24b4 100644 --- a/system/src/Grav/Common/Service/SessionServiceProvider.php +++ b/system/src/Grav/Common/Service/SessionServiceProvider.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Service * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -40,7 +40,8 @@ class SessionServiceProvider implements ServiceProviderInterface // Get session options. $enabled = (bool)$config->get('system.session.enabled', false); - $cookie_secure = (bool)$config->get('system.session.secure', false); + $cookie_secure = $config->get('system.session.secure', false) + || ($config->get('system.session.secure_https', true) && $uri->scheme(true) === 'https'); $cookie_httponly = (bool)$config->get('system.session.httponly', true); $cookie_lifetime = (int)$config->get('system.session.timeout', 1800); $cookie_domain = $config->get('system.session.domain'); diff --git a/system/src/Grav/Common/Service/StreamsServiceProvider.php b/system/src/Grav/Common/Service/StreamsServiceProvider.php index edde09a..f501b49 100644 --- a/system/src/Grav/Common/Service/StreamsServiceProvider.php +++ b/system/src/Grav/Common/Service/StreamsServiceProvider.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Service * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Service/TaskServiceProvider.php b/system/src/Grav/Common/Service/TaskServiceProvider.php index 49ce147..2989e9d 100644 --- a/system/src/Grav/Common/Service/TaskServiceProvider.php +++ b/system/src/Grav/Common/Service/TaskServiceProvider.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Service * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Session.php b/system/src/Grav/Common/Session.php index 3221627..513143e 100644 --- a/system/src/Grav/Common/Session.php +++ b/system/src/Grav/Common/Session.php @@ -3,7 +3,7 @@ /** * @package Grav\Common * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Taxonomy.php b/system/src/Grav/Common/Taxonomy.php index 0d0a450..5079a25 100644 --- a/system/src/Grav/Common/Taxonomy.php +++ b/system/src/Grav/Common/Taxonomy.php @@ -3,7 +3,7 @@ /** * @package Grav\Common * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Theme.php b/system/src/Grav/Common/Theme.php index a5006a2..6013129 100644 --- a/system/src/Grav/Common/Theme.php +++ b/system/src/Grav/Common/Theme.php @@ -3,7 +3,7 @@ /** * @package Grav\Common * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Themes.php b/system/src/Grav/Common/Themes.php index 6126841..3567cca 100644 --- a/system/src/Grav/Common/Themes.php +++ b/system/src/Grav/Common/Themes.php @@ -3,7 +3,7 @@ /** * @package Grav\Common * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Twig/Exception/TwigException.php b/system/src/Grav/Common/Twig/Exception/TwigException.php index 8f543dc..7605de4 100644 --- a/system/src/Grav/Common/Twig/Exception/TwigException.php +++ b/system/src/Grav/Common/Twig/Exception/TwigException.php @@ -3,17 +3,19 @@ /** * @package Grav\Common\Twig\Exception * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Common\Twig\Exception; +use RuntimeException; + /** * TwigException gets thrown when you use {% throw code message %} in twig. * * This allows Grav to catch 401, 403 and 404 exceptions and display proper error page. */ -class TwigException extends \RuntimeException +class TwigException extends RuntimeException { } diff --git a/system/src/Grav/Common/Twig/Extension/FilesystemExtension.php b/system/src/Grav/Common/Twig/Extension/FilesystemExtension.php index dfd5fdd..0ae793e 100644 --- a/system/src/Grav/Common/Twig/Extension/FilesystemExtension.php +++ b/system/src/Grav/Common/Twig/Extension/FilesystemExtension.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -266,24 +266,24 @@ class FilesystemExtension extends AbstractExtension } /** - * @param string $file + * @param string $filename * @param string|null $required_sections * @param bool $as_arrays * @param bool $read_thumbnail * @return array|false */ - public function exif_read_data($file, ?string $required_sections, bool $as_arrays = false, bool $read_thumbnail = false) + public function exif_read_data($filename, ?string $required_sections, bool $as_arrays = false, bool $read_thumbnail = false) { - if (!Utils::functionExists('exif_read_data') || !$this->checkFilename($file)) { + if (!Utils::functionExists('exif_read_data') || !$this->checkFilename($filename)) { return false; } - return exif_read_data($file, $required_sections, $as_arrays, $read_thumbnail); + return exif_read_data($filename, $required_sections, $as_arrays, $read_thumbnail); } /** * @param string $filename - * @return string|false + * @return int|false */ public function exif_imagetype($filename) { @@ -311,18 +311,18 @@ class FilesystemExtension extends AbstractExtension /** * @param string $algo - * @param string $data + * @param string $filename * @param string $key * @param bool $binary * @return string|false */ - public function hash_hmac_file(string $algo, string $data, string $key, bool $binary = false) + public function hash_hmac_file(string $algo, string $filename, string $key, bool $binary = false) { - if (!$this->checkFilename($data)) { + if (!$this->checkFilename($filename)) { return false; } - return hash_hmac_file($algo, $data, $key, $binary); + return hash_hmac_file($algo, $filename, $key, $binary); } /** @@ -373,11 +373,7 @@ class FilesystemExtension extends AbstractExtension */ public function pathinfo($path, $flags = null) { - if (null !== $flags) { - return pathinfo($path, (int)$flags); - } - - return pathinfo($path); + return Utils::pathinfo($path, $flags); } /** diff --git a/system/src/Grav/Common/Twig/Extension/GravExtension.php b/system/src/Grav/Common/Twig/Extension/GravExtension.php index 638617c..52e2a18 100644 --- a/system/src/Grav/Common/Twig/Extension/GravExtension.php +++ b/system/src/Grav/Common/Twig/Extension/GravExtension.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -22,6 +22,7 @@ use Grav\Common\Page\Media; use Grav\Common\Scheduler\Cron; use Grav\Common\Security; use Grav\Common\Twig\TokenParser\TwigTokenParserCache; +use Grav\Common\Twig\TokenParser\TwigTokenParserLink; use Grav\Common\Twig\TokenParser\TwigTokenParserRender; use Grav\Common\Twig\TokenParser\TwigTokenParserScript; use Grav\Common\Twig\TokenParser\TwigTokenParserStyle; @@ -115,6 +116,7 @@ class GravExtension extends AbstractExtension implements GlobalsInterface new TwigFilter('defined', [$this, 'definedDefaultFilter']), new TwigFilter('ends_with', [$this, 'endsWithFilter']), new TwigFilter('fieldName', [$this, 'fieldNameFilter']), + new TwigFilter('parent_field', [$this, 'fieldParentFilter']), new TwigFilter('ksort', [$this, 'ksortFilter']), new TwigFilter('ltrim', [$this, 'ltrimFilter']), new TwigFilter('markdown', [$this, 'markdownFunction'], ['needs_context' => true, 'is_safe' => ['html']]), @@ -252,12 +254,17 @@ class GravExtension extends AbstractExtension implements GlobalsInterface new TwigTokenParserTryCatch(), new TwigTokenParserScript(), new TwigTokenParserStyle(), + new TwigTokenParserLink(), new TwigTokenParserMarkdown(), new TwigTokenParserSwitch(), new TwigTokenParserCache(), ]; } + /** + * @param mixed $var + * @return string + */ public function print_r($var) { return print_r($var, true); @@ -276,6 +283,20 @@ class GravExtension extends AbstractExtension implements GlobalsInterface return array_shift($path) . ($path ? '[' . implode('][', $path) . ']' : ''); } + /** + * Filters field name by changing dot notation into array notation. + * + * @param string $str + * @return string + */ + public function fieldParentFilter($str) + { + $path = explode('.', rtrim($str, '.')); + array_pop($path); + + return implode('.', $path); + } + /** * Protects email address. * @@ -847,6 +868,12 @@ class GravExtension extends AbstractExtension implements GlobalsInterface if (($numargs === 3 && is_array($args[1])) || ($numargs === 2 && !is_array($args[1]))) { $lang = array_pop($args); + /** @var Language $language */ + $language = $this->grav['language']; + if (is_string($lang) && !$language->getLanguageCode($lang)) { + $args[] = $lang; + $lang = null; + } } elseif ($numargs === 2 && is_array($args[1])) { $subs = array_pop($args); $args = array_merge($args, $subs); @@ -1354,7 +1381,7 @@ class GravExtension extends AbstractExtension implements GlobalsInterface */ public function vardumpFunc($var) { - var_dump($var); + dump($var); } /** @@ -1418,7 +1445,7 @@ class GravExtension extends AbstractExtension implements GlobalsInterface * @param array $context Twig Context * @param string $var variable to be found (using dot notation) * @param null $default the default value to be used as last resort - * @param null $page an optional page to use for the current page + * @param PageInterface|null $page an optional page to use for the current page * @param bool $exists toggle to simply return the page where the variable is set, else null * @return mixed */ diff --git a/system/src/Grav/Common/Twig/Node/TwigNodeCache.php b/system/src/Grav/Common/Twig/Node/TwigNodeCache.php index 0ed1fff..e05e372 100644 --- a/system/src/Grav/Common/Twig/Node/TwigNodeCache.php +++ b/system/src/Grav/Common/Twig/Node/TwigNodeCache.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Twig/Node/TwigNodeLink.php b/system/src/Grav/Common/Twig/Node/TwigNodeLink.php new file mode 100644 index 0000000..a468633 --- /dev/null +++ b/system/src/Grav/Common/Twig/Node/TwigNodeLink.php @@ -0,0 +1,114 @@ + $file, 'group' => $group, 'priority' => $priority, 'attributes' => $attributes]; + $nodes = array_filter($nodes); + + parent::__construct($nodes, ['rel' => $rel], $lineno, $tag); + } + + /** + * Compiles the node to PHP. + * + * @param Compiler $compiler A Twig Compiler instance + * @return void + * @throws LogicException + */ + public function compile(Compiler $compiler): void + { + $compiler->addDebugInfo($this); + if (!$this->hasNode('file')) { + return; + } + + $compiler->write('$attributes = [\'rel\' => \'' . $this->getAttribute('rel') . '\'];' . "\n"); + if ($this->hasNode('attributes')) { + $compiler + ->write('$attributes += ') + ->subcompile($this->getNode('attributes')) + ->raw(';' . PHP_EOL) + ->write('if (!is_array($attributes)) {' . PHP_EOL) + ->indent() + ->write("throw new UnexpectedValueException('{% {$this->tagName} with x %}: x is not an array');" . PHP_EOL) + ->outdent() + ->write('}' . PHP_EOL); + } + + if ($this->hasNode('group')) { + $compiler + ->write('$group = ') + ->subcompile($this->getNode('group')) + ->raw(';' . PHP_EOL) + ->write('if (!is_string($group)) {' . PHP_EOL) + ->indent() + ->write("throw new UnexpectedValueException('{% {$this->tagName} in x %}: x is not a string');" . PHP_EOL) + ->outdent() + ->write('}' . PHP_EOL); + } else { + $compiler->write('$group = \'head\';' . PHP_EOL); + } + + if ($this->hasNode('priority')) { + $compiler + ->write('$priority = (int)(') + ->subcompile($this->getNode('priority')) + ->raw(');' . PHP_EOL); + } else { + $compiler->write('$priority = 10;' . PHP_EOL); + } + + $compiler->write("\$assets = \\Grav\\Common\\Grav::instance()['assets'];" . PHP_EOL); + $compiler->write("\$block = \$context['block'] ?? null;" . PHP_EOL); + + $compiler + ->write('$file = (string)(') + ->subcompile($this->getNode('file')) + ->raw(');' . PHP_EOL); + + // Assets support. + $compiler->write('$assets->addLink($file, [\'group\' => $group, \'priority\' => $priority] + $attributes);' . PHP_EOL); + + // HtmlBlock support. + $compiler + ->write('if ($block instanceof \Grav\Framework\ContentBlock\HtmlBlock) {' . PHP_EOL) + ->indent() + ->write('$block->addLink([\'href\'=> $file] + $attributes, $priority, $group);' . PHP_EOL) + ->outdent() + ->write('}' . PHP_EOL); + } +} diff --git a/system/src/Grav/Common/Twig/Node/TwigNodeMarkdown.php b/system/src/Grav/Common/Twig/Node/TwigNodeMarkdown.php index 81cecae..4637db7 100644 --- a/system/src/Grav/Common/Twig/Node/TwigNodeMarkdown.php +++ b/system/src/Grav/Common/Twig/Node/TwigNodeMarkdown.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Twig/Node/TwigNodeRender.php b/system/src/Grav/Common/Twig/Node/TwigNodeRender.php index 798c6ba..dbce3c6 100644 --- a/system/src/Grav/Common/Twig/Node/TwigNodeRender.php +++ b/system/src/Grav/Common/Twig/Node/TwigNodeRender.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -75,6 +75,7 @@ class TwigNodeRender extends Node implements NodeCaptureInterface ->outdent() ->write('} else {' . PHP_EOL) ->indent() + ->write('\Grav\Common\Assets\BlockAssets::registerAssets($html);' . PHP_EOL) ->write('echo (string)$html;' . PHP_EOL) ->outdent() ->write('}' . PHP_EOL) diff --git a/system/src/Grav/Common/Twig/Node/TwigNodeScript.php b/system/src/Grav/Common/Twig/Node/TwigNodeScript.php index 46a0870..03554bd 100644 --- a/system/src/Grav/Common/Twig/Node/TwigNodeScript.php +++ b/system/src/Grav/Common/Twig/Node/TwigNodeScript.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -27,6 +27,7 @@ class TwigNodeScript extends Node implements NodeCaptureInterface /** * TwigNodeScript constructor. * @param Node|null $body + * @param string|null $type * @param AbstractExpression|null $file * @param AbstractExpression|null $group * @param AbstractExpression|null $priority @@ -34,12 +35,12 @@ class TwigNodeScript extends Node implements NodeCaptureInterface * @param int $lineno * @param string|null $tag */ - public function __construct(?Node $body, ?AbstractExpression $file, ?AbstractExpression $group, ?AbstractExpression $priority, ?AbstractExpression $attributes, $lineno = 0, $tag = null) + public function __construct(?Node $body, ?string $type, ?AbstractExpression $file, ?AbstractExpression $group, ?AbstractExpression $priority, ?AbstractExpression $attributes, $lineno = 0, $tag = null) { $nodes = ['body' => $body, 'file' => $file, 'group' => $group, 'priority' => $priority, 'attributes' => $attributes]; $nodes = array_filter($nodes); - parent::__construct($nodes, [], $lineno, $tag); + parent::__construct($nodes, ['type' => $type], $lineno, $tag); } /** @@ -53,52 +54,89 @@ class TwigNodeScript extends Node implements NodeCaptureInterface { $compiler->addDebugInfo($this); - $compiler->write("\$assets = \\Grav\\Common\\Grav::instance()['assets'];\n"); - if ($this->hasNode('attributes')) { $compiler ->write('$attributes = ') ->subcompile($this->getNode('attributes')) - ->raw(";\n") - ->write("if (!is_array(\$attributes)) {\n") + ->raw(';' . PHP_EOL) + ->write('if (!is_array($attributes)) {' . PHP_EOL) ->indent() - ->write("throw new UnexpectedValueException('{% {$this->tagName} with x %}: x is not an array');\n") + ->write("throw new UnexpectedValueException('{% {$this->tagName} with x %}: x is not an array');" . PHP_EOL) ->outdent() - ->write("}\n"); + ->write('}' . PHP_EOL); } else { - $compiler->write('$attributes = [];' . "\n"); + $compiler->write('$attributes = [];' . PHP_EOL); } if ($this->hasNode('group')) { $compiler - ->write("\$attributes['group'] = ") + ->write('$group = ') ->subcompile($this->getNode('group')) - ->raw(";\n") - ->write("if (!is_string(\$attributes['group'])) {\n") + ->raw(';' . PHP_EOL) + ->write('if (!is_string($group)) {' . PHP_EOL) ->indent() - ->write("throw new UnexpectedValueException('{% {$this->tagName} in x %}: x is not a string');\n") + ->write("throw new UnexpectedValueException('{% {$this->tagName} in x %}: x is not a string');" . PHP_EOL) ->outdent() - ->write("}\n"); + ->write('}' . PHP_EOL); + } else { + $compiler->write('$group = \'head\';' . PHP_EOL); } if ($this->hasNode('priority')) { $compiler - ->write("\$attributes['priority'] = (int)(") + ->write('$priority = (int)(') ->subcompile($this->getNode('priority')) - ->raw(");\n"); + ->raw(');' . PHP_EOL); + } else { + $compiler->write('$priority = 10;' . PHP_EOL); } + $compiler->write("\$assets = \\Grav\\Common\\Grav::instance()['assets'];" . PHP_EOL); + $compiler->write("\$block = \$context['block'] ?? null;" . PHP_EOL); + if ($this->hasNode('file')) { + // JS file. $compiler - ->write('$assets->addJs(') + ->write('$file = (string)(') ->subcompile($this->getNode('file')) - ->raw(", \$attributes);\n"); - } else { + ->raw(');' . PHP_EOL); + + $method = $this->getAttribute('type') === 'module' ? 'addJsModule' : 'addJs'; + + // Assets support. + $compiler->write('$assets->' . $method . '($file, [\'group\' => $group, \'priority\' => $priority] + $attributes);' . PHP_EOL); + + $method = $this->getAttribute('type') === 'module' ? 'addModule' : 'addScript'; + + // HtmlBlock support. $compiler - ->write("ob_start();\n") + ->write('if ($block instanceof \Grav\Framework\ContentBlock\HtmlBlock) {' . PHP_EOL) + ->indent() + ->write('$block->' . $method . '([\'src\'=> $file] + $attributes, $priority, $group);' . PHP_EOL) + ->outdent() + ->write('}' . PHP_EOL); + + } else { + // Inline script. + $compiler + ->write('ob_start();' . PHP_EOL) ->subcompile($this->getNode('body')) - ->write('$content = ob_get_clean();' . "\n") - ->write("\$assets->addInlineJs(\$content, \$attributes);\n"); + ->write('$content = ob_get_clean();' . PHP_EOL); + + $method = $this->getAttribute('type') === 'module' ? 'addInlineJsModule' : 'addInlineJs'; + + // Assets support. + $compiler->write('$assets->' . $method . '($content, [\'group\' => $group, \'priority\' => $priority] + $attributes);' . PHP_EOL); + + $method = $this->getAttribute('type') === 'module' ? 'addInlineModule' : 'addInlineScript'; + + // HtmlBlock support. + $compiler + ->write('if ($block instanceof \Grav\Framework\ContentBlock\HtmlBlock) {' . PHP_EOL) + ->indent() + ->write('$block->' . $method . '([\'content\'=> $content] + $attributes, $priority, $group);' . PHP_EOL) + ->outdent() + ->write('}' . PHP_EOL); } } } diff --git a/system/src/Grav/Common/Twig/Node/TwigNodeStyle.php b/system/src/Grav/Common/Twig/Node/TwigNodeStyle.php index 05355f9..1c3568c 100644 --- a/system/src/Grav/Common/Twig/Node/TwigNodeStyle.php +++ b/system/src/Grav/Common/Twig/Node/TwigNodeStyle.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -41,6 +41,7 @@ class TwigNodeStyle extends Node implements NodeCaptureInterface parent::__construct($nodes, [], $lineno, $tag); } + /** * Compiles the node to PHP. * @@ -52,52 +53,81 @@ class TwigNodeStyle extends Node implements NodeCaptureInterface { $compiler->addDebugInfo($this); - $compiler->write("\$assets = \\Grav\\Common\\Grav::instance()['assets'];\n"); - if ($this->hasNode('attributes')) { $compiler ->write('$attributes = ') ->subcompile($this->getNode('attributes')) - ->raw(";\n") - ->write("if (!is_array(\$attributes)) {\n") + ->raw(';' . PHP_EOL) + ->write('if (!is_array($attributes)) {' . PHP_EOL) ->indent() - ->write("throw new UnexpectedValueException('{% {$this->tagName} with x %}: x is not an array');\n") + ->write("throw new UnexpectedValueException('{% {$this->tagName} with x %}: x is not an array');" . PHP_EOL) ->outdent() - ->write("}\n"); + ->write('}' . PHP_EOL); } else { - $compiler->write('$attributes = [];' . "\n"); + $compiler->write('$attributes = [];' . PHP_EOL); } if ($this->hasNode('group')) { $compiler - ->write("\$attributes['group'] = ") + ->write('$group = ') ->subcompile($this->getNode('group')) - ->raw(";\n") - ->write("if (!is_string(\$attributes['group'])) {\n") + ->raw(';' . PHP_EOL) + ->write('if (!is_string($group)) {' . PHP_EOL) ->indent() - ->write("throw new UnexpectedValueException('{% {$this->tagName} in x %}: x is not a string');\n") + ->write("throw new UnexpectedValueException('{% {$this->tagName} in x %}: x is not a string');" . PHP_EOL) ->outdent() - ->write("}\n"); + ->write('}' . PHP_EOL); + } else { + $compiler->write('$group = \'head\';' . PHP_EOL); } if ($this->hasNode('priority')) { $compiler - ->write("\$attributes['priority'] = (int)(") + ->write('$priority = (int)(') ->subcompile($this->getNode('priority')) - ->raw(");\n"); + ->raw(');' . PHP_EOL); + } else { + $compiler->write('$priority = 10;' . PHP_EOL); } + $compiler->write("\$assets = \\Grav\\Common\\Grav::instance()['assets'];" . PHP_EOL); + $compiler->write("\$block = \$context['block'] ?? null;" . PHP_EOL); + if ($this->hasNode('file')) { + // CSS file. $compiler - ->write('$assets->addCss(') + ->write('$file = (string)(') ->subcompile($this->getNode('file')) - ->raw(", \$attributes);\n"); - } else { + ->raw(');' . PHP_EOL); + + // Assets support. + $compiler->write('$assets->addCss($file, [\'group\' => $group, \'priority\' => $priority] + $attributes);' . PHP_EOL); + + // HtmlBlock support. $compiler - ->write("ob_start();\n") + ->write('if ($block instanceof \Grav\Framework\ContentBlock\HtmlBlock) {' . PHP_EOL) + ->indent() + ->write('$block->addStyle([\'href\'=> $file] + $attributes, $priority, $group);' . PHP_EOL) + ->outdent() + ->write('}' . PHP_EOL); + + } else { + // Inline style. + $compiler + ->write('ob_start();' . PHP_EOL) ->subcompile($this->getNode('body')) - ->write('$content = ob_get_clean();' . "\n") - ->write("\$assets->addInlineCss(\$content, \$attributes);\n"); + ->write('$content = ob_get_clean();' . PHP_EOL); + + // Assets support. + $compiler->write('$assets->addInlineCss($content, [\'group\' => $group, \'priority\' => $priority] + $attributes);' . PHP_EOL); + + // HtmlBlock support. + $compiler + ->write('if ($block instanceof \Grav\Framework\ContentBlock\HtmlBlock) {' . PHP_EOL) + ->indent() + ->write('$block->addInlineStyle([\'content\'=> $content] + $attributes, $priority, $group);' . PHP_EOL) + ->outdent() + ->write('}' . PHP_EOL); } } } diff --git a/system/src/Grav/Common/Twig/Node/TwigNodeSwitch.php b/system/src/Grav/Common/Twig/Node/TwigNodeSwitch.php index 43ae56f..7bd368e 100644 --- a/system/src/Grav/Common/Twig/Node/TwigNodeSwitch.php +++ b/system/src/Grav/Common/Twig/Node/TwigNodeSwitch.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Twig/Node/TwigNodeThrow.php b/system/src/Grav/Common/Twig/Node/TwigNodeThrow.php index 3bfe612..41ca15d 100644 --- a/system/src/Grav/Common/Twig/Node/TwigNodeThrow.php +++ b/system/src/Grav/Common/Twig/Node/TwigNodeThrow.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Twig/Node/TwigNodeTryCatch.php b/system/src/Grav/Common/Twig/Node/TwigNodeTryCatch.php index 40e9240..1213b7c 100644 --- a/system/src/Grav/Common/Twig/Node/TwigNodeTryCatch.php +++ b/system/src/Grav/Common/Twig/Node/TwigNodeTryCatch.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserCache.php b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserCache.php index 371e89a..1cf2191 100644 --- a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserCache.php +++ b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserCache.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserLink.php b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserLink.php new file mode 100644 index 0000000..2cb0208 --- /dev/null +++ b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserLink.php @@ -0,0 +1,109 @@ +getLine(); + + [$rel, $file, $group, $priority, $attributes] = $this->parseArguments($token); + + return new TwigNodeLink($rel, $file, $group, $priority, $attributes, $lineno, $this->getTag()); + } + + /** + * @param Token $token + * @return array + */ + protected function parseArguments(Token $token): array + { + $stream = $this->parser->getStream(); + + + $rel = null; + if ($stream->test(Token::NAME_TYPE, $this->rel)) { + $rel = $stream->getCurrent()->getValue(); + $stream->next(); + } + + $file = null; + if (!$stream->test(Token::NAME_TYPE) && !$stream->test(Token::BLOCK_END_TYPE)) { + $file = $this->parser->getExpressionParser()->parseExpression(); + } + + $group = null; + if ($stream->nextIf(Token::NAME_TYPE, 'at')) { + $group = $this->parser->getExpressionParser()->parseExpression(); + } + + $priority = null; + if ($stream->nextIf(Token::NAME_TYPE, 'priority')) { + $stream->expect(Token::PUNCTUATION_TYPE, ':'); + $priority = $this->parser->getExpressionParser()->parseExpression(); + } + + $attributes = null; + if ($stream->nextIf(Token::NAME_TYPE, 'with')) { + $attributes = $this->parser->getExpressionParser()->parseExpression(); + } + + $stream->expect(Token::BLOCK_END_TYPE); + + return [$rel, $file, $group, $priority, $attributes]; + } + + /** + * Gets the tag name associated with this token parser. + * + * @return string The tag name + */ + public function getTag(): string + { + return 'link'; + } +} diff --git a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserMarkdown.php b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserMarkdown.php index 9dab4ae..49e34e6 100644 --- a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserMarkdown.php +++ b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserMarkdown.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserRender.php b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserRender.php index ab2bdde..de16266 100644 --- a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserRender.php +++ b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserRender.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserScript.php b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserScript.php index b860631..b943a25 100644 --- a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserScript.php +++ b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserScript.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -15,14 +15,20 @@ use Twig\Token; use Twig\TokenParser\AbstractTokenParser; /** - * Adds a script to head/bottom/custom location in the document. + * Adds a script to head/bottom/custom group location in the document. * - * {% script 'theme://js/something.js' at 'bottom' priority: 20 with { defer: true, async: true } %} + * {% script 'theme://js/something.js' at 'bottom' priority: 20 with { position: 'pipeline', loading: 'async defer' } %} + * {% script module 'theme://js/module.mjs' at 'head' %} * + * {% script 'theme://js/something.js' at 'bottom' priority: 20 with { loading: 'inline' } %} * {% script at 'bottom' priority: 20 %} - * alert('Warning!'); + * alert('Warning!'); + * {% endscript %} + * + * {% script module 'theme://js/module.mjs' at 'bottom' with { loading: 'inline' } %} + * {% script module at 'bottom' %} + * ... * {% endscript %} - */ class TwigTokenParserScript extends AbstractTokenParser { @@ -38,7 +44,7 @@ class TwigTokenParserScript extends AbstractTokenParser $lineno = $token->getLine(); $stream = $this->parser->getStream(); - [$file, $group, $priority, $attributes] = $this->parseArguments($token); + [$type, $file, $group, $priority, $attributes] = $this->parseArguments($token); $content = null; if ($file === null) { @@ -46,7 +52,7 @@ class TwigTokenParserScript extends AbstractTokenParser $stream->expect(Token::BLOCK_END_TYPE); } - return new TwigNodeScript($content, $file, $group, $priority, $attributes, $lineno, $this->getTag()); + return new TwigNodeScript($content, $type, $file, $group, $priority, $attributes, $lineno, $this->getTag()); } /** @@ -73,6 +79,12 @@ class TwigTokenParserScript extends AbstractTokenParser } while (true); } + $type = null; + if ($stream->test(Token::NAME_TYPE, 'module')) { + $type = $stream->getCurrent()->getValue(); + $stream->next(); + } + $file = null; if (!$stream->test(Token::NAME_TYPE) && !$stream->test(Token::OPERATOR_TYPE, 'in') && !$stream->test(Token::BLOCK_END_TYPE)) { $file = $this->parser->getExpressionParser()->parseExpression(); @@ -96,7 +108,7 @@ class TwigTokenParserScript extends AbstractTokenParser $stream->expect(Token::BLOCK_END_TYPE); - return [$file, $group, $priority, $attributes]; + return [$type, $file, $group, $priority, $attributes]; } /** diff --git a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserStyle.php b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserStyle.php index c8d9544..0687395 100644 --- a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserStyle.php +++ b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserStyle.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserSwitch.php b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserSwitch.php index 4540bbf..c63fe95 100644 --- a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserSwitch.php +++ b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserSwitch.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. * @origin https://gist.github.com/maxgalbu/9409182 */ diff --git a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserThrow.php b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserThrow.php index bd4adab..41f415d 100644 --- a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserThrow.php +++ b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserThrow.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserTryCatch.php b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserTryCatch.php index 46af176..9930632 100644 --- a/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserTryCatch.php +++ b/system/src/Grav/Common/Twig/TokenParser/TwigTokenParserTryCatch.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Twig/Twig.php b/system/src/Grav/Common/Twig/Twig.php index 19bad01..39ae3c6 100644 --- a/system/src/Grav/Common/Twig/Twig.php +++ b/system/src/Grav/Common/Twig/Twig.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -33,6 +33,7 @@ use Twig\Extension\DebugExtension; use Twig\Extension\StringLoaderExtension; use Twig\Loader\ArrayLoader; use Twig\Loader\ChainLoader; +use Twig\Loader\ExistsLoaderInterface; use Twig\Loader\FilesystemLoader; use Twig\Profiler\Profile; use Twig\TwigFilter; @@ -160,7 +161,7 @@ class Twig $this->twig = new TwigEnvironment($loader_chain, $params); - $this->twig->registerUndefinedFunctionCallback(function ($name) use ($config) { + $this->twig->registerUndefinedFunctionCallback(function (string $name) use ($config) { $allowed = $config->get('system.twig.safe_functions'); if (is_array($allowed) && in_array($name, $allowed, true) && function_exists($name)) { return new TwigFunction($name, $name); @@ -184,7 +185,7 @@ class Twig return false; }); - $this->twig->registerUndefinedFilterCallback(function ($name) use ($config) { + $this->twig->registerUndefinedFilterCallback(function (string $name) use ($config) { $allowed = $config->get('system.twig.safe_filters'); if (is_array($allowed) && in_array($name, $allowed, true) && function_exists($name)) { return new TwigFilter($name, $name); @@ -514,6 +515,8 @@ class Twig $twig_extension = $extension ? '.'. $extension .TWIG_EXT : TEMPLATE_EXT; $template_file = $this->template($page->template() . $twig_extension); + // TODO: no longer needed in Twig 3. + /** @var ExistsLoaderInterface $loader */ $loader = $this->twig->getLoader(); if ($loader->exists($template_file)) { // template.xxx.twig diff --git a/system/src/Grav/Common/Twig/TwigClockworkDataSource.php b/system/src/Grav/Common/Twig/TwigClockworkDataSource.php index 11127b8..407a05e 100644 --- a/system/src/Grav/Common/Twig/TwigClockworkDataSource.php +++ b/system/src/Grav/Common/Twig/TwigClockworkDataSource.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Twig/TwigClockworkDumper.php b/system/src/Grav/Common/Twig/TwigClockworkDumper.php index 2c1f4be..c960cf2 100644 --- a/system/src/Grav/Common/Twig/TwigClockworkDumper.php +++ b/system/src/Grav/Common/Twig/TwigClockworkDumper.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Twig/TwigEnvironment.php b/system/src/Grav/Common/Twig/TwigEnvironment.php index bebbdf1..6b2a86b 100644 --- a/system/src/Grav/Common/Twig/TwigEnvironment.php +++ b/system/src/Grav/Common/Twig/TwigEnvironment.php @@ -3,13 +3,16 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Common\Twig; use Twig\Environment; +use Twig\Error\LoaderError; +use Twig\Template; +use Twig\TemplateWrapper; /** * Class TwigEnvironment @@ -18,4 +21,34 @@ use Twig\Environment; class TwigEnvironment extends Environment { use WriteCacheFileTrait; + + /** + * @inheritDoc + */ + public function resolveTemplate($names) + { + if (!\is_array($names)) { + $names = [$names]; + } + + $count = \count($names); + foreach ($names as $name) { + if ($name instanceof Template) { + return $name; + } + if ($name instanceof TemplateWrapper) { + return $name; + } + + // Optimization: Avoid throwing an exception when it would be ignored anyway. + if (1 !== $count && !$this->getLoader()->exists($name)) { + continue; + } + + // Throws LoaderError: Unable to find template "%s". + return $this->loadTemplate($name); + } + + throw new LoaderError(sprintf('Unable to find one of the following templates: "%s".', implode('", "', $names))); + } } diff --git a/system/src/Grav/Common/Twig/TwigExtension.php b/system/src/Grav/Common/Twig/TwigExtension.php index 698858b..07c442e 100644 --- a/system/src/Grav/Common/Twig/TwigExtension.php +++ b/system/src/Grav/Common/Twig/TwigExtension.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Twig/WriteCacheFileTrait.php b/system/src/Grav/Common/Twig/WriteCacheFileTrait.php index edab8e5..575a281 100644 --- a/system/src/Grav/Common/Twig/WriteCacheFileTrait.php +++ b/system/src/Grav/Common/Twig/WriteCacheFileTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Twig * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Uri.php b/system/src/Grav/Common/Uri.php index 7bef097..76d9a99 100644 --- a/system/src/Grav/Common/Uri.php +++ b/system/src/Grav/Common/Uri.php @@ -3,7 +3,7 @@ /** * @package Grav\Common * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -165,7 +165,7 @@ class Uri } // Handle custom base - $custom_base = rtrim($grav['config']->get('system.custom_base_url'), '/'); + $custom_base = rtrim($grav['config']->get('system.custom_base_url', ''), '/'); if ($custom_base) { $custom_parts = parse_url($custom_base); if ($custom_parts === false) { @@ -217,7 +217,7 @@ class Uri $path = $bits['path'] ?? '/'; // remove the extension if there is one set - $parts = pathinfo($path); + $parts = Utils::pathinfo($path); // set the original basename $this->basename = $parts['basename']; @@ -337,9 +337,9 @@ class Uri /** * Get URI parameter. * - * @param string|null $id - * @param string|bool|null $default - * @return bool|string + * @param string $id + * @param string|false|null $default + * @return string|false|null */ public function param($id, $default = false) { @@ -623,6 +623,7 @@ class Uri /** * @return string */ + #[\ReturnTypeWillChange] public function __toString() { return static::buildUrl($this->toArray()); @@ -853,7 +854,7 @@ class Uri } if ($full_path) { - $path_info = pathinfo($full_path); + $path_info = Utils::pathinfo($full_path); $page_path = $path_info['dirname']; $filename = ''; @@ -898,7 +899,7 @@ class Uri $routes = $pages->routes(); // if this is an image, get the proper path - $url_bits = pathinfo($url_path); + $url_bits = Utils::pathinfo($url_path); if (isset($url_bits['extension'])) { $target_path = $url_bits['dirname']; } else { @@ -1045,7 +1046,7 @@ class Uri $base_url = rtrim($base . $grav['pages']->base(), '/') . $language_append; // if absolute and starts with a base_url move on - if (pathinfo($markdown_url, PATHINFO_DIRNAME) === '.' && $page->url() === '/') { + if (Utils::pathinfo($markdown_url, PATHINFO_DIRNAME) === '.' && $page->url() === '/') { return '/' . $markdown_url; } // no path to convert @@ -1084,7 +1085,7 @@ class Uri return $normalized_url; } - $path_info = pathinfo($full_path); + $path_info = Utils::pathinfo($full_path); $page_path = $path_info['dirname']; $filename = ''; @@ -1272,9 +1273,9 @@ class Uri $this->path = rawurldecode(parse_url('http://example.com' . $request_uri, PHP_URL_PATH)); // Build query string. - $this->query = $env['QUERY_STRING'] ?? ''; + $this->query = $env['QUERY_STRING'] ?? ''; if ($this->query === '') { - $this->query = parse_url('http://example.com' . $request_uri, PHP_URL_QUERY); + $this->query = parse_url('http://example.com' . $request_uri, PHP_URL_QUERY) ?? ''; } // Support ngnix routes. diff --git a/system/src/Grav/Common/User/Access.php b/system/src/Grav/Common/User/Access.php index 6503c89..96f4d96 100644 --- a/system/src/Grav/Common/User/Access.php +++ b/system/src/Grav/Common/User/Access.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\User * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/User/Authentication.php b/system/src/Grav/Common/User/Authentication.php index 6ff95e7..c32fd62 100644 --- a/system/src/Grav/Common/User/Authentication.php +++ b/system/src/Grav/Common/User/Authentication.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\User * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/User/DataUser/User.php b/system/src/Grav/Common/User/DataUser/User.php index 4735342..71e7f9b 100644 --- a/system/src/Grav/Common/User/DataUser/User.php +++ b/system/src/Grav/Common/User/DataUser/User.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\User * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -21,6 +21,7 @@ use Grav\Common\Page\Medium\MediumFactory; use Grav\Common\User\Authentication; use Grav\Common\User\Interfaces\UserInterface; use Grav\Common\User\Traits\UserTrait; +use Grav\Common\Utils; use Grav\Framework\Flex\Flex; use function is_array; @@ -57,6 +58,7 @@ class User extends Data implements UserInterface * @param string $offset * @return bool */ + #[\ReturnTypeWillChange] public function offsetExists($offset) { $value = parent::offsetExists($offset); @@ -73,6 +75,7 @@ class User extends Data implements UserInterface * @param string $offset * @return mixed */ + #[\ReturnTypeWillChange] public function offsetGet($offset) { $value = parent::offsetGet($offset); @@ -175,7 +178,7 @@ class User extends Data implements UserInterface if ($path && is_file($path)) { $medium = MediumFactory::fromFile($path); if ($medium) { - $media->add(basename($path), $medium); + $media->add(Utils::basename($path), $medium); } } @@ -293,6 +296,7 @@ class User extends Data implements UserInterface * @return int * @deprecated 1.6 Method makes no sense for user account. */ + #[\ReturnTypeWillChange] public function count() { user_error(__CLASS__ . '::' . __FUNCTION__ . '() is deprecated since Grav 1.6', E_USER_DEPRECATED); diff --git a/system/src/Grav/Common/User/DataUser/UserCollection.php b/system/src/Grav/Common/User/DataUser/UserCollection.php index 3da7e2d..d0ada09 100644 --- a/system/src/Grav/Common/User/DataUser/UserCollection.php +++ b/system/src/Grav/Common/User/DataUser/UserCollection.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\User * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -114,7 +114,7 @@ class UserCollection implements UserCollectionInterface if (!$user->exists()) { foreach ($files as $file) { if (Utils::endsWith($file, YAML_EXT)) { - $find_user = $this->load(trim(pathinfo($file, PATHINFO_FILENAME))); + $find_user = $this->load(trim(Utils::pathinfo($file, PATHINFO_FILENAME))); foreach ($fields as $field) { if (isset($find_user[$field]) && $find_user[$field] === $query) { return $find_user; diff --git a/system/src/Grav/Common/User/Group.php b/system/src/Grav/Common/User/Group.php index 5be5386..9be619a 100644 --- a/system/src/Grav/Common/User/Group.php +++ b/system/src/Grav/Common/User/Group.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\User * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -27,7 +27,7 @@ class Group extends Data * @return array * @deprecated 1.7, use $grav['user_groups'] Flex UserGroupCollection instead */ - private static function groups() + protected static function groups() { user_error(__METHOD__ . '() is deprecated since Grav 1.7, use $grav[\'user_groups\'] Flex UserGroupCollection instead', E_USER_DEPRECATED); diff --git a/system/src/Grav/Common/User/Interfaces/AuthorizeInterface.php b/system/src/Grav/Common/User/Interfaces/AuthorizeInterface.php index 3ad8d2c..1aeb004 100644 --- a/system/src/Grav/Common/User/Interfaces/AuthorizeInterface.php +++ b/system/src/Grav/Common/User/Interfaces/AuthorizeInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\User * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/User/Interfaces/UserCollectionInterface.php b/system/src/Grav/Common/User/Interfaces/UserCollectionInterface.php index d892e82..5bcfbc0 100644 --- a/system/src/Grav/Common/User/Interfaces/UserCollectionInterface.php +++ b/system/src/Grav/Common/User/Interfaces/UserCollectionInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\User * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/User/Interfaces/UserGroupInterface.php b/system/src/Grav/Common/User/Interfaces/UserGroupInterface.php index c345c4b..c18388c 100644 --- a/system/src/Grav/Common/User/Interfaces/UserGroupInterface.php +++ b/system/src/Grav/Common/User/Interfaces/UserGroupInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\User * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/User/Interfaces/UserInterface.php b/system/src/Grav/Common/User/Interfaces/UserInterface.php index 02827e3..dcd014c 100644 --- a/system/src/Grav/Common/User/Interfaces/UserInterface.php +++ b/system/src/Grav/Common/User/Interfaces/UserInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\User * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/User/Traits/UserTrait.php b/system/src/Grav/Common/User/Traits/UserTrait.php index 5a6b749..0608caf 100644 --- a/system/src/Grav/Common/User/Traits/UserTrait.php +++ b/system/src/Grav/Common/User/Traits/UserTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\User * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/User/User.php b/system/src/Grav/Common/User/User.php index 4b2319f..b38c72e 100644 --- a/system/src/Grav/Common/User/User.php +++ b/system/src/Grav/Common/User/User.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\User * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php index f843a0c..64c81a9 100644 --- a/system/src/Grav/Common/Utils.php +++ b/system/src/Grav/Common/Utils.php @@ -3,7 +3,7 @@ /** * @package Grav\Common * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -21,6 +21,7 @@ use Grav\Common\Page\Markdown\Excerpts; use Grav\Common\Page\Pages; use Grav\Framework\Flex\Flex; use Grav\Framework\Flex\Interfaces\FlexObjectInterface; +use Grav\Framework\Media\Interfaces\MediaInterface; use InvalidArgumentException; use Negotiation\Accept; use Negotiation\Negotiator; @@ -150,7 +151,7 @@ abstract class Utils $domain = $domain ?: $grav['config']->get('system.absolute_urls', false); - return rtrim($uri->rootUrl($domain), '/') . '/' . ($resource ?? ''); + return rtrim($uri->rootUrl($domain), '/') . '/' . ($resource ?: ''); } /** @@ -652,16 +653,17 @@ abstract class Utils * @param bool $force_download as opposed to letting browser choose if to download or render * @param int $sec Throttling, try 0.1 for some speed throttling of downloads * @param int $bytes Size of chunks to send in bytes. Default is 1024 + * @param array $options Extra options: [mime, download_name, expires] * @throws Exception */ - public static function download($file, $force_download = true, $sec = 0, $bytes = 1024) + public static function download($file, $force_download = true, $sec = 0, $bytes = 1024, array $options = []) { if (file_exists($file)) { // fire download event - Grav::instance()->fireEvent('onBeforeDownload', new Event(['file' => $file])); + Grav::instance()->fireEvent('onBeforeDownload', new Event(['file' => $file, 'options' => &$options])); - $file_parts = pathinfo($file); - $mimetype = static::getMimeByExtension($file_parts['extension']); + $file_parts = static::pathinfo($file); + $mimetype = $options['mime'] ?? static::getMimeByExtension($file_parts['extension']); $size = filesize($file); // File size // clean all buffers @@ -679,7 +681,7 @@ abstract class Utils if ($force_download) { // output the regular HTTP headers - header('Content-Disposition: attachment; filename="' . $file_parts['basename'] . '"'); + header('Content-Disposition: attachment; filename="' . ($options['download_name'] ?? $file_parts['basename']) . '"'); } // multipart-download and download resuming support @@ -703,7 +705,7 @@ abstract class Utils header('Content-Length: ' . $size); if (Grav::instance()['config']->get('system.cache.enabled')) { - $expires = Grav::instance()['config']->get('system.pages.expires'); + $expires = $options['expires'] ?? Grav::instance()['config']->get('system.pages.expires'); if ($expires > 0) { $expires_date = gmdate('D, d M Y H:i:s T', time() + $expires); header('Cache-Control: max-age=' . $expires); @@ -829,6 +831,31 @@ abstract class Utils return $mimetypes; } + /** + * Return all extensions for given mimetype. The first extension is the default one. + * + * @param string $mime Mime type (eg 'image/jpeg') + * @return string[] List of extensions eg. ['jpg', 'jpe', 'jpeg'] + */ + public static function getExtensionsByMime($mime) + { + $mime = strtolower($mime); + + $media_types = (array)Grav::instance()['config']->get('media.types'); + + $list = []; + foreach ($media_types as $extension => $type) { + if ($extension === '' || $extension === 'defaults') { + continue; + } + + if (isset($type['mime']) && $type['mime'] === $mime) { + $list[] = $extension; + } + } + + return $list; + } /** * Return the mimetype based on filename extension @@ -899,7 +926,7 @@ abstract class Utils */ public static function getMimeByFilename($filename, $default = 'application/octet-stream') { - return static::getMimeByExtension(pathinfo($filename, PATHINFO_EXTENSION), $default); + return static::getMimeByExtension(static::pathinfo($filename, PATHINFO_EXTENSION), $default); } /** @@ -944,7 +971,7 @@ abstract class Utils public static function checkFilename($filename) { $dangerous_extensions = Grav::instance()['config']->get('security.uploads_dangerous_extensions', []); - $extension = pathinfo($filename, PATHINFO_EXTENSION); + $extension = static::pathinfo($filename, PATHINFO_EXTENSION); return !( // Empty filenames are not allowed. @@ -958,6 +985,46 @@ abstract class Utils ); } + /** + * Unicode-safe version of PHP’s pathinfo() function. + * + * @link https://www.php.net/manual/en/function.pathinfo.php + * + * @param string $path + * @param int|null $flags + * @return array|string + */ + public static function pathinfo(string $path, int $flags = null) + { + $path = str_replace(['%2F', '%5C'], ['/', '\\'], rawurlencode($path)); + + if (null === $flags) { + $info = pathinfo($path); + } else { + $info = pathinfo($path, $flags); + } + + if (is_array($info)) { + return array_map('rawurldecode', $info); + } + + return rawurldecode($info); + } + + /** + * Unicode-safe version of the PHP basename() function. + * + * @link https://www.php.net/manual/en/function.basename.php + * + * @param string $path + * @param string $suffix + * @return string + */ + public static function basename(string $path, string $suffix = ''): string + { + return rawurldecode(basename(str_replace(['%2F', '%5C'], '/', rawurlencode($path)), $suffix)); + } + /** * Normalize path by processing relative `.` and `..` syntax and merging path * @@ -1566,7 +1633,7 @@ abstract class Utils switch ($matches[0]) { case 'self': - if (null === $object) { + if (!$object instanceof MediaInterface) { throw new RuntimeException(sprintf('Page not available for self@ reference: %s', $path)); } @@ -1593,8 +1660,8 @@ abstract class Utils $route = '/' . $matches[2]; // Exclude filename from the page lookup. - if (pathinfo($route, PATHINFO_EXTENSION)) { - $basename = '/' . basename($route); + if (static::pathinfo($route, PATHINFO_EXTENSION)) { + $basename = '/' . static::basename($route); $route = \dirname($route); } else { $basename = ''; @@ -1640,7 +1707,7 @@ abstract class Utils * @param string $path * @return string[]|null */ - private static function resolveTokenPath(string $path): ?array + protected static function resolveTokenPath(string $path): ?array { if (strpos($path, '@') !== false) { $regex = '/^(@\w+|\w+@|@\w+@)([^:]*)(.*)$/u'; @@ -1774,7 +1841,7 @@ abstract class Utils * * @param string $string * @param bool $block Block or Line processing - * @param null $page + * @param PageInterface|null $page * @return string * @throws Exception */ diff --git a/system/src/Grav/Common/Yaml.php b/system/src/Grav/Common/Yaml.php index 5adbd0c..0758599 100644 --- a/system/src/Grav/Common/Yaml.php +++ b/system/src/Grav/Common/Yaml.php @@ -3,7 +3,7 @@ /** * @package Grav\Common * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -17,8 +17,8 @@ use Grav\Framework\File\Formatter\YamlFormatter; */ abstract class Yaml { - /** @var YamlFormatter */ - private static $yaml; + /** @var YamlFormatter|null */ + protected static $yaml; /** * @param string $data @@ -51,7 +51,7 @@ abstract class Yaml /** * @return void */ - private static function init() + protected static function init() { $config = [ 'inline' => 5, diff --git a/system/src/Grav/Console/Application/Application.php b/system/src/Grav/Console/Application/Application.php index 21cea87..873b94e 100644 --- a/system/src/Grav/Console/Application/Application.php +++ b/system/src/Grav/Console/Application/Application.php @@ -3,18 +3,21 @@ /** * @package Grav\Console * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Console\Application; use Grav\Common\Grav; +use Symfony\Component\Console\ConsoleEvents; +use Symfony\Component\Console\Event\ConsoleCommandEvent; use Symfony\Component\Console\Formatter\OutputFormatterStyle; use Symfony\Component\Console\Input\InputDefinition; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; /** * Class GpmApplication @@ -29,19 +32,48 @@ class Application extends \Symfony\Component\Console\Application /** @var bool */ protected $initialized = false; + /** + * PluginApplication constructor. + * @param string $name + * @param string $version + */ + public function __construct(string $name = 'UNKNOWN', string $version = 'UNKNOWN') + { + parent::__construct($name, $version); + + // Add listener to prepare environment. + $dispatcher = new EventDispatcher(); + $dispatcher->addListener(ConsoleEvents::COMMAND, [$this, 'prepareEnvironment']); + + $this->setDispatcher($dispatcher); + } + /** * @param InputInterface $input * @return string|null */ public function getCommandName(InputInterface $input): ?string { - $this->environment = $input->getOption('env'); - $this->language = $input->getOption('lang') ?? $this->language; + if ($input->hasParameterOption('--env', true)) { + $this->environment = $input->getParameterOption('--env'); + } + if ($input->hasParameterOption('--lang', true)) { + $this->language = $input->getParameterOption('--lang'); + } + $this->init(); return parent::getCommandName($input); } + /** + * @param ConsoleCommandEvent $event + * @return void + */ + public function prepareEnvironment(ConsoleCommandEvent $event): void + { + } + /** * @return void */ @@ -58,7 +90,7 @@ class Application extends \Symfony\Component\Console\Application } /** - * Add global a --env option. + * Add global --env and --lang options. * * @return InputDefinition */ @@ -67,16 +99,16 @@ class Application extends \Symfony\Component\Console\Application $inputDefinition = parent::getDefaultInputDefinition(); $inputDefinition->addOption( new InputOption( - 'env', - null, + '--env', + '', InputOption::VALUE_OPTIONAL, 'Use environment configuration (defaults to localhost)' ) ); $inputDefinition->addOption( new InputOption( - 'lang', - null, + '--lang', + '', InputOption::VALUE_OPTIONAL, 'Language to be used (defaults to en)' ) diff --git a/system/src/Grav/Console/Application/CommandLoader/PluginCommandLoader.php b/system/src/Grav/Console/Application/CommandLoader/PluginCommandLoader.php index 9b7b568..adf6447 100644 --- a/system/src/Grav/Console/Application/CommandLoader/PluginCommandLoader.php +++ b/system/src/Grav/Console/Application/CommandLoader/PluginCommandLoader.php @@ -3,7 +3,7 @@ /** * @package Grav\Console * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Application/GpmApplication.php b/system/src/Grav/Console/Application/GpmApplication.php index df383f2..f9e6b61 100644 --- a/system/src/Grav/Console/Application/GpmApplication.php +++ b/system/src/Grav/Console/Application/GpmApplication.php @@ -3,7 +3,7 @@ /** * @package Grav\Console * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Application/GravApplication.php b/system/src/Grav/Console/Application/GravApplication.php index 739ca39..f06dae6 100644 --- a/system/src/Grav/Console/Application/GravApplication.php +++ b/system/src/Grav/Console/Application/GravApplication.php @@ -3,7 +3,7 @@ /** * @package Grav\Console * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Application/PluginApplication.php b/system/src/Grav/Console/Application/PluginApplication.php index 75e9eb7..e365b1e 100644 --- a/system/src/Grav/Console/Application/PluginApplication.php +++ b/system/src/Grav/Console/Application/PluginApplication.php @@ -3,7 +3,7 @@ /** * @package Grav\Console * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Cli/BackupCommand.php b/system/src/Grav/Console/Cli/BackupCommand.php index a8a025d..1fe84ea 100644 --- a/system/src/Grav/Console/Cli/BackupCommand.php +++ b/system/src/Grav/Console/Cli/BackupCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console\Cli * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Cli/CleanCommand.php b/system/src/Grav/Console/Cli/CleanCommand.php index 84b46d6..e6b8f98 100644 --- a/system/src/Grav/Console/Cli/CleanCommand.php +++ b/system/src/Grav/Console/Cli/CleanCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console\Cli * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Cli/ClearCacheCommand.php b/system/src/Grav/Console/Cli/ClearCacheCommand.php index daed2a5..7550dd9 100644 --- a/system/src/Grav/Console/Cli/ClearCacheCommand.php +++ b/system/src/Grav/Console/Cli/ClearCacheCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console\Cli * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Cli/ComposerCommand.php b/system/src/Grav/Console/Cli/ComposerCommand.php index 5075d1d..0dc9bcd 100644 --- a/system/src/Grav/Console/Cli/ComposerCommand.php +++ b/system/src/Grav/Console/Cli/ComposerCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console\Cli * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Cli/InstallCommand.php b/system/src/Grav/Console/Cli/InstallCommand.php index 22258be..33198cb 100644 --- a/system/src/Grav/Console/Cli/InstallCommand.php +++ b/system/src/Grav/Console/Cli/InstallCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console\Cli * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Cli/LogViewerCommand.php b/system/src/Grav/Console/Cli/LogViewerCommand.php index d3924f8..d7e127e 100644 --- a/system/src/Grav/Console/Cli/LogViewerCommand.php +++ b/system/src/Grav/Console/Cli/LogViewerCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console\Cli * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Cli/NewProjectCommand.php b/system/src/Grav/Console/Cli/NewProjectCommand.php index d67cb1c..7abbc4e 100644 --- a/system/src/Grav/Console/Cli/NewProjectCommand.php +++ b/system/src/Grav/Console/Cli/NewProjectCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console\Cli * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Cli/PageSystemValidatorCommand.php b/system/src/Grav/Console/Cli/PageSystemValidatorCommand.php index 4d234d7..3a69600 100644 --- a/system/src/Grav/Console/Cli/PageSystemValidatorCommand.php +++ b/system/src/Grav/Console/Cli/PageSystemValidatorCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console\Cli * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Cli/SandboxCommand.php b/system/src/Grav/Console/Cli/SandboxCommand.php index d865b4a..0ba2850 100644 --- a/system/src/Grav/Console/Cli/SandboxCommand.php +++ b/system/src/Grav/Console/Cli/SandboxCommand.php @@ -3,13 +3,14 @@ /** * @package Grav\Console\Cli * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Console\Cli; use Grav\Common\Filesystem\Folder; +use Grav\Common\Utils; use Grav\Console\GravCommand; use RuntimeException; use Symfony\Component\Console\Input\InputArgument; @@ -200,6 +201,10 @@ class SandboxCommand extends GravCommand $io->newLine(); $io->writeln('Resetting Symbolic Links'); + // Symlink also tests if using git. + if (is_dir($this->source . '/tests')) { + $this->mappings['/tests'] = '/tests'; + } foreach ($this->mappings as $source => $target) { if ((string)(int)$source === (string)$source) { @@ -297,7 +302,7 @@ class SandboxCommand extends GravCommand foreach ($binaries as $bin) { chmod($bin, $dir_perms); - $io->writeln(' bin/' . basename($bin) . ' permissions reset to ' . decoct($dir_perms)); + $io->writeln(' bin/' . Utils::basename($bin) . ' permissions reset to ' . decoct($dir_perms)); } $io->newLine(); diff --git a/system/src/Grav/Console/Cli/SchedulerCommand.php b/system/src/Grav/Console/Cli/SchedulerCommand.php index c5385aa..91fc57d 100644 --- a/system/src/Grav/Console/Cli/SchedulerCommand.php +++ b/system/src/Grav/Console/Cli/SchedulerCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console\Cli * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -77,8 +77,6 @@ class SchedulerCommand extends GravCommand $scheduler = $grav['scheduler']; $grav->fireEvent('onSchedulerInitialized', new Event(['scheduler' => $scheduler])); - $this->setHelp('foo'); - $input = $this->getInput(); $io = $this->getIO(); $error = 0; diff --git a/system/src/Grav/Console/Cli/SecurityCommand.php b/system/src/Grav/Console/Cli/SecurityCommand.php index 7f728b6..b6cb662 100644 --- a/system/src/Grav/Console/Cli/SecurityCommand.php +++ b/system/src/Grav/Console/Cli/SecurityCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console\Cli * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Cli/ServerCommand.php b/system/src/Grav/Console/Cli/ServerCommand.php index 77bce8b..29b423e 100644 --- a/system/src/Grav/Console/Cli/ServerCommand.php +++ b/system/src/Grav/Console/Cli/ServerCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console\Cli * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Cli/YamlLinterCommand.php b/system/src/Grav/Console/Cli/YamlLinterCommand.php index a9628cd..75a7559 100644 --- a/system/src/Grav/Console/Cli/YamlLinterCommand.php +++ b/system/src/Grav/Console/Cli/YamlLinterCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console\Cli * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/ConsoleCommand.php b/system/src/Grav/Console/ConsoleCommand.php index 044f1b0..c522c88 100644 --- a/system/src/Grav/Console/ConsoleCommand.php +++ b/system/src/Grav/Console/ConsoleCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/ConsoleTrait.php b/system/src/Grav/Console/ConsoleTrait.php index feb10d3..0af4075 100644 --- a/system/src/Grav/Console/ConsoleTrait.php +++ b/system/src/Grav/Console/ConsoleTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Console * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Gpm/DirectInstallCommand.php b/system/src/Grav/Console/Gpm/DirectInstallCommand.php index e5c662b..fbb0220 100644 --- a/system/src/Grav/Console/Gpm/DirectInstallCommand.php +++ b/system/src/Grav/Console/Gpm/DirectInstallCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console\Gpm * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Gpm/IndexCommand.php b/system/src/Grav/Console/Gpm/IndexCommand.php index 64d3597..367322b 100644 --- a/system/src/Grav/Console/Gpm/IndexCommand.php +++ b/system/src/Grav/Console/Gpm/IndexCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console\Gpm * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -200,7 +200,6 @@ class IndexCommand extends GpmCommand */ private function installed(Package $package): string { - $package = $list[$package->slug] ?? $package; $type = ucfirst(preg_replace('/s$/', '', $package->package_type)); $method = 'is' . $type . 'Installed'; $installed = $this->gpm->{$method}($package->slug); @@ -214,7 +213,6 @@ class IndexCommand extends GpmCommand */ private function enabled(Package $package): string { - $package = $list[$package->slug] ?? $package; $type = ucfirst(preg_replace('/s$/', '', $package->package_type)); $method = 'is' . $type . 'Installed'; $installed = $this->gpm->{$method}($package->slug); diff --git a/system/src/Grav/Console/Gpm/InfoCommand.php b/system/src/Grav/Console/Gpm/InfoCommand.php index 5dddd32..c6f8552 100644 --- a/system/src/Grav/Console/Gpm/InfoCommand.php +++ b/system/src/Grav/Console/Gpm/InfoCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console\Gpm * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Gpm/InstallCommand.php b/system/src/Grav/Console/Gpm/InstallCommand.php index 92c8e07..d25ebe3 100644 --- a/system/src/Grav/Console/Gpm/InstallCommand.php +++ b/system/src/Grav/Console/Gpm/InstallCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console\Gpm * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -579,7 +579,7 @@ class InstallCommand extends GpmCommand $tmp_dir = Grav::instance()['locator']->findResource('tmp://', true, true); $this->tmp = $tmp_dir . '/Grav-' . uniqid(); - $filename = $package->slug . basename($package->zipball_url); + $filename = $package->slug . Utils::basename($package->zipball_url); $filename = preg_replace('/[\\\\\/:"*?&<>|]+/m', '-', $filename); $query = ''; diff --git a/system/src/Grav/Console/Gpm/SelfupgradeCommand.php b/system/src/Grav/Console/Gpm/SelfupgradeCommand.php index b840016..a3d53ff 100644 --- a/system/src/Grav/Console/Gpm/SelfupgradeCommand.php +++ b/system/src/Grav/Console/Gpm/SelfupgradeCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console\Gpm * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Gpm/UninstallCommand.php b/system/src/Grav/Console/Gpm/UninstallCommand.php index cb14c65..a3db657 100644 --- a/system/src/Grav/Console/Gpm/UninstallCommand.php +++ b/system/src/Grav/Console/Gpm/UninstallCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console\Gpm * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Gpm/UpdateCommand.php b/system/src/Grav/Console/Gpm/UpdateCommand.php index 300fe61..73c5c9e 100644 --- a/system/src/Grav/Console/Gpm/UpdateCommand.php +++ b/system/src/Grav/Console/Gpm/UpdateCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console\Gpm * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Gpm/VersionCommand.php b/system/src/Grav/Console/Gpm/VersionCommand.php index 124aad3..8629196 100644 --- a/system/src/Grav/Console/Gpm/VersionCommand.php +++ b/system/src/Grav/Console/Gpm/VersionCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console\Gpm * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/GpmCommand.php b/system/src/Grav/Console/GpmCommand.php index 816e872..a14eb47 100644 --- a/system/src/Grav/Console/GpmCommand.php +++ b/system/src/Grav/Console/GpmCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/GravCommand.php b/system/src/Grav/Console/GravCommand.php index 0249f14..7db18e2 100644 --- a/system/src/Grav/Console/GravCommand.php +++ b/system/src/Grav/Console/GravCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/Plugin/PluginListCommand.php b/system/src/Grav/Console/Plugin/PluginListCommand.php index 81041c0..0226614 100644 --- a/system/src/Grav/Console/Plugin/PluginListCommand.php +++ b/system/src/Grav/Console/Plugin/PluginListCommand.php @@ -3,7 +3,7 @@ /** * @package Grav\Console\Plugin * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Console/TerminalObjects/Table.php b/system/src/Grav/Console/TerminalObjects/Table.php index a71d7c0..153993d 100644 --- a/system/src/Grav/Console/TerminalObjects/Table.php +++ b/system/src/Grav/Console/TerminalObjects/Table.php @@ -3,7 +3,7 @@ /** * @package Grav\Console\TerminalObjects * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Events/FlexRegisterEvent.php b/system/src/Grav/Events/FlexRegisterEvent.php index 13aebf3..b02ed1d 100644 --- a/system/src/Grav/Events/FlexRegisterEvent.php +++ b/system/src/Grav/Events/FlexRegisterEvent.php @@ -3,7 +3,7 @@ /** * @package Grav\Events * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Events/PermissionsRegisterEvent.php b/system/src/Grav/Events/PermissionsRegisterEvent.php index 5e63c85..434dcfa 100644 --- a/system/src/Grav/Events/PermissionsRegisterEvent.php +++ b/system/src/Grav/Events/PermissionsRegisterEvent.php @@ -3,7 +3,7 @@ /** * @package Grav\Events * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Events/PluginsLoadedEvent.php b/system/src/Grav/Events/PluginsLoadedEvent.php index 9dfd18b..13e2226 100644 --- a/system/src/Grav/Events/PluginsLoadedEvent.php +++ b/system/src/Grav/Events/PluginsLoadedEvent.php @@ -3,7 +3,7 @@ /** * @package Grav\Events * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Events/SessionStartEvent.php b/system/src/Grav/Events/SessionStartEvent.php index e724a09..fc4f364 100644 --- a/system/src/Grav/Events/SessionStartEvent.php +++ b/system/src/Grav/Events/SessionStartEvent.php @@ -3,7 +3,7 @@ /** * @package Grav\Events * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Acl/Access.php b/system/src/Grav/Framework/Acl/Access.php index ccc22cf..45cf2bf 100644 --- a/system/src/Grav/Framework/Acl/Access.php +++ b/system/src/Grav/Framework/Acl/Access.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Acl * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -14,7 +14,6 @@ use Countable; use Grav\Common\Utils; use IteratorAggregate; use JsonSerializable; -use RuntimeException; use Traversable; use function count; use function is_array; @@ -25,6 +24,7 @@ use function strlen; /** * Class Access * @package Grav\Framework\Acl + * @implements IteratorAggregate */ class Access implements JsonSerializable, IteratorAggregate, Countable { @@ -34,7 +34,7 @@ class Access implements JsonSerializable, IteratorAggregate, Countable private $rules; /** @var array */ private $ops; - /** @var array */ + /** @var array */ private $acl = []; /** @var array */ private $inherited = []; @@ -78,12 +78,7 @@ class Access implements JsonSerializable, IteratorAggregate, Countable $inherited = array_diff_key($parent->getAllActions(), $acl); $this->inherited += $parent->inherited + array_fill_keys(array_keys($inherited), $name ?? $parent->getName()); - $acl = array_replace($acl, $inherited); - if (null === $acl) { - throw new RuntimeException('Internal error'); - } - - $this->acl = $acl; + $this->acl = array_replace($acl, $inherited); } /** diff --git a/system/src/Grav/Framework/Acl/Action.php b/system/src/Grav/Framework/Acl/Action.php index 1f883a0..6251c3c 100644 --- a/system/src/Grav/Framework/Acl/Action.php +++ b/system/src/Grav/Framework/Acl/Action.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Acl * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -16,11 +16,11 @@ use IteratorAggregate; use RuntimeException; use Traversable; use function count; -use function strlen; /** * Class Action * @package Grav\Framework\Acl + * @implements IteratorAggregate */ class Action implements IteratorAggregate, Countable { @@ -37,7 +37,7 @@ class Action implements IteratorAggregate, Countable /** @var Action|null */ protected $parent; - /** @var Action[] */ + /** @var array */ protected $children = []; /** @@ -48,8 +48,8 @@ class Action implements IteratorAggregate, Countable { $label = $action['label'] ?? null; if (!$label) { - if ($pos = strrpos($name, '.')) { - $label = substr($name, $pos + 1); + if ($pos = mb_strrpos($name, '.')) { + $label = mb_substr($name, $pos + 1); } else { $label = $name; } @@ -114,9 +114,9 @@ class Action implements IteratorAggregate, Countable */ public function getScope(): string { - $pos = strpos($this->name, '.'); + $pos = mb_strpos($this->name, '.'); if ($pos) { - return substr($this->name, 0, $pos); + return mb_substr($this->name, 0, $pos); } return $this->name; @@ -127,7 +127,7 @@ class Action implements IteratorAggregate, Countable */ public function getLevels(): int { - return substr_count($this->name, '.'); + return mb_substr_count($this->name, '.'); } /** @@ -161,12 +161,12 @@ class Action implements IteratorAggregate, Countable */ public function addChild(Action $child): void { - if (strpos($child->name, "{$this->name}.") !== 0) { + if (mb_strpos($child->name, "{$this->name}.") !== 0) { throw new RuntimeException('Bad child'); } $child->setParent($this); - $name = substr($child->name, strlen($this->name) + 1); + $name = mb_substr($child->name, mb_strlen($this->name) + 1); $this->children[$name] = $child; } @@ -190,6 +190,7 @@ class Action implements IteratorAggregate, Countable /** * @return array */ + #[\ReturnTypeWillChange] public function __debugInfo() { return [ diff --git a/system/src/Grav/Framework/Acl/Permissions.php b/system/src/Grav/Framework/Acl/Permissions.php index 0375716..e54590a 100644 --- a/system/src/Grav/Framework/Acl/Permissions.php +++ b/system/src/Grav/Framework/Acl/Permissions.php @@ -3,26 +3,32 @@ /** * @package Grav\Framework\Acl * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Framework\Acl; +use ArrayAccess; use ArrayIterator; +use Countable; +use IteratorAggregate; use RecursiveIteratorIterator; use RuntimeException; use Traversable; +use function count; /** * Class Permissions * @package Grav\Framework\Acl + * @implements ArrayAccess + * @implements IteratorAggregate */ -class Permissions implements \ArrayAccess, \Countable, \IteratorAggregate +class Permissions implements ArrayAccess, Countable, IteratorAggregate { - /** @var Action[] */ + /** @var array */ protected $instances = []; - /** @var Action[] */ + /** @var array */ protected $actions = []; /** @var array */ protected $nested = []; @@ -142,9 +148,6 @@ class Permissions implements \ArrayAccess, \Countable, \IteratorAggregate public function addTypes(array $types): void { $types = array_replace($this->types, $types); - if (null === $types) { - throw new RuntimeException('Internal error'); - } $this->types = $types; } @@ -206,6 +209,7 @@ class Permissions implements \ArrayAccess, \Countable, \IteratorAggregate /** * @return ArrayIterator|Traversable */ + #[\ReturnTypeWillChange] public function getIterator() { return new ArrayIterator($this->actions); @@ -214,6 +218,7 @@ class Permissions implements \ArrayAccess, \Countable, \IteratorAggregate /** * @return array */ + #[\ReturnTypeWillChange] public function __debugInfo() { return [ diff --git a/system/src/Grav/Framework/Acl/PermissionsReader.php b/system/src/Grav/Framework/Acl/PermissionsReader.php index 2c38afb..350fc96 100644 --- a/system/src/Grav/Framework/Acl/PermissionsReader.php +++ b/system/src/Grav/Framework/Acl/PermissionsReader.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Acl * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -21,7 +21,7 @@ use function is_array; class PermissionsReader { /** @var array */ - private static $types; + protected static $types; /** * @param string $filename @@ -62,12 +62,14 @@ class PermissionsReader { $list = []; foreach ($actions as $name => $action) { - $prefixNname = $prefix . $name; - $list[$prefixNname] = null; + $prefixName = $prefix . $name; + $list[$prefixName] = null; // Support nested sets of actions. if (isset($action['actions']) && is_array($action['actions'])) { - $list += static::read($action['actions'], "{$prefixNname}."); + $innerList = static::read($action['actions'], "{$prefixName}."); + + $list += $innerList; } unset($action['actions']); @@ -76,7 +78,7 @@ class PermissionsReader $action = static::addDefaults($action); // Build flat list of actions. - $list[$prefixNname] = $action; + $list[$prefixName] = $action; } return $list; @@ -131,7 +133,7 @@ class PermissionsReader */ protected static function getDependencies(array $dependencies): array { - $list = []; + $list = [[]]; foreach ($dependencies as $name => $deps) { $current = $deps ? static::getDependencies($deps) : []; $current[] = $name; @@ -172,9 +174,6 @@ class PermissionsReader $scopes[] = $action; $action = array_replace_recursive(...$scopes); - if (null === $action) { - throw new RuntimeException('Internal error'); - } $newType = $defaults['type'] ?? null; if ($newType && $newType !== $type) { diff --git a/system/src/Grav/Framework/Acl/RecursiveActionIterator.php b/system/src/Grav/Framework/Acl/RecursiveActionIterator.php index ac219da..1fb9a60 100644 --- a/system/src/Grav/Framework/Acl/RecursiveActionIterator.php +++ b/system/src/Grav/Framework/Acl/RecursiveActionIterator.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Acl * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -17,6 +17,7 @@ use RocketTheme\Toolbox\ArrayTraits\Iterator; /** * Class Action * @package Grav\Framework\Acl + * @implements RecursiveIterator */ class RecursiveActionIterator implements RecursiveIterator, \Countable { @@ -26,6 +27,7 @@ class RecursiveActionIterator implements RecursiveIterator, \Countable * @see \Iterator::key() * @return string */ + #[\ReturnTypeWillChange] public function key() { /** @var Action $current */ diff --git a/system/src/Grav/Framework/Cache/AbstractCache.php b/system/src/Grav/Framework/Cache/AbstractCache.php index bfb5125..b6d00a3 100644 --- a/system/src/Grav/Framework/Cache/AbstractCache.php +++ b/system/src/Grav/Framework/Cache/AbstractCache.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Cache * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Cache/Adapter/ChainCache.php b/system/src/Grav/Framework/Cache/Adapter/ChainCache.php index 76f5185..bca940f 100644 --- a/system/src/Grav/Framework/Cache/Adapter/ChainCache.php +++ b/system/src/Grav/Framework/Cache/Adapter/ChainCache.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Cache * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -33,11 +33,15 @@ class ChainCache extends AbstractCache * Chain Cache constructor. * @param array $caches * @param null|int|DateInterval $defaultLifetime - * @throws \Psr\SimpleCache\InvalidArgumentException|InvalidArgumentException + * @throws InvalidArgumentException */ public function __construct(array $caches, $defaultLifetime = null) { - parent::__construct('', $defaultLifetime); + try { + parent::__construct('', $defaultLifetime); + } catch (\Psr\SimpleCache\InvalidArgumentException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } if (!$caches) { throw new InvalidArgumentException('At least one cache must be specified'); diff --git a/system/src/Grav/Framework/Cache/Adapter/DoctrineCache.php b/system/src/Grav/Framework/Cache/Adapter/DoctrineCache.php index 29e9e3b..15afed3 100644 --- a/system/src/Grav/Framework/Cache/Adapter/DoctrineCache.php +++ b/system/src/Grav/Framework/Cache/Adapter/DoctrineCache.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Cache * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -29,12 +29,16 @@ class DoctrineCache extends AbstractCache * @param CacheProvider $doctrineCache * @param string $namespace * @param null|int|DateInterval $defaultLifetime - * @throws \Psr\SimpleCache\InvalidArgumentException|InvalidArgumentException + * @throws InvalidArgumentException */ public function __construct(CacheProvider $doctrineCache, $namespace = '', $defaultLifetime = null) { // Do not use $namespace or $defaultLifetime directly, store them with constructor and fetch with methods. - parent::__construct($namespace, $defaultLifetime); + try { + parent::__construct($namespace, $defaultLifetime); + } catch (\Psr\SimpleCache\InvalidArgumentException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } // Set namespace to Doctrine Cache provider if it was given. $namespace = $this->getNamespace(); diff --git a/system/src/Grav/Framework/Cache/Adapter/FileCache.php b/system/src/Grav/Framework/Cache/Adapter/FileCache.php index 1995e15..7f1d5e7 100644 --- a/system/src/Grav/Framework/Cache/Adapter/FileCache.php +++ b/system/src/Grav/Framework/Cache/Adapter/FileCache.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Cache * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -42,9 +42,13 @@ class FileCache extends AbstractCache */ public function __construct($namespace = '', $defaultLifetime = null, $folder = null) { - parent::__construct($namespace, $defaultLifetime ?: 31557600); // = 1 year + try { + parent::__construct($namespace, $defaultLifetime ?: 31557600); // = 1 year - $this->initFileCache($namespace, $folder ?? ''); + $this->initFileCache($namespace, $folder ?? ''); + } catch (\Psr\SimpleCache\InvalidArgumentException $e) { + throw new InvalidArgumentException($e->getMessage(), $e->getCode(), $e); + } } /** @@ -103,7 +107,13 @@ class FileCache extends AbstractCache { $file = $this->getFile($key); - return (!file_exists($file) || @unlink($file) || !file_exists($file)); + $result = false; + if (file_exists($file)) { + $result = @unlink($file); + $result &= !file_exists($file); + } + + return $result; } /** @@ -156,7 +166,7 @@ class FileCache extends AbstractCache */ protected function initFileCache($namespace, $directory) { - if (!isset($directory[0])) { + if ($directory === '') { $directory = sys_get_temp_dir() . '/grav-cache'; } else { $directory = realpath($directory) ?: $directory; @@ -246,6 +256,7 @@ class FileCache extends AbstractCache /** * @return void */ + #[\ReturnTypeWillChange] public function __destruct() { if ($this->tmp !== null && file_exists($this->tmp)) { diff --git a/system/src/Grav/Framework/Cache/Adapter/MemoryCache.php b/system/src/Grav/Framework/Cache/Adapter/MemoryCache.php index c043bf9..21cd89c 100644 --- a/system/src/Grav/Framework/Cache/Adapter/MemoryCache.php +++ b/system/src/Grav/Framework/Cache/Adapter/MemoryCache.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Cache * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Cache/Adapter/SessionCache.php b/system/src/Grav/Framework/Cache/Adapter/SessionCache.php index e189e3e..02fe7c9 100644 --- a/system/src/Grav/Framework/Cache/Adapter/SessionCache.php +++ b/system/src/Grav/Framework/Cache/Adapter/SessionCache.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Cache * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Cache/CacheInterface.php b/system/src/Grav/Framework/Cache/CacheInterface.php index efd9d31..03dfa7a 100644 --- a/system/src/Grav/Framework/Cache/CacheInterface.php +++ b/system/src/Grav/Framework/Cache/CacheInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Cache * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Cache/CacheTrait.php b/system/src/Grav/Framework/Cache/CacheTrait.php index 287f924..37dd6c9 100644 --- a/system/src/Grav/Framework/Cache/CacheTrait.php +++ b/system/src/Grav/Framework/Cache/CacheTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Cache * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Cache/Exception/CacheException.php b/system/src/Grav/Framework/Cache/Exception/CacheException.php index 1db3256..db33883 100644 --- a/system/src/Grav/Framework/Cache/Exception/CacheException.php +++ b/system/src/Grav/Framework/Cache/Exception/CacheException.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Cache * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Cache/Exception/InvalidArgumentException.php b/system/src/Grav/Framework/Cache/Exception/InvalidArgumentException.php index 08a3435..70c0418 100644 --- a/system/src/Grav/Framework/Cache/Exception/InvalidArgumentException.php +++ b/system/src/Grav/Framework/Cache/Exception/InvalidArgumentException.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Cache * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Collection/AbstractFileCollection.php b/system/src/Grav/Framework/Collection/AbstractFileCollection.php index f892267..500aea8 100644 --- a/system/src/Grav/Framework/Collection/AbstractFileCollection.php +++ b/system/src/Grav/Framework/Collection/AbstractFileCollection.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Collection * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -25,9 +25,9 @@ use function array_slice; * * @package Grav\Framework\Collection * @template TKey of array-key - * @template T + * @template T of object * @extends AbstractLazyCollection - * @mplements FileCollectionInterface + * @implements FileCollectionInterface */ class AbstractFileCollection extends AbstractLazyCollection implements FileCollectionInterface { @@ -68,6 +68,7 @@ class AbstractFileCollection extends AbstractLazyCollection implements FileColle /** * @param Criteria $criteria * @return ArrayCollection + * @phpstan-return ArrayCollection * @todo Implement lazy matching */ public function matching(Criteria $criteria) @@ -93,6 +94,7 @@ class AbstractFileCollection extends AbstractLazyCollection implements FileColle foreach (array_reverse($orderings) as $field => $ordering) { $next = ClosureExpressionVisitor::sortByField($field, $ordering === Criteria::DESC ? -1 : 1, $next); } + /** @phpstan-ignore-next-line */ if (null === $next) { throw new RuntimeException('Criteria is missing orderings'); } @@ -162,6 +164,7 @@ class AbstractFileCollection extends AbstractLazyCollection implements FileColle * @param SeekableIterator $iterator * @param int $nestingLimit * @return array + * @phpstan-param SeekableIterator $iterator */ protected function doInitializeByIterator(SeekableIterator $iterator, $nestingLimit) { @@ -211,7 +214,6 @@ class AbstractFileCollection extends AbstractLazyCollection implements FileColle protected function doInitializeChildren(array $children, $nestingLimit) { $objects = []; - foreach ($children as $iterator) { $objects += $this->doInitializeByIterator($iterator, $nestingLimit); } diff --git a/system/src/Grav/Framework/Collection/AbstractIndexCollection.php b/system/src/Grav/Framework/Collection/AbstractIndexCollection.php index 6a8861e..02916d9 100644 --- a/system/src/Grav/Framework/Collection/AbstractIndexCollection.php +++ b/system/src/Grav/Framework/Collection/AbstractIndexCollection.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Collection * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -14,6 +14,7 @@ use Closure; use Grav\Framework\Compat\Serializable; use Grav\Framework\Flex\Interfaces\FlexObjectInterface; use InvalidArgumentException; +use Iterator; use function array_key_exists; use function array_slice; use function count; @@ -22,6 +23,7 @@ use function count; * Abstract Index Collection. * @template TKey of array-key * @template T + * @template C of CollectionInterface * @implements CollectionInterface */ abstract class AbstractIndexCollection implements CollectionInterface @@ -78,17 +80,17 @@ abstract class AbstractIndexCollection implements CollectionInterface /** * {@inheritDoc} */ + #[\ReturnTypeWillChange] public function key() { - /** @phpstan-var TKey $key */ - $key = (string)key($this->entries); - - return $key; + /** @phpstan-var TKey */ + return (string)key($this->entries); } /** * {@inheritDoc} */ + #[\ReturnTypeWillChange] public function next() { $value = next($this->entries); @@ -100,6 +102,7 @@ abstract class AbstractIndexCollection implements CollectionInterface /** * {@inheritDoc} */ + #[\ReturnTypeWillChange] public function current() { $value = current($this->entries); @@ -130,7 +133,7 @@ abstract class AbstractIndexCollection implements CollectionInterface { $key = $this->isAllowedElement($element) ? $this->getCurrentKey($element) : null; - if (!$key || !isset($this->entries[$key])) { + if (null !== $key || !isset($this->entries[$key])) { return false; } @@ -142,45 +145,64 @@ abstract class AbstractIndexCollection implements CollectionInterface /** * Required by interface ArrayAccess. * - * {@inheritDoc} + * @param string|int|null $offset + * @return bool + * @phpstan-param TKey|null $offset */ + #[\ReturnTypeWillChange] public function offsetExists($offset) { - return $this->containsKey($offset); + /** @phpstan-ignore-next-line phpstan bug? */ + return $offset !== null ? $this->containsKey($offset) : false; } /** * Required by interface ArrayAccess. * - * {@inheritDoc} + * @param string|int|null $offset + * @return mixed + * @phpstan-param TKey|null $offset */ + #[\ReturnTypeWillChange] public function offsetGet($offset) { - return $this->get($offset); + /** @phpstan-ignore-next-line phpstan bug? */ + return $offset !== null ? $this->get($offset) : null; } /** * Required by interface ArrayAccess. * - * {@inheritDoc} + * @param string|int|null $offset + * @param mixed $value + * @return void + * @phpstan-param TKey|null $offset */ + #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { if (null === $offset) { $this->add($value); + } else { + /** @phpstan-ignore-next-line phpstan bug? */ + $this->set($offset, $value); } - - $this->set($offset, $value); } /** * Required by interface ArrayAccess. * - * {@inheritDoc} + * @param string|int|null $offset + * @return void + * @phpstan-param TKey|null $offset */ + #[\ReturnTypeWillChange] public function offsetUnset($offset) { - return $this->remove($offset); + if ($offset !== null) { + /** @phpstan-ignore-next-line phpstan bug? */ + $this->remove($offset); + } } /** @@ -250,6 +272,7 @@ abstract class AbstractIndexCollection implements CollectionInterface /** * {@inheritDoc} */ + #[\ReturnTypeWillChange] public function count() { return count($this->entries); @@ -293,7 +316,9 @@ abstract class AbstractIndexCollection implements CollectionInterface * Required by interface IteratorAggregate. * * {@inheritDoc} + * @phpstan-return Iterator */ + #[\ReturnTypeWillChange] public function getIterator() { return new ArrayIterator($this->loadElements()); @@ -336,6 +361,7 @@ abstract class AbstractIndexCollection implements CollectionInterface * * @return string */ + #[\ReturnTypeWillChange] public function __toString() { return __CLASS__ . '@' . spl_object_hash($this); @@ -361,7 +387,7 @@ abstract class AbstractIndexCollection implements CollectionInterface * @param int $start * @param int|null $limit * @return static - * @phpstan-return static + * @phpstan-return static */ public function limit($start, $limit = null) { @@ -372,7 +398,7 @@ abstract class AbstractIndexCollection implements CollectionInterface * Reverse the order of the items. * * @return static - * @phpstan-return static + * @phpstan-return static */ public function reverse() { @@ -383,14 +409,14 @@ abstract class AbstractIndexCollection implements CollectionInterface * Shuffle items. * * @return static - * @phpstan-return static + * @phpstan-return static */ public function shuffle() { $keys = $this->getKeys(); shuffle($keys); - return $this->createFrom(array_replace(array_flip($keys), $this->entries) ?? []); + return $this->createFrom(array_replace(array_flip($keys), $this->entries)); } /** @@ -400,7 +426,7 @@ abstract class AbstractIndexCollection implements CollectionInterface * * @param array $keys * @return static - * @phpstan-return static + * @phpstan-return static */ public function select(array $keys) { @@ -419,7 +445,7 @@ abstract class AbstractIndexCollection implements CollectionInterface * * @param array $keys * @return static - * @phpstan-return static + * @phpstan-return static */ public function unselect(array $keys) { @@ -431,9 +457,11 @@ abstract class AbstractIndexCollection implements CollectionInterface * * @param int $size Size of each chunk. * @return array + * @phpstan-return array> */ public function chunk($size) { + /** @phpstan-var array> */ return $this->loadCollection($this->entries)->chunk($size); } @@ -461,6 +489,7 @@ abstract class AbstractIndexCollection implements CollectionInterface * * @return array */ + #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->loadCollection()->jsonSerialize(); @@ -474,7 +503,7 @@ abstract class AbstractIndexCollection implements CollectionInterface * * @param array $entries Elements. * @return static - * @phpstan-return static + * @phpstan-return static */ protected function createFrom(array $entries) { @@ -527,7 +556,7 @@ abstract class AbstractIndexCollection implements CollectionInterface /** * @param array|null $entries * @return CollectionInterface - * @phpstan-return T + * @phpstan-return C */ abstract protected function loadCollection(array $entries = null): CollectionInterface; diff --git a/system/src/Grav/Framework/Collection/AbstractLazyCollection.php b/system/src/Grav/Framework/Collection/AbstractLazyCollection.php index 9afaab1..b0a93f5 100644 --- a/system/src/Grav/Framework/Collection/AbstractLazyCollection.php +++ b/system/src/Grav/Framework/Collection/AbstractLazyCollection.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Collection * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -22,11 +22,15 @@ use Doctrine\Common\Collections\AbstractLazyCollection as BaseAbstractLazyCollec */ abstract class AbstractLazyCollection extends BaseAbstractLazyCollection implements CollectionInterface { - /** @var ArrayCollection The backed collection to use */ + /** + * @par ArrayCollection + * @phpstan-var ArrayCollection + */ protected $collection; /** * {@inheritDoc} + * @phpstan-return ArrayCollection */ public function reverse() { @@ -37,6 +41,7 @@ abstract class AbstractLazyCollection extends BaseAbstractLazyCollection impleme /** * {@inheritDoc} + * @phpstan-return ArrayCollection */ public function shuffle() { @@ -57,6 +62,8 @@ abstract class AbstractLazyCollection extends BaseAbstractLazyCollection impleme /** * {@inheritDoc} + * @phpstan-param array $keys + * @phpstan-return ArrayCollection */ public function select(array $keys) { @@ -67,6 +74,8 @@ abstract class AbstractLazyCollection extends BaseAbstractLazyCollection impleme /** * {@inheritDoc} + * @phpstan-param array $keys + * @phpstan-return ArrayCollection */ public function unselect(array $keys) { @@ -78,6 +87,7 @@ abstract class AbstractLazyCollection extends BaseAbstractLazyCollection impleme /** * @return array */ + #[\ReturnTypeWillChange] public function jsonSerialize() { $this->initialize(); diff --git a/system/src/Grav/Framework/Collection/ArrayCollection.php b/system/src/Grav/Framework/Collection/ArrayCollection.php index d76aa05..cad02f2 100644 --- a/system/src/Grav/Framework/Collection/ArrayCollection.php +++ b/system/src/Grav/Framework/Collection/ArrayCollection.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Collection * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -30,7 +30,10 @@ class ArrayCollection extends BaseArrayCollection implements CollectionInterface */ public function reverse() { - return $this->createFrom(array_reverse($this->toArray())); + $keys = array_reverse($this->toArray()); + + /** @phpstan-var static */ + return $this->createFrom($keys); } /** @@ -43,8 +46,10 @@ class ArrayCollection extends BaseArrayCollection implements CollectionInterface { $keys = $this->getKeys(); shuffle($keys); + $keys = array_replace(array_flip($keys), $this->toArray()); - return $this->createFrom(array_replace(array_flip($keys), $this->toArray()) ?? []); + /** @phpstan-var static */ + return $this->createFrom($keys); } /** @@ -52,9 +57,11 @@ class ArrayCollection extends BaseArrayCollection implements CollectionInterface * * @param int $size Size of each chunk. * @return array + * @phpstan-return array> */ public function chunk($size) { + /** @phpstan-var array> */ return array_chunk($this->toArray(), $size, true); } @@ -63,9 +70,9 @@ class ArrayCollection extends BaseArrayCollection implements CollectionInterface * * Collection is returned in the order of $keys given to the function. * - * @param array $keys + * @param array $keys * @return static - * @phpstan-param array $keys + * @phpstan-param TKey[] $keys * @phpstan-return static */ public function select(array $keys) @@ -77,6 +84,7 @@ class ArrayCollection extends BaseArrayCollection implements CollectionInterface } } + /** @phpstan-var static */ return $this->createFrom($list); } @@ -85,11 +93,15 @@ class ArrayCollection extends BaseArrayCollection implements CollectionInterface * * @param array $keys * @return static + * @phpstan-param TKey[] $keys * @phpstan-return static */ public function unselect(array $keys) { - return $this->select(array_diff($this->getKeys(), $keys)); + $list = array_diff($this->getKeys(), $keys); + + /** @phpstan-var static */ + return $this->select($list); } /** @@ -97,6 +109,7 @@ class ArrayCollection extends BaseArrayCollection implements CollectionInterface * * @return array */ + #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->toArray(); diff --git a/system/src/Grav/Framework/Collection/CollectionInterface.php b/system/src/Grav/Framework/Collection/CollectionInterface.php index 42414ce..e098dd6 100644 --- a/system/src/Grav/Framework/Collection/CollectionInterface.php +++ b/system/src/Grav/Framework/Collection/CollectionInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Collection * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -26,7 +26,7 @@ interface CollectionInterface extends Collection, JsonSerializable * Reverse the order of the items. * * @return CollectionInterface - * @phpstan-return CollectionInterface + * @phpstan-return static */ public function reverse(); @@ -34,7 +34,7 @@ interface CollectionInterface extends Collection, JsonSerializable * Shuffle items. * * @return CollectionInterface - * @phpstan-return CollectionInterface + * @phpstan-return static */ public function shuffle(); @@ -43,6 +43,7 @@ interface CollectionInterface extends Collection, JsonSerializable * * @param int $size Size of each chunk. * @return array + * @phpstan-return array> */ public function chunk($size); @@ -53,7 +54,7 @@ interface CollectionInterface extends Collection, JsonSerializable * * @param array $keys * @return CollectionInterface - * @phpstan-return CollectionInterface + * @phpstan-return static */ public function select(array $keys); @@ -62,7 +63,7 @@ interface CollectionInterface extends Collection, JsonSerializable * * @param array $keys * @return CollectionInterface - * @phpstan-return CollectionInterface + * @phpstan-return static */ public function unselect(array $keys); } diff --git a/system/src/Grav/Framework/Collection/FileCollection.php b/system/src/Grav/Framework/Collection/FileCollection.php index 59df221..bae0e7b 100644 --- a/system/src/Grav/Framework/Collection/FileCollection.php +++ b/system/src/Grav/Framework/Collection/FileCollection.php @@ -3,19 +3,19 @@ /** * @package Grav\Framework\Collection * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Framework\Collection; +use stdClass; + /** * Collection of objects stored into a filesystem. * * @package Grav\Framework\Collection - * @template TKey of array-key - * @template T - * @extends AbstractFileCollection + * @extends AbstractFileCollection */ class FileCollection extends AbstractFileCollection { diff --git a/system/src/Grav/Framework/Collection/FileCollectionInterface.php b/system/src/Grav/Framework/Collection/FileCollectionInterface.php index 45c446c..1b56da9 100644 --- a/system/src/Grav/Framework/Collection/FileCollectionInterface.php +++ b/system/src/Grav/Framework/Collection/FileCollectionInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Collection * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Compat/Serializable.php b/system/src/Grav/Framework/Compat/Serializable.php index 3c9bf6a..22e4648 100644 --- a/system/src/Grav/Framework/Compat/Serializable.php +++ b/system/src/Grav/Framework/Compat/Serializable.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Compat * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/ContentBlock/ContentBlock.php b/system/src/Grav/Framework/ContentBlock/ContentBlock.php index efcd5f3..7259df0 100644 --- a/system/src/Grav/Framework/ContentBlock/ContentBlock.php +++ b/system/src/Grav/Framework/ContentBlock/ContentBlock.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\ContentBlock * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -161,6 +161,7 @@ class ContentBlock implements ContentBlockInterface /** * @return string */ + #[\ReturnTypeWillChange] public function __toString() { try { diff --git a/system/src/Grav/Framework/ContentBlock/ContentBlockInterface.php b/system/src/Grav/Framework/ContentBlock/ContentBlockInterface.php index fb445a3..bb0e46f 100644 --- a/system/src/Grav/Framework/ContentBlock/ContentBlockInterface.php +++ b/system/src/Grav/Framework/ContentBlock/ContentBlockInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\ContentBlock * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/ContentBlock/HtmlBlock.php b/system/src/Grav/Framework/ContentBlock/HtmlBlock.php index 5c5eb43..69d417c 100644 --- a/system/src/Grav/Framework/ContentBlock/HtmlBlock.php +++ b/system/src/Grav/Framework/ContentBlock/HtmlBlock.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\ContentBlock * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -29,6 +29,8 @@ class HtmlBlock extends ContentBlock implements HtmlBlockInterface /** @var array */ protected $scripts = []; /** @var array */ + protected $links = []; + /** @var array */ protected $html = []; /** @@ -40,6 +42,7 @@ class HtmlBlock extends ContentBlock implements HtmlBlockInterface $this->sortAssets($assets['styles']); $this->sortAssets($assets['scripts']); + $this->sortAssets($assets['links']); $this->sortAssets($assets['html']); return $assets; @@ -73,6 +76,15 @@ class HtmlBlock extends ContentBlock implements HtmlBlockInterface return $this->getAssetsInLocation('scripts', $location); } + /** + * @param string $location + * @return array + */ + public function getLinks($location = 'head') + { + return $this->getAssetsInLocation('links', $location); + } + /** * @param string $location * @return array @@ -98,6 +110,9 @@ class HtmlBlock extends ContentBlock implements HtmlBlockInterface if ($this->scripts) { $array['scripts'] = $this->scripts; } + if ($this->links) { + $array['links'] = $this->links; + } if ($this->html) { $array['html'] = $this->html; } @@ -117,6 +132,7 @@ class HtmlBlock extends ContentBlock implements HtmlBlockInterface $this->frameworks = isset($serialized['frameworks']) ? (array) $serialized['frameworks'] : []; $this->styles = isset($serialized['styles']) ? (array) $serialized['styles'] : []; $this->scripts = isset($serialized['scripts']) ? (array) $serialized['scripts'] : []; + $this->links = isset($serialized['links']) ? (array) $serialized['links'] : []; $this->html = isset($serialized['html']) ? (array) $serialized['html'] : []; } @@ -199,11 +215,14 @@ class HtmlBlock extends ContentBlock implements HtmlBlockInterface $content = (string) $element['content']; $type = !empty($element['type']) ? (string) $element['type'] : 'text/css'; + unset($element['content'], $element['type']); + $this->styles[$location][md5($content) . sha1($content)] = [ ':type' => 'inline', ':priority' => (int) $priority, 'content' => $content, - 'type' => $type + 'type' => $type, + 'element' => $element ]; return true; @@ -229,18 +248,23 @@ class HtmlBlock extends ContentBlock implements HtmlBlockInterface $src = $element['src']; $type = !empty($element['type']) ? (string) $element['type'] : 'text/javascript'; - $defer = isset($element['defer']); - $async = isset($element['async']); + $loading = !empty($element['loading']) ? (string) $element['loading'] : null; + $defer = !empty($element['defer']); + $async = !empty($element['async']); $handle = !empty($element['handle']) ? (string) $element['handle'] : ''; + unset($element['src'], $element['type'], $element['loading'], $element['defer'], $element['async'], $element['handle']); + $this->scripts[$location][md5($src) . sha1($src)] = [ ':type' => 'file', ':priority' => (int) $priority, 'src' => $src, 'type' => $type, + 'loading' => $loading, 'defer' => $defer, 'async' => $async, - 'handle' => $handle + 'handle' => $handle, + 'element' => $element ]; return true; @@ -266,12 +290,83 @@ class HtmlBlock extends ContentBlock implements HtmlBlockInterface $content = (string) $element['content']; $type = !empty($element['type']) ? (string) $element['type'] : 'text/javascript'; + $loading = !empty($element['loading']) ? (string) $element['loading'] : null; + + unset($element['content'], $element['type'], $element['loading']); $this->scripts[$location][md5($content) . sha1($content)] = [ ':type' => 'inline', ':priority' => (int) $priority, 'content' => $content, - 'type' => $type + 'type' => $type, + 'loading' => $loading, + 'element' => $element + ]; + + return true; + } + + /** + * @param string|array $element + * @param int $priority + * @param string $location + * @return bool + */ + public function addModule($element, $priority = 0, $location = 'head') + { + if (!is_array($element)) { + $element = ['src' => (string) $element]; + } + + $element['type'] = 'module'; + + return $this->addScript($element, $priority, $location); + } + + /** + * @param string|array $element + * @param int $priority + * @param string $location + * @return bool + */ + public function addInlineModule($element, $priority = 0, $location = 'head') + { + if (!is_array($element)) { + $element = ['content' => (string) $element]; + } + + $element['type'] = 'module'; + + return $this->addInlineScript($element, $priority, $location); + } + + /** + * @param array $element + * @param int $priority + * @param string $location + * @return bool + */ + public function addLink($element, $priority = 0, $location = 'head') + { + if (!is_array($element) || empty($element['rel']) || empty($element['href'])) { + return false; + } + + if (!isset($this->links[$location])) { + $this->links[$location] = []; + } + + $rel = (string) $element['rel']; + $href = (string) $element['href']; + + unset($element['rel'], $element['href']); + + $this->links[$location][md5($href) . sha1($href)] = [ + ':type' => 'file', + ':priority' => (int) $priority, + 'href' => $href, + 'rel' => $rel, + 'element' => $element, ]; return true; @@ -309,6 +404,7 @@ class HtmlBlock extends ContentBlock implements HtmlBlockInterface 'frameworks' => $this->frameworks, 'styles' => $this->styles, 'scripts' => $this->scripts, + 'links' => $this->links, 'html' => $this->html ]; @@ -333,6 +429,14 @@ class HtmlBlock extends ContentBlock implements HtmlBlockInterface } } + foreach ($blockAssets['links'] as $location => $links) { + if (!isset($assets['links'][$location])) { + $assets['links'][$location] = $links; + } elseif ($links) { + $assets['links'][$location] += $links; + } + } + foreach ($blockAssets['html'] as $location => $htmls) { if (!isset($assets['html'][$location])) { $assets['html'][$location] = $htmls; @@ -391,7 +495,7 @@ class HtmlBlock extends ContentBlock implements HtmlBlockInterface */ protected function sortAssets(array &$array) { - foreach ($array as $location => &$items) { + foreach ($array as &$items) { $this->sortAssetsInLocation($items); } } diff --git a/system/src/Grav/Framework/ContentBlock/HtmlBlockInterface.php b/system/src/Grav/Framework/ContentBlock/HtmlBlockInterface.php index 616e4a2..b151d7b 100644 --- a/system/src/Grav/Framework/ContentBlock/HtmlBlockInterface.php +++ b/system/src/Grav/Framework/ContentBlock/HtmlBlockInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\ContentBlock * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -37,6 +37,13 @@ interface HtmlBlockInterface extends ContentBlockInterface */ public function getScripts($location = 'head'); + + /** + * @param string $location + * @return array + */ + public function getLinks($location = 'head'); + /** * @param string $location * @return array @@ -76,7 +83,6 @@ interface HtmlBlockInterface extends ContentBlockInterface */ public function addScript($element, $priority = 0, $location = 'head'); - /** * @param string|array $element * @param int $priority @@ -85,6 +91,35 @@ interface HtmlBlockInterface extends ContentBlockInterface */ public function addInlineScript($element, $priority = 0, $location = 'head'); + + /** + * Shortcut for writing addScript(['type' => 'module', 'src' => ...]). + * + * @param string|array $element + * @param int $priority + * @param string $location + * @return bool + */ + public function addModule($element, $priority = 0, $location = 'head'); + + /** + * Shortcut for writing addInlineScript(['type' => 'module', 'content' => ...]). + * + * @param string|array $element + * @param int $priority + * @param string $location + * @return bool + */ + public function addInlineModule($element, $priority = 0, $location = 'head'); + + /** + * @param array $element + * @param int $priority + * @param string $location + * @return bool + */ + public function addLink($element, $priority = 0, $location = 'head'); + /** * @param string $html * @param int $priority diff --git a/system/src/Grav/Framework/Controller/Traits/ControllerResponseTrait.php b/system/src/Grav/Framework/Controller/Traits/ControllerResponseTrait.php index 62ed3e1..24e1fc2 100644 --- a/system/src/Grav/Framework/Controller/Traits/ControllerResponseTrait.php +++ b/system/src/Grav/Framework/Controller/Traits/ControllerResponseTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Controller * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -12,12 +12,14 @@ declare(strict_types=1); namespace Grav\Framework\Controller\Traits; use Grav\Common\Config\Config; +use Grav\Common\Data\ValidationException; use Grav\Common\Debugger; use Grav\Common\Grav; use Grav\Common\Utils; use Grav\Framework\Psr7\Response; use Grav\Framework\RequestHandler\Exception\RequestException; use Grav\Framework\Route\Route; +use JsonSerializable; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\StreamInterface; @@ -95,7 +97,7 @@ trait ControllerResponseTrait $headers = $headers ?? []; $options = $options ?? ['force_download' => true]; - $file_parts = pathinfo($filename); + $file_parts = Utils::pathinfo($filename); if (!isset($headers['Content-Type'])) { $mimetype = Utils::getMimeByExtension($file_parts['extension']); @@ -138,9 +140,9 @@ trait ControllerResponseTrait $code = (int)$this->getConfig()->get('system.pages.redirect_default_code', 302); } + $ext = Utils::pathinfo($url, PATHINFO_EXTENSION); $accept = $this->getAccept(['application/json', 'text/html']); - - if ($accept === 'application/json') { + if ($ext === 'json' || $accept === 'application/json') { return $this->createJsonResponse(['code' => $code, 'status' => 'redirect', 'redirect' => $url]); } @@ -203,15 +205,23 @@ trait ControllerResponseTrait protected function getErrorJson(Throwable $e): array { $code = $this->getErrorCode($e instanceof RequestException ? $e->getHttpCode() : $e->getCode()); - $message = $e->getMessage(); + if ($e instanceof ValidationException) { + $message = $e->getMessage(); + } else { + $message = htmlspecialchars($e->getMessage(), ENT_QUOTES | ENT_HTML5, 'UTF-8'); + } + + $extra = $e instanceof JsonSerializable ? $e->jsonSerialize() : []; + $response = [ 'code' => $code, 'status' => 'error', 'message' => $message, + 'redirect' => null, 'error' => [ 'code' => $code, 'message' => $message - ] + ] + $extra ]; /** @var Debugger $debugger */ diff --git a/system/src/Grav/Framework/DI/Container.php b/system/src/Grav/Framework/DI/Container.php index 76434a3..dde0da3 100644 --- a/system/src/Grav/Framework/DI/Container.php +++ b/system/src/Grav/Framework/DI/Container.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\DI * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/File/AbstractFile.php b/system/src/Grav/Framework/File/AbstractFile.php index b82bb33..b216ea6 100644 --- a/system/src/Grav/Framework/File/AbstractFile.php +++ b/system/src/Grav/Framework/File/AbstractFile.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\File * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -55,6 +55,7 @@ class AbstractFile implements FileInterface /** * Unlock file when the object gets destroyed. */ + #[\ReturnTypeWillChange] public function __destruct() { if ($this->isLocked()) { @@ -65,6 +66,7 @@ class AbstractFile implements FileInterface /** * @return void */ + #[\ReturnTypeWillChange] public function __clone() { $this->handle = null; @@ -191,15 +193,16 @@ class AbstractFile implements FileInterface $this->handle = @fopen($this->filepath, 'cb+') ?: null; if (!$this->handle) { $error = error_get_last(); + $message = $error['message'] ?? 'Unknown error'; - throw new RuntimeException("Opening file for writing failed on error {$error['message']}"); + throw new RuntimeException("Opening file for writing failed on error {$message}"); } } $lock = $block ? LOCK_EX : LOCK_EX | LOCK_NB; // Some filesystems do not support file locks, only fail if another process holds the lock. - $this->locked = flock($this->handle, $lock, $wouldblock) || !$wouldblock; + $this->locked = flock($this->handle, $lock, $wouldBlock) || !$wouldBlock; return $this->locked; } diff --git a/system/src/Grav/Framework/File/CsvFile.php b/system/src/Grav/Framework/File/CsvFile.php index 999f88a..7eba91e 100644 --- a/system/src/Grav/Framework/File/CsvFile.php +++ b/system/src/Grav/Framework/File/CsvFile.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\File * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -28,4 +28,13 @@ class CsvFile extends DataFile { parent::__construct($filepath, $formatter); } + + /** + * @return array + */ + public function load(): array + { + /** @var array */ + return parent::load(); + } } diff --git a/system/src/Grav/Framework/File/DataFile.php b/system/src/Grav/Framework/File/DataFile.php index 114146f..189db53 100644 --- a/system/src/Grav/Framework/File/DataFile.php +++ b/system/src/Grav/Framework/File/DataFile.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\File * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/File/File.php b/system/src/Grav/Framework/File/File.php index 1d4055a..b1a1c4f 100644 --- a/system/src/Grav/Framework/File/File.php +++ b/system/src/Grav/Framework/File/File.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\File * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -20,15 +20,6 @@ use function is_string; */ class File extends AbstractFile { - /** - * {@inheritdoc} - * @see FileInterface::load() - */ - public function load() - { - return parent::load(); - } - /** * {@inheritdoc} * @see FileInterface::save() diff --git a/system/src/Grav/Framework/File/Formatter/AbstractFormatter.php b/system/src/Grav/Framework/File/Formatter/AbstractFormatter.php index f5ff3f2..6a6981b 100644 --- a/system/src/Grav/Framework/File/Formatter/AbstractFormatter.php +++ b/system/src/Grav/Framework/File/Formatter/AbstractFormatter.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\File\Formatter * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/File/Formatter/CsvFormatter.php b/system/src/Grav/Framework/File/Formatter/CsvFormatter.php index 9d0a9a8..37ab5bb 100644 --- a/system/src/Grav/Framework/File/Formatter/CsvFormatter.php +++ b/system/src/Grav/Framework/File/Formatter/CsvFormatter.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\File\Formatter * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -16,6 +16,7 @@ use Grav\Framework\File\Interfaces\FileFormatterInterface; use JsonSerializable; use RuntimeException; use stdClass; +use function count; use function is_array; use function is_object; use function is_scalar; diff --git a/system/src/Grav/Framework/File/Formatter/IniFormatter.php b/system/src/Grav/Framework/File/Formatter/IniFormatter.php index ec16959..ecf67d1 100644 --- a/system/src/Grav/Framework/File/Formatter/IniFormatter.php +++ b/system/src/Grav/Framework/File/Formatter/IniFormatter.php @@ -5,13 +5,14 @@ declare(strict_types=1); /** * @package Grav\Framework\File\Formatter * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Framework\File\Formatter; use Grav\Framework\File\Interfaces\FileFormatterInterface; +use RuntimeException; /** * Class IniFormatter @@ -59,7 +60,7 @@ class IniFormatter extends AbstractFormatter $decoded = @parse_ini_string($data); if ($decoded === false) { - throw new \RuntimeException('Decoding INI failed'); + throw new RuntimeException('Decoding INI failed'); } return $decoded; diff --git a/system/src/Grav/Framework/File/Formatter/JsonFormatter.php b/system/src/Grav/Framework/File/Formatter/JsonFormatter.php index 6b19690..2bd6c79 100644 --- a/system/src/Grav/Framework/File/Formatter/JsonFormatter.php +++ b/system/src/Grav/Framework/File/Formatter/JsonFormatter.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\File\Formatter * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -75,9 +75,11 @@ class JsonFormatter extends AbstractFormatter if (is_string($options)) { $list = preg_split('/[\s,|]+/', $options); $options = 0; - foreach ($list as $option) { - if (isset($this->encodeOptions[$option])) { - $options += $this->encodeOptions[$option]; + if ($list) { + foreach ($list as $option) { + if (isset($this->encodeOptions[$option])) { + $options += $this->encodeOptions[$option]; + } } } } else { @@ -100,9 +102,11 @@ class JsonFormatter extends AbstractFormatter if (is_string($options)) { $list = preg_split('/[\s,|]+/', $options); $options = 0; - foreach ($list as $option) { - if (isset($this->decodeOptions[$option])) { - $options += $this->decodeOptions[$option]; + if ($list) { + foreach ($list as $option) { + if (isset($this->decodeOptions[$option])) { + $options += $this->decodeOptions[$option]; + } } } } else { @@ -117,6 +121,7 @@ class JsonFormatter extends AbstractFormatter * Returns recursion depth used in decode() function. * * @return int + * @phpstan-return positive-int */ public function getDecodeDepth(): int { diff --git a/system/src/Grav/Framework/File/Formatter/MarkdownFormatter.php b/system/src/Grav/Framework/File/Formatter/MarkdownFormatter.php index 8a624df..e36276a 100644 --- a/system/src/Grav/Framework/File/Formatter/MarkdownFormatter.php +++ b/system/src/Grav/Framework/File/Formatter/MarkdownFormatter.php @@ -5,13 +5,14 @@ declare(strict_types=1); /** * @package Grav\Framework\File\Formatter * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Framework\File\Formatter; use Grav\Framework\File\Interfaces\FileFormatterInterface; +use RuntimeException; /** * Class MarkdownFormatter @@ -99,7 +100,7 @@ class MarkdownFormatter extends AbstractFormatter // Normalize line endings to Unix style. $encoded = preg_replace("/(\r\n|\r)/u", "\n", $encoded); if (null === $encoded) { - throw new \RuntimeException('Encoding markdown failed'); + throw new RuntimeException('Encoding markdown failed'); } return $encoded; @@ -126,7 +127,7 @@ class MarkdownFormatter extends AbstractFormatter // Normalize line endings to Unix style. $data = preg_replace("/(\r\n|\r)/u", "\n", $data); if (null === $data) { - throw new \RuntimeException('Decoding markdown failed'); + throw new RuntimeException('Decoding markdown failed'); } // Parse header. diff --git a/system/src/Grav/Framework/File/Formatter/SerializeFormatter.php b/system/src/Grav/Framework/File/Formatter/SerializeFormatter.php index f045d71..25aee81 100644 --- a/system/src/Grav/Framework/File/Formatter/SerializeFormatter.php +++ b/system/src/Grav/Framework/File/Formatter/SerializeFormatter.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\File\Formatter * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/File/Formatter/YamlFormatter.php b/system/src/Grav/Framework/File/Formatter/YamlFormatter.php index 32d4d29..31aff2c 100644 --- a/system/src/Grav/Framework/File/Formatter/YamlFormatter.php +++ b/system/src/Grav/Framework/File/Formatter/YamlFormatter.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\File\Formatter * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -107,7 +107,9 @@ class YamlFormatter extends AbstractFormatter $saved = @ini_get('yaml.decode_php'); @ini_set('yaml.decode_php', '0'); $decoded = @yaml_parse($data); - @ini_set('yaml.decode_php', $saved); + if ($saved !== false) { + @ini_set('yaml.decode_php', $saved); + } if ($decoded !== false) { return (array) $decoded; diff --git a/system/src/Grav/Framework/File/IniFile.php b/system/src/Grav/Framework/File/IniFile.php index 6cda609..0807ee3 100644 --- a/system/src/Grav/Framework/File/IniFile.php +++ b/system/src/Grav/Framework/File/IniFile.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\File * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -28,4 +28,13 @@ class IniFile extends DataFile { parent::__construct($filepath, $formatter); } + + /** + * @return array + */ + public function load(): array + { + /** @var array */ + return parent::load(); + } } diff --git a/system/src/Grav/Framework/File/Interfaces/FileFormatterInterface.php b/system/src/Grav/Framework/File/Interfaces/FileFormatterInterface.php index 49eda4f..78714af 100644 --- a/system/src/Grav/Framework/File/Interfaces/FileFormatterInterface.php +++ b/system/src/Grav/Framework/File/Interfaces/FileFormatterInterface.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\File * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/File/Interfaces/FileInterface.php b/system/src/Grav/Framework/File/Interfaces/FileInterface.php index 015b1ad..f2cc288 100644 --- a/system/src/Grav/Framework/File/Interfaces/FileInterface.php +++ b/system/src/Grav/Framework/File/Interfaces/FileInterface.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\File * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/File/JsonFile.php b/system/src/Grav/Framework/File/JsonFile.php index f3b0902..7751e84 100644 --- a/system/src/Grav/Framework/File/JsonFile.php +++ b/system/src/Grav/Framework/File/JsonFile.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\File * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/File/MarkdownFile.php b/system/src/Grav/Framework/File/MarkdownFile.php index 4ae3aa0..a79d5ce 100644 --- a/system/src/Grav/Framework/File/MarkdownFile.php +++ b/system/src/Grav/Framework/File/MarkdownFile.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\File * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -28,4 +28,13 @@ class MarkdownFile extends DataFile { parent::__construct($filepath, $formatter); } + + /** + * @return array + */ + public function load(): array + { + /** @var array */ + return parent::load(); + } } diff --git a/system/src/Grav/Framework/File/YamlFile.php b/system/src/Grav/Framework/File/YamlFile.php index 29224ce..c4300ad 100644 --- a/system/src/Grav/Framework/File/YamlFile.php +++ b/system/src/Grav/Framework/File/YamlFile.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\File * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -28,4 +28,13 @@ class YamlFile extends DataFile { parent::__construct($filepath, $formatter); } + + /** + * @return array + */ + public function load(): array + { + /** @var array */ + return parent::load(); + } } diff --git a/system/src/Grav/Framework/Filesystem/Filesystem.php b/system/src/Grav/Framework/Filesystem/Filesystem.php index 9b077d7..ddbbab0 100644 --- a/system/src/Grav/Framework/Filesystem/Filesystem.php +++ b/system/src/Grav/Framework/Filesystem/Filesystem.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Filesystem * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -15,6 +15,7 @@ use Grav\Framework\Filesystem\Interfaces\FilesystemInterface; use RuntimeException; use function count; use function dirname; +use function is_array; use function pathinfo; /** @@ -150,7 +151,10 @@ class Filesystem implements FilesystemInterface */ public function basename(string $path, ?string $suffix = null): string { - return $suffix ? basename($path, $suffix) : basename($path); + // Escape path. + $path = str_replace(['%2F', '%5C'], '/', rawurlencode($path)); + + return rawurldecode($suffix ? basename($path, $suffix) : basename($path)); } /** @@ -176,6 +180,7 @@ class Filesystem implements FilesystemInterface * @param string $path * @param int $levels * @return string + * @phpstan-param positive-int $levels */ public function pathname(string $path, int $levels = 1): string { @@ -204,6 +209,7 @@ class Filesystem implements FilesystemInterface * @param string $path * @param int $levels * @return array + * @phpstan-param positive-int $levels */ protected function dirnameInternal(?string $scheme, string $path, int $levels = 1): array { @@ -229,20 +235,30 @@ class Filesystem implements FilesystemInterface */ protected function pathinfoInternal(?string $scheme, string $path, ?int $options = null) { - if ($options) { - return pathinfo($path, $options); + $path = str_replace(['%2F', '%5C'], ['/', '\\'], rawurlencode($path)); + + if (null === $options) { + $info = pathinfo($path); + } else { + $info = pathinfo($path, $options); } - $info = pathinfo($path); + if (!is_array($info)) { + return rawurldecode($info); + } + + $info = array_map('rawurldecode', $info); if (null !== $scheme) { $info['scheme'] = $scheme; - $dirname = isset($info['dirname']) && $info['dirname'] !== '.' ? $info['dirname'] : null; - if (null !== $dirname) { + /** @phpstan-ignore-next-line because pathinfo('') doesn't have dirname */ + $dirname = $info['dirname'] ?? '.'; + + if ('' !== $dirname && '.' !== $dirname) { // In Windows dirname may be using backslashes, fix that. if (DIRECTORY_SEPARATOR !== '/') { - $dirname = str_replace('\\', '/', $dirname); + $dirname = str_replace(DIRECTORY_SEPARATOR, '/', $dirname); } $info['dirname'] = $scheme . '://' . $dirname; diff --git a/system/src/Grav/Framework/Filesystem/Interfaces/FilesystemInterface.php b/system/src/Grav/Framework/Filesystem/Interfaces/FilesystemInterface.php index 0e280a8..4006c8f 100644 --- a/system/src/Grav/Framework/Filesystem/Interfaces/FilesystemInterface.php +++ b/system/src/Grav/Framework/Filesystem/Interfaces/FilesystemInterface.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Filesystem * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -31,6 +31,7 @@ interface FilesystemInterface * @param int $levels The number of parent directories to go up (>= 1). * @return string Returns parent path. * @throws RuntimeException + * @phpstan-param positive-int $levels * @api */ public function parent(string $path, int $levels = 1): string; @@ -46,7 +47,7 @@ interface FilesystemInterface public function normalize(string $path): string; /** - * Returns filename component of path. + * Unicode-safe and stream-safe `\basename()` replacement. * * @param string $path A filename or path, does not need to exist as a file. * @param string|null $suffix If the filename ends in suffix this will also be cut off. @@ -56,7 +57,7 @@ interface FilesystemInterface public function basename(string $path, ?string $suffix = null): string; /** - * Stream-safe `\dirname()` replacement. + * Unicode-safe and stream-safe `\dirname()` replacement. * * @see http://php.net/manual/en/function.dirname.php * @@ -64,12 +65,13 @@ interface FilesystemInterface * @param int $levels The number of parent directories to go up (>= 1). * @return string Returns path to the directory. * @throws RuntimeException + * @phpstan-param positive-int $levels * @api */ public function dirname(string $path, int $levels = 1): string; /** - * Stream-safe `\pathinfo()` replacement. + * Unicode-safe and stream-safe `\pathinfo()` replacement. * * @see http://php.net/manual/en/function.pathinfo.php * diff --git a/system/src/Grav/Framework/Flex/Flex.php b/system/src/Grav/Framework/Flex/Flex.php index 8739282..4d662d1 100644 --- a/system/src/Grav/Framework/Flex/Flex.php +++ b/system/src/Grav/Framework/Flex/Flex.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -62,7 +62,7 @@ class Flex implements FlexInterface */ public function addDirectoryType(string $type, string $blueprint, array $config = []) { - $config = array_replace_recursive(['enabled' => true], $this->config ?? [], $config); + $config = array_replace_recursive(['enabled' => true], $this->config, $config); $this->types[$type] = new FlexDirectory($type, $blueprint, $config); @@ -123,6 +123,7 @@ class Flex implements FlexInterface * @param array|null $keys * @param string|null $keyField * @return FlexCollectionInterface|null + * @phpstan-return FlexCollectionInterface|null */ public function getCollection(string $type, array $keys = null, string $keyField = null): ?FlexCollectionInterface { @@ -137,11 +138,12 @@ class Flex implements FlexInterface * collection_class: Class to be used to create the collection. Defaults to ObjectCollection. * @return FlexCollectionInterface * @throws RuntimeException + * @phpstan-return FlexCollectionInterface */ public function getMixedCollection(array $keys, array $options = []): FlexCollectionInterface { $collectionClass = $options['collection_class'] ?? ObjectCollection::class; - if (!class_exists($collectionClass)) { + if (!is_a($collectionClass, FlexCollectionInterface::class, true)) { throw new RuntimeException(sprintf('Cannot create collection: Class %s does not exist', $collectionClass)); } @@ -231,7 +233,7 @@ class Flex implements FlexInterface // Use the original key ordering. if (!$guessed) { - $list = array_replace(array_fill_keys($keys, null), $list) ?? []; + $list = array_replace(array_fill_keys($keys, null), $list); } else { // We have mixed keys, we need to map flex keys back to storage keys. $results = []; @@ -256,7 +258,7 @@ class Flex implements FlexInterface } // Remove missing objects if not asked to keep them. - if (empty($option['keep_missing'])) { + if (empty($options['keep_missing'])) { $list = array_filter($list); } diff --git a/system/src/Grav/Framework/Flex/FlexCollection.php b/system/src/Grav/Framework/Flex/FlexCollection.php index 54e0665..186c60a 100644 --- a/system/src/Grav/Framework/Flex/FlexCollection.php +++ b/system/src/Grav/Framework/Flex/FlexCollection.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -19,6 +19,7 @@ use Grav\Common\User\Interfaces\UserInterface; use Grav\Common\Utils; use Grav\Framework\Cache\CacheInterface; use Grav\Framework\ContentBlock\HtmlBlock; +use Grav\Framework\Flex\Interfaces\FlexIndexInterface; use Grav\Framework\Flex\Interfaces\FlexObjectInterface; use Grav\Framework\Object\ObjectCollection; use Grav\Framework\Flex\Interfaces\FlexCollectionInterface; @@ -47,7 +48,7 @@ class FlexCollection extends ObjectCollection implements FlexCollectionInterface private $_flexDirectory; /** @var string */ - private $_keyField; + private $_keyField = 'storage_key'; /** * Get list of cached methods. @@ -124,6 +125,7 @@ class FlexCollection extends ObjectCollection implements FlexCollectionInterface */ public function getFlexFeatures(): array { + /** @var array $implements */ $implements = class_implements($this); $list = []; @@ -152,7 +154,11 @@ class FlexCollection extends ObjectCollection implements FlexCollectionInterface arsort($matching, SORT_NUMERIC); } - return $this->select(array_keys($matching)); + /** @var string[] $array */ + $array = array_keys($matching); + + /** @phpstan-var static */ + return $this->select($array); } /** @@ -163,7 +169,7 @@ class FlexCollection extends ObjectCollection implements FlexCollectionInterface { $criteria = Criteria::create()->orderBy($order); - /** @var FlexCollectionInterface $matching */ + /** @phpstan-var FlexCollectionInterface $matching */ $matching = $this->matching($criteria); return $matching; @@ -171,7 +177,8 @@ class FlexCollection extends ObjectCollection implements FlexCollectionInterface /** * @param array $filters - * @return FlexCollectionInterface|Collection + * @return static + * @phpstan-return static */ public function filterBy(array $filters) { @@ -182,6 +189,7 @@ class FlexCollection extends ObjectCollection implements FlexCollectionInterface $criteria->andWhere($expr->eq($key, $value)); } + /** @phpstan-var static */ return $this->matching($criteria); } @@ -336,6 +344,7 @@ class FlexCollection extends ObjectCollection implements FlexCollectionInterface */ public function getIndex() { + /** @phpstan-var FlexIndexInterface */ return $this->getFlexDirectory()->getIndex($this->getKeys(), $this->getKeyField()); } @@ -460,7 +469,7 @@ class FlexCollection extends ObjectCollection implements FlexCollectionInterface * @param string $key * @return array */ - public function getMetaData(string $key): array + public function getMetaData($key): array { $object = $this->get($key); @@ -481,7 +490,7 @@ class FlexCollection extends ObjectCollection implements FlexCollectionInterface */ public function getKeyField(): string { - return $this->_keyField ?? 'storage_key'; + return $this->_keyField; } /** @@ -496,13 +505,18 @@ class FlexCollection extends ObjectCollection implements FlexCollectionInterface $list = $this->call('isAuthorized', [$action, $scope, $user]); $list = array_filter($list); - return $this->select(array_keys($list)); + /** @var string[] $keys */ + $keys = array_keys($list); + + /** @phpstan-var static */ + return $this->select($keys); } /** * @param string $value * @param string $field - * @return T|null + * @return FlexObjectInterface|null + * @phpstan-return T|null */ public function find($value, $field = 'id') { @@ -520,6 +534,7 @@ class FlexCollection extends ObjectCollection implements FlexCollectionInterface /** * @return array */ + #[\ReturnTypeWillChange] public function jsonSerialize() { $elements = []; @@ -538,6 +553,7 @@ class FlexCollection extends ObjectCollection implements FlexCollectionInterface /** * @return array */ + #[\ReturnTypeWillChange] public function __debugInfo() { return [ diff --git a/system/src/Grav/Framework/Flex/FlexDirectory.php b/system/src/Grav/Framework/Flex/FlexDirectory.php index 29f1490..f96633b 100644 --- a/system/src/Grav/Framework/Flex/FlexDirectory.php +++ b/system/src/Grav/Framework/Flex/FlexDirectory.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -21,7 +21,7 @@ use Grav\Common\Utils; use Grav\Framework\Cache\Adapter\DoctrineCache; use Grav\Framework\Cache\Adapter\MemoryCache; use Grav\Framework\Cache\CacheInterface; -use Grav\Framework\Flex\Interfaces\FlexAuthorizeInterface; +use Grav\Framework\Filesystem\Filesystem; use Grav\Framework\Flex\Interfaces\FlexCollectionInterface; use Grav\Framework\Flex\Interfaces\FlexDirectoryInterface; use Grav\Framework\Flex\Interfaces\FlexFormInterface; @@ -45,7 +45,6 @@ use function is_callable; /** * Class FlexDirectory * @package Grav\Framework\Flex - * @template T */ class FlexDirectory implements FlexDirectoryInterface { @@ -57,9 +56,15 @@ class FlexDirectory implements FlexDirectoryInterface protected $blueprint_file; /** @var Blueprint[] */ protected $blueprints; - /** @var FlexIndexInterface[] */ + /** + * @var FlexIndexInterface[] + * @phpstan-var FlexIndexInterface[] + */ protected $indexes = []; - /** @var FlexCollectionInterface|null */ + /** + * @var FlexCollectionInterface|null + * @phpstan-var FlexCollectionInterface|null + */ protected $collection; /** @var bool */ protected $enabled; @@ -213,8 +218,17 @@ class FlexDirectory implements FlexDirectoryInterface /** @var UniformResourceLocator $locator */ $locator = $grav['locator']; - /** @var string $filename Filename is always string */ - $filename = $locator->findResource($this->getDirectoryConfigUri($name), true, true); + + $filename = $this->getDirectoryConfigUri($name); + if (file_exists($filename)) { + $filename = $locator->findResource($filename, true); + } else { + $filesystem = Filesystem::getInstance(); + $dirname = $filesystem->dirname($filename); + $basename = $filesystem->basename($filename); + $dirname = $locator->findResource($dirname, true) ?: $locator->findResource($dirname, true, true); + $filename = "{$dirname}/{$basename}"; + } $file = YamlFile::instance($filename); if (!empty($data)) { @@ -318,7 +332,7 @@ class FlexDirectory implements FlexDirectoryInterface * @param array|null $keys Array of keys. * @param string|null $keyField Field to be used as the key. * @return FlexCollectionInterface - * @phpstan-return FlexCollectionInterface + * @phpstan-return FlexCollectionInterface */ public function getCollection(array $keys = null, string $keyField = null): FlexCollectionInterface { @@ -345,6 +359,7 @@ class FlexDirectory implements FlexDirectoryInterface * @param array|null $keys Array of keys. * @param string|null $keyField Field to be used as the key. * @return FlexIndexInterface + * @phpstan-return FlexIndexInterface */ public function getIndex(array $keys = null, string $keyField = null): FlexIndexInterface { @@ -353,7 +368,7 @@ class FlexDirectory implements FlexDirectoryInterface $index = clone $index; if (null !== $keys) { - /** @var FlexIndexInterface $index */ + /** @var FlexIndexInterface $index */ $index = $index->select($keys); } @@ -487,8 +502,11 @@ class FlexDirectory implements FlexDirectoryInterface */ public function createObject(array $data, string $key = '', bool $validate = false): FlexObjectInterface { - /** @var string|FlexObjectInterface $className */ + /** @phpstan-var class-string $className */ $className = $this->objectClassName ?: $this->getObjectClass(); + if (!is_a($className, FlexObjectInterface::class, true)) { + throw new \RuntimeException('Bad object class: ' . $className); + } return new $className($data, $key, $this, $validate); } @@ -497,11 +515,15 @@ class FlexDirectory implements FlexDirectoryInterface * @param array $entries * @param string|null $keyField * @return FlexCollectionInterface + * @phpstan-return FlexCollectionInterface */ public function createCollection(array $entries, string $keyField = null): FlexCollectionInterface { - /** @var string|FlexCollectionInterface $className */ + /** phpstan-var class-string $className */ $className = $this->collectionClassName ?: $this->getCollectionClass(); + if (!is_a($className, FlexCollectionInterface::class, true)) { + throw new \RuntimeException('Bad collection class: ' . $className); + } return $className::createFromArray($entries, $this, $keyField); } @@ -510,11 +532,15 @@ class FlexDirectory implements FlexDirectoryInterface * @param array $entries * @param string|null $keyField * @return FlexIndexInterface + * @phpstan-return FlexIndexInterface */ public function createIndex(array $entries, string $keyField = null): FlexIndexInterface { - /** @var string|FlexIndexInterface $className */ + /** @phpstan-var class-string $className */ $className = $this->indexClassName ?: $this->getIndexClass(); + if (!is_a($className, FlexIndexInterface::class, true)) { + throw new \RuntimeException('Bad index class: ' . $className); + } return $className::createFromArray($entries, $this, $keyField); } @@ -560,6 +586,7 @@ class FlexDirectory implements FlexDirectoryInterface * @param array $entries * @param string|null $keyField * @return FlexCollectionInterface + * @phpstan-return FlexCollectionInterface */ public function loadCollection(array $entries, string $keyField = null): FlexCollectionInterface { @@ -889,12 +916,17 @@ class FlexDirectory implements FlexDirectoryInterface $className = $storage['class'] ?? SimpleStorage::class; $options = $storage['options'] ?? []; + if (!is_a($className, FlexStorageInterface::class, true)) { + throw new \RuntimeException('Bad storage class: ' . $className); + } + return new $className($options); } /** * @param string $keyField * @return FlexIndexInterface + * @phpstan-return FlexIndexInterface */ protected function loadIndex(string $keyField): FlexIndexInterface { @@ -924,7 +956,7 @@ class FlexDirectory implements FlexDirectoryInterface } if (!is_array($keys)) { - /** @var string|FlexIndexInterface $className */ + /** @phpstan-var class-string $className */ $className = $this->getIndexClass(); $keys = $className::loadEntriesFromStorage($storage); if (!$cache instanceof MemoryCache) { @@ -946,7 +978,7 @@ class FlexDirectory implements FlexDirectoryInterface // We need to do this in two steps as orderBy() calls loadIndex() again and we do not want infinite loop. $this->indexes['storage_key'] = $index = $this->createIndex($keys, 'storage_key'); if ($ordering) { - /** @var FlexCollectionInterface $collection */ + /** @var FlexCollectionInterface $collection */ $collection = $this->indexes['storage_key']->orderBy($ordering); $this->indexes['storage_key'] = $index = $collection->getIndex(); } @@ -1035,7 +1067,9 @@ class FlexDirectory implements FlexDirectoryInterface $newKey = $object->getStorageKey(); if ($oldKey !== $newKey) { - $object->triggerEvent('move'); + if (method_exists($object, 'triggerEvent')) { + $object->triggerEvent('move'); + } $storage->renameRow($oldKey, $newKey); // TODO: media support. } diff --git a/system/src/Grav/Framework/Flex/FlexDirectoryForm.php b/system/src/Grav/Framework/Flex/FlexDirectoryForm.php index a225967..709f418 100644 --- a/system/src/Grav/Framework/Flex/FlexDirectoryForm.php +++ b/system/src/Grav/Framework/Flex/FlexDirectoryForm.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -15,6 +15,7 @@ use Grav\Common\Data\Blueprint; use Grav\Common\Data\Data; use Grav\Common\Grav; use Grav\Common\Twig\Twig; +use Grav\Common\Utils; use Grav\Framework\Flex\Interfaces\FlexDirectoryFormInterface; use Grav\Framework\Flex\Interfaces\FlexFormInterface; use Grav\Framework\Form\Interfaces\FormFlashInterface; @@ -94,9 +95,14 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, JsonSerializable $uniqueId = md5($directory->getFlexType() . '-directory-' . $this->name); } $this->setUniqueId($uniqueId); + $this->setFlashLookupFolder($directory->getDirectoryBlueprint()->get('form/flash_folder') ?? 'tmp://forms/[SESSIONID]'); $this->form = $options['form'] ?? null; + if (Utils::isPositive($this->form['disabled'] ?? false)) { + $this->disable(); + } + $this->initialize(); } @@ -352,6 +358,7 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, JsonSerializable * @param string $name * @return mixed|null */ + #[\ReturnTypeWillChange] public function __get($name) { $method = "get{$name}"; @@ -369,6 +376,7 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, JsonSerializable * @param mixed $value * @return void */ + #[\ReturnTypeWillChange] public function __set($name, $value) { $method = "set{$name}"; @@ -381,6 +389,7 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, JsonSerializable * @param string $name * @return bool */ + #[\ReturnTypeWillChange] public function __isset($name) { $method = "get{$name}"; @@ -397,6 +406,7 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, JsonSerializable * @param string $name * @return void */ + #[\ReturnTypeWillChange] public function __unset($name) { } @@ -487,6 +497,7 @@ class FlexDirectoryForm implements FlexDirectoryFormInterface, JsonSerializable * Filter validated data. * * @param ArrayAccess|Data|null $data + * @phpstan-param ArrayAccess|Data|null $data */ protected function filterData($data = null): void { diff --git a/system/src/Grav/Framework/Flex/FlexForm.php b/system/src/Grav/Framework/Flex/FlexForm.php index 4fb8daf..a1263f6 100644 --- a/system/src/Grav/Framework/Flex/FlexForm.php +++ b/system/src/Grav/Framework/Flex/FlexForm.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -15,6 +15,7 @@ use Grav\Common\Data\Blueprint; use Grav\Common\Data\Data; use Grav\Common\Grav; use Grav\Common\Twig\Twig; +use Grav\Common\Utils; use Grav\Framework\Flex\Interfaces\FlexFormInterface; use Grav\Framework\Flex\Interfaces\FlexObjectFormInterface; use Grav\Framework\Flex\Interfaces\FlexObjectInterface; @@ -125,10 +126,15 @@ class FlexForm implements FlexObjectFormInterface, JsonSerializable $uniqueId = md5($uniqueId); } $this->setUniqueId($uniqueId); + $directory = $object->getFlexDirectory(); $this->setFlashLookupFolder($options['flash_folder'] ?? $directory->getBlueprint()->get('form/flash_folder') ?? 'tmp://forms/[SESSIONID]'); $this->form = $options['form'] ?? null; + if (Utils::isPositive($this->items['disabled'] ?? $this->form['disabled'] ?? false)) { + $this->disable(); + } + if (!empty($options['reset'])) { $this->getFlash()->delete(); } @@ -438,6 +444,7 @@ class FlexForm implements FlexObjectFormInterface, JsonSerializable * @param string $name * @return mixed|null */ + #[\ReturnTypeWillChange] public function __get($name) { $method = "get{$name}"; @@ -455,6 +462,7 @@ class FlexForm implements FlexObjectFormInterface, JsonSerializable * @param mixed $value * @return void */ + #[\ReturnTypeWillChange] public function __set($name, $value) { $method = "set{$name}"; @@ -467,6 +475,7 @@ class FlexForm implements FlexObjectFormInterface, JsonSerializable * @param string $name * @return bool */ + #[\ReturnTypeWillChange] public function __isset($name) { $method = "get{$name}"; @@ -483,6 +492,7 @@ class FlexForm implements FlexObjectFormInterface, JsonSerializable * @param string $name * @return void */ + #[\ReturnTypeWillChange] public function __unset($name) { } @@ -588,6 +598,7 @@ class FlexForm implements FlexObjectFormInterface, JsonSerializable * * @param ArrayAccess|Data|null $data * @return void + * @phpstan-param ArrayAccess|Data|null $data */ protected function filterData($data = null): void { diff --git a/system/src/Grav/Framework/Flex/FlexFormFlash.php b/system/src/Grav/Framework/Flex/FlexFormFlash.php index feb7a9e..3f52cdb 100644 --- a/system/src/Grav/Framework/Flex/FlexFormFlash.php +++ b/system/src/Grav/Framework/Flex/FlexFormFlash.php @@ -3,7 +3,7 @@ /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Flex/FlexIndex.php b/system/src/Grav/Framework/Flex/FlexIndex.php index 1df5c4d..54438af 100644 --- a/system/src/Grav/Framework/Flex/FlexIndex.php +++ b/system/src/Grav/Framework/Flex/FlexIndex.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -11,6 +11,7 @@ namespace Grav\Framework\Flex; use Exception; use Grav\Common\Debugger; +use Grav\Common\File\CompiledJsonFile; use Grav\Common\File\CompiledYamlFile; use Grav\Common\Grav; use Grav\Common\Inflector; @@ -35,18 +36,18 @@ use function in_array; * @package Grav\Framework\Flex * @template T of FlexObjectInterface * @template C of FlexCollectionInterface - * @extends ObjectIndex + * @extends ObjectIndex * @implements FlexIndexInterface * @mixin C */ -class FlexIndex extends ObjectIndex implements FlexCollectionInterface, FlexIndexInterface +class FlexIndex extends ObjectIndex implements FlexIndexInterface { const VERSION = 1; /** @var FlexDirectory|null */ private $_flexDirectory; /** @var string */ - private $_keyField; + private $_keyField = 'storage_key'; /** @var array */ private $_indexKeys; @@ -117,6 +118,14 @@ class FlexIndex extends ObjectIndex implements FlexCollectionInterface, FlexInde $this->setKeyField(null); } + /** + * @return string + */ + public function getKey() + { + return $this->_key ?: $this->getFlexType() . '@@' . spl_object_hash($this); + } + /** * {@inheritdoc} * @see FlexCommonInterface::hasFlexFeature() @@ -132,6 +141,7 @@ class FlexIndex extends ObjectIndex implements FlexCollectionInterface, FlexInde */ public function getFlexFeatures(): array { + /** @var array $implements */ $implements = class_implements($this->getFlexDirectory()->getCollectionClass()); $list = []; @@ -164,7 +174,6 @@ class FlexIndex extends ObjectIndex implements FlexCollectionInterface, FlexInde return $this->orderBy($orderings); } - /** * {@inheritdoc} * @see FlexCollectionInterface::filterBy() @@ -353,7 +362,7 @@ class FlexIndex extends ObjectIndex implements FlexCollectionInterface, FlexInde */ public function getKeyField(): string { - return $this->_keyField ?? 'storage_key'; + return $this->_keyField; } /** @@ -416,7 +425,7 @@ class FlexIndex extends ObjectIndex implements FlexCollectionInterface, FlexInde $previous = $search; } - return $this->createFrom(array_replace($previous ?? [], $this->getEntries()) ?? []); + return $this->createFrom(array_replace($previous ?? [], $this->getEntries())); } /** @@ -432,12 +441,13 @@ class FlexIndex extends ObjectIndex implements FlexCollectionInterface, FlexInde * @param array $arguments * @return mixed */ + #[\ReturnTypeWillChange] public function __call($name, $arguments) { /** @var Debugger $debugger */ $debugger = Grav::instance()['debugger']; - /** @var FlexCollection $className */ + /** @phpstan-var class-string $className */ $className = $this->getFlexDirectory()->getCollectionClass(); $cachedMethods = $className::getCachedMethods(); @@ -492,9 +502,13 @@ class FlexIndex extends ObjectIndex implements FlexCollectionInterface, FlexInde } } else { $collection = $this->loadCollection(); - $result = $collection->{$name}(...$arguments); - if (!isset($cachedMethods[$name])) { - $debugger->addMessage("Call '{$flexType}:{$name}()' isn't cached", 'debug'); + if (\is_callable([$collection, $name])) { + $result = $collection->{$name}(...$arguments); + if (!isset($cachedMethods[$name])) { + $debugger->addMessage("Call '{$flexType}:{$name}()' isn't cached", 'debug'); + } + } else { + $result = null; } } @@ -522,6 +536,7 @@ class FlexIndex extends ObjectIndex implements FlexCollectionInterface, FlexInde /** * @return array */ + #[\ReturnTypeWillChange] public function __debugInfo() { return [ @@ -607,9 +622,11 @@ class FlexIndex extends ObjectIndex implements FlexCollectionInterface, FlexInde * @param string $key * @param mixed $value * @return ObjectInterface|null + * @phpstan-return T|null */ protected function loadElement($key, $value): ?ObjectInterface { + /** @phpstan-var T[] $objects */ $objects = $this->getFlexDirectory()->loadObjects([$key => $value]); return $objects ? reset($objects): null; @@ -618,10 +635,14 @@ class FlexIndex extends ObjectIndex implements FlexCollectionInterface, FlexInde /** * @param array|null $entries * @return ObjectInterface[] + * @phpstan-return T[] */ protected function loadElements(array $entries = null): array { - return $this->getFlexDirectory()->loadObjects($entries ?? $this->getEntries()); + /** @phpstan-var T[] $objects */ + $objects = $this->getFlexDirectory()->loadObjects($entries ?? $this->getEntries()); + + return $objects; } /** @@ -824,7 +845,7 @@ class FlexIndex extends ObjectIndex implements FlexCollectionInterface, FlexInde /** * @param FlexStorageInterface $storage - * @return CompiledYamlFile|null + * @return CompiledYamlFile|CompiledJsonFile|null */ protected static function getIndexFile(FlexStorageInterface $storage) { diff --git a/system/src/Grav/Framework/Flex/FlexObject.php b/system/src/Grav/Framework/Flex/FlexObject.php index 945f5ad..afc656c 100644 --- a/system/src/Grav/Framework/Flex/FlexObject.php +++ b/system/src/Grav/Framework/Flex/FlexObject.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -12,6 +12,7 @@ namespace Grav\Framework\Flex; use ArrayAccess; use Exception; use Grav\Common\Data\Blueprint; +use Grav\Common\Data\Data; use Grav\Common\Debugger; use Grav\Common\Grav; use Grav\Common\Inflector; @@ -68,15 +69,13 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface private $_forms = []; /** @var Blueprint[] */ private $_blueprint = []; - /** @var array */ + /** @var array|null */ private $_meta; - /** @var array */ + /** @var array|null */ protected $_original; - /** @var array */ - protected $_changes; - /** @var string */ + /** @var string|null */ protected $storage_key; - /** @var int */ + /** @var int|null */ protected $storage_timestamp; /** @@ -164,6 +163,7 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface */ public function getFlexFeatures(): array { + /** @var array $implements */ $implements = class_implements($this); $list = []; @@ -454,13 +454,50 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface } /** - * Get any changes based on data sent to update + * Get diff array from the object. + * + * @return array + */ + public function getDiff(): array + { + $blueprint = $this->getBlueprint(); + + $flattenOriginal = $blueprint->flattenData($this->getOriginalData()); + $flattenElements = $blueprint->flattenData($this->getElements()); + $removedElements = array_diff_key($flattenOriginal, $flattenElements); + $diff = []; + + // Include all added or changed keys. + foreach ($flattenElements as $key => $value) { + $orig = $flattenOriginal[$key] ?? null; + if ($orig !== $value) { + $diff[$key] = ['old' => $orig, 'new' => $value]; + } + } + + // Include all removed keys. + foreach ($removedElements as $key => $value) { + $diff[$key] = ['old' => $value, 'new' => null]; + } + + return $diff; + } + + /** + * Get any changes from the object. * * @return array */ public function getChanges(): array { - return $this->_changes ?? []; + $diff = $this->getDiff(); + + $data = new Data(); + foreach ($diff as $key => $change) { + $data->set($key, $change['new']); + } + + return $data->toArray(); } /** @@ -620,6 +657,7 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface /** * @return array */ + #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->getElements(); @@ -641,14 +679,19 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface public function update(array $data, array $files = []) { if ($data) { + // Get currently stored data. + $elements = $this->getElements(); + + // Store original version of the object. + if ($this->_original === null) { + $this->_original = $elements; + } + $blueprint = $this->getBlueprint(); // Process updated data through the object filters. $this->filterElements($data); - // Get currently stored data. - $elements = $this->getElements(); - // Merge existing object to the test data to be validated. $test = $blueprint->mergeData($elements, $data); @@ -657,17 +700,14 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface $data = $blueprint->filter($data, true, true); // Finally update the object. - foreach ($blueprint->flattenData($data) as $key => $value) { + $flattenData = $blueprint->flattenData($data); + foreach ($flattenData as $key => $value) { if ($value === null) { $this->unsetNestedProperty($key); } else { $this->setNestedProperty($key, $value); } } - - // Store the changes - $this->_original = $this->getElements(); - $this->_changes = Utils::arrayDiffMultidimensional($this->_original, $elements); } if ($files && method_exists($this, 'setUpdatedMedia')) { @@ -737,7 +777,7 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface $value = reset($result); $meta = $value['__META'] ?? null; if ($meta) { - /** @var FlexIndex $indexClass */ + /** @phpstan-var class-string $indexClass */ $indexClass = $this->getFlexDirectory()->getIndexClass(); $indexClass::updateObjectMeta($meta, $value, $storage); $this->_meta = $meta; @@ -849,8 +889,8 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface public function getDefaultValue(string $name, string $separator = null) { $separator = $separator ?: '.'; - $path = explode($separator, $name) ?: []; - $offset = array_shift($path) ?? ''; + $path = explode($separator, $name); + $offset = array_shift($path); $current = $this->getDefaultValues(); @@ -912,6 +952,7 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface * * @return string */ + #[\ReturnTypeWillChange] public function __toString() { return $this->getFlexKey(); @@ -920,6 +961,7 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface /** * @return array */ + #[\ReturnTypeWillChange] public function __debugInfo() { return [ @@ -935,6 +977,7 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface /** * Clone object. */ + #[\ReturnTypeWillChange] public function __clone() { // Allows future compatibility as parent::__clone() works. diff --git a/system/src/Grav/Framework/Flex/Interfaces/FlexAuthorizeInterface.php b/system/src/Grav/Framework/Flex/Interfaces/FlexAuthorizeInterface.php index 102dbf4..c1025a4 100644 --- a/system/src/Grav/Framework/Flex/Interfaces/FlexAuthorizeInterface.php +++ b/system/src/Grav/Framework/Flex/Interfaces/FlexAuthorizeInterface.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Flex/Interfaces/FlexCollectionInterface.php b/system/src/Grav/Framework/Flex/Interfaces/FlexCollectionInterface.php index 16be3d5..d14b69f 100644 --- a/system/src/Grav/Framework/Flex/Interfaces/FlexCollectionInterface.php +++ b/system/src/Grav/Framework/Flex/Interfaces/FlexCollectionInterface.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -140,5 +140,5 @@ interface FlexCollectionInterface extends FlexCommonInterface, ObjectCollectionI * @param string $key Key. * @return array */ - public function getMetaData(string $key): array; + public function getMetaData($key): array; } diff --git a/system/src/Grav/Framework/Flex/Interfaces/FlexCommonInterface.php b/system/src/Grav/Framework/Flex/Interfaces/FlexCommonInterface.php index ed045e2..7c1975f 100644 --- a/system/src/Grav/Framework/Flex/Interfaces/FlexCommonInterface.php +++ b/system/src/Grav/Framework/Flex/Interfaces/FlexCommonInterface.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Flex/Interfaces/FlexDirectoryFormInterface.php b/system/src/Grav/Framework/Flex/Interfaces/FlexDirectoryFormInterface.php index df6f7d6..203c766 100644 --- a/system/src/Grav/Framework/Flex/Interfaces/FlexDirectoryFormInterface.php +++ b/system/src/Grav/Framework/Flex/Interfaces/FlexDirectoryFormInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Flex/Interfaces/FlexDirectoryInterface.php b/system/src/Grav/Framework/Flex/Interfaces/FlexDirectoryInterface.php index 07eab01..a77df95 100644 --- a/system/src/Grav/Framework/Flex/Interfaces/FlexDirectoryInterface.php +++ b/system/src/Grav/Framework/Flex/Interfaces/FlexDirectoryInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -105,6 +105,7 @@ interface FlexDirectoryInterface extends FlexAuthorizeInterface * @param array|null $keys Array of keys. * @param string|null $keyField Field to be used as the key. * @return FlexCollectionInterface + * @phpstan-return FlexCollectionInterface */ public function getCollection(array $keys = null, string $keyField = null): FlexCollectionInterface; @@ -116,6 +117,7 @@ interface FlexDirectoryInterface extends FlexAuthorizeInterface * @param array|null $keys Array of keys. * @param string|null $keyField Field to be used as the key. * @return FlexIndexInterface + * @phpstan-return FlexIndexInterface */ public function getIndex(array $keys = null, string $keyField = null): FlexIndexInterface; @@ -170,6 +172,7 @@ interface FlexDirectoryInterface extends FlexAuthorizeInterface * @param array $entries * @param string|null $keyField * @return FlexCollectionInterface + * @phpstan-return FlexCollectionInterface */ public function createCollection(array $entries, string $keyField = null): FlexCollectionInterface; @@ -177,6 +180,7 @@ interface FlexDirectoryInterface extends FlexAuthorizeInterface * @param array $entries * @param string|null $keyField * @return FlexIndexInterface + * @phpstan-return FlexIndexInterface */ public function createIndex(array $entries, string $keyField = null): FlexIndexInterface; @@ -199,6 +203,7 @@ interface FlexDirectoryInterface extends FlexAuthorizeInterface * @param array $entries * @param string|null $keyField * @return FlexCollectionInterface + * @phpstan-return FlexCollectionInterface */ public function loadCollection(array $entries, string $keyField = null): FlexCollectionInterface; diff --git a/system/src/Grav/Framework/Flex/Interfaces/FlexFormInterface.php b/system/src/Grav/Framework/Flex/Interfaces/FlexFormInterface.php index 32dab19..ba9996a 100644 --- a/system/src/Grav/Framework/Flex/Interfaces/FlexFormInterface.php +++ b/system/src/Grav/Framework/Flex/Interfaces/FlexFormInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -43,4 +43,9 @@ interface FlexFormInterface extends Serializable, FormInterface * @return Route|null Returns Route object or null if file uploads are not enabled. */ public function getFileDeleteAjaxRoute($field, $filename); + +// /** +// * @return FlexObjectInterface +// */ +// public function getObject(); } diff --git a/system/src/Grav/Framework/Flex/Interfaces/FlexIndexInterface.php b/system/src/Grav/Framework/Flex/Interfaces/FlexIndexInterface.php index 71dd727..d8f8ba3 100644 --- a/system/src/Grav/Framework/Flex/Interfaces/FlexIndexInterface.php +++ b/system/src/Grav/Framework/Flex/Interfaces/FlexIndexInterface.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Flex/Interfaces/FlexInterface.php b/system/src/Grav/Framework/Flex/Interfaces/FlexInterface.php index 3c5a103..f1376a5 100644 --- a/system/src/Grav/Framework/Flex/Interfaces/FlexInterface.php +++ b/system/src/Grav/Framework/Flex/Interfaces/FlexInterface.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -59,6 +59,7 @@ interface FlexInterface extends Countable * @param array|null $keys * @param string|null $keyField * @return FlexCollectionInterface|null + * @phpstan-return FlexCollectionInterface|null */ public function getCollection(string $type, array $keys = null, string $keyField = null): ?FlexCollectionInterface; @@ -68,6 +69,7 @@ interface FlexInterface extends Countable * collection_class: Class to be used to create the collection. Defaults to ObjectCollection. * @return FlexCollectionInterface * @throws RuntimeException + * @phpstan-return FlexCollectionInterface */ public function getMixedCollection(array $keys, array $options = []): FlexCollectionInterface; diff --git a/system/src/Grav/Framework/Flex/Interfaces/FlexObjectFormInterface.php b/system/src/Grav/Framework/Flex/Interfaces/FlexObjectFormInterface.php index 044ee61..190a963 100644 --- a/system/src/Grav/Framework/Flex/Interfaces/FlexObjectFormInterface.php +++ b/system/src/Grav/Framework/Flex/Interfaces/FlexObjectFormInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Flex/Interfaces/FlexObjectInterface.php b/system/src/Grav/Framework/Flex/Interfaces/FlexObjectInterface.php index a1e3e3b..d02bc8b 100644 --- a/system/src/Grav/Framework/Flex/Interfaces/FlexObjectInterface.php +++ b/system/src/Grav/Framework/Flex/Interfaces/FlexObjectInterface.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -23,6 +23,7 @@ use RuntimeException; /** * Defines Flex Objects. * + * @extends ArrayAccess * @used-by \Grav\Framework\Flex\FlexObject * @since 1.6 */ diff --git a/system/src/Grav/Framework/Flex/Interfaces/FlexStorageInterface.php b/system/src/Grav/Framework/Flex/Interfaces/FlexStorageInterface.php index 08c4335..4209924 100644 --- a/system/src/Grav/Framework/Flex/Interfaces/FlexStorageInterface.php +++ b/system/src/Grav/Framework/Flex/Interfaces/FlexStorageInterface.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Flex/Interfaces/FlexTranslateInterface.php b/system/src/Grav/Framework/Flex/Interfaces/FlexTranslateInterface.php index 28e4888..8df11fa 100644 --- a/system/src/Grav/Framework/Flex/Interfaces/FlexTranslateInterface.php +++ b/system/src/Grav/Framework/Flex/Interfaces/FlexTranslateInterface.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Flex/Pages/FlexPageCollection.php b/system/src/Grav/Framework/Flex/Pages/FlexPageCollection.php index 9482568..5901557 100644 --- a/system/src/Grav/Framework/Flex/Pages/FlexPageCollection.php +++ b/system/src/Grav/Framework/Flex/Pages/FlexPageCollection.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -13,6 +13,7 @@ namespace Grav\Framework\Flex\Pages; use Grav\Common\Page\Interfaces\PageInterface; use Grav\Framework\Flex\FlexCollection; +use Grav\Framework\Flex\Interfaces\FlexObjectInterface; use function array_search; use function assert; use function is_int; @@ -20,7 +21,7 @@ use function is_int; /** * Class FlexPageCollection * @package Grav\Plugin\FlexObjects\Types\FlexPages - * @template T of \Grav\Framework\Flex\Interfaces\FlexObjectInterface + * @template T of FlexObjectInterface * @extends FlexCollection */ class FlexPageCollection extends FlexCollection @@ -56,8 +57,10 @@ class FlexPageCollection extends FlexCollection */ public function withPublished(bool $bool = true) { + /** @var string[] $list */ $list = array_keys(array_filter($this->call('isPublished', [$bool]))); + /** @phpstan-var static */ return $this->select($list); } @@ -68,8 +71,10 @@ class FlexPageCollection extends FlexCollection */ public function withVisible(bool $bool = true) { + /** @var string[] $list */ $list = array_keys(array_filter($this->call('isVisible', [$bool]))); + /** @phpstan-var static */ return $this->select($list); } @@ -80,8 +85,10 @@ class FlexPageCollection extends FlexCollection */ public function withRoutable(bool $bool = true) { + /** @var string[] $list */ $list = array_keys(array_filter($this->call('isRoutable', [$bool]))); + /** @phpstan-var static */ return $this->select($list); } @@ -148,9 +155,10 @@ class FlexPageCollection extends FlexCollection public function adjacentSibling($path, $direction = 1) { $keys = $this->getKeys(); + $direction = (int)$direction; $pos = array_search($path, $keys, true); - if ($pos !== false) { + if (is_int($pos)) { $pos += $direction; if (isset($keys[$pos])) { return $this[$keys[$pos]]; @@ -170,7 +178,7 @@ class FlexPageCollection extends FlexCollection { $pos = array_search($path, $this->getKeys(), true); - return $pos !== false ? $pos : null; + return is_int($pos) ? $pos : null; } /** @@ -184,7 +192,6 @@ class FlexPageCollection extends FlexCollection $keys = $collection->getStorageKeys(); // Assign next free order. - /** @var FlexPageObject|null $last */ $last = null; $order = 0; foreach ($keys as $folder => $key) { @@ -196,8 +203,9 @@ class FlexPageCollection extends FlexCollection } } + /** @var FlexPageObject|null $last */ $last = $collection[$last]; - return sprintf('%d.', $last ? $last->value('order') + 1 : 1); + return sprintf('%d.', $last ? $last->getFormValue('order') + 1 : 1); } } diff --git a/system/src/Grav/Framework/Flex/Pages/FlexPageIndex.php b/system/src/Grav/Framework/Flex/Pages/FlexPageIndex.php index 904d1f6..5bb5517 100644 --- a/system/src/Grav/Framework/Flex/Pages/FlexPageIndex.php +++ b/system/src/Grav/Framework/Flex/Pages/FlexPageIndex.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Flex/Pages/FlexPageObject.php b/system/src/Grav/Framework/Flex/Pages/FlexPageObject.php index ebc26b4..e700424 100644 --- a/system/src/Grav/Framework/Flex/Pages/FlexPageObject.php +++ b/system/src/Grav/Framework/Flex/Pages/FlexPageObject.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -55,6 +55,7 @@ class FlexPageObject extends FlexObject implements PageInterface, FlexTranslateI /** * Clone page. */ + #[\ReturnTypeWillChange] public function __clone() { parent::__clone(); diff --git a/system/src/Grav/Framework/Flex/Pages/Traits/PageAuthorsTrait.php b/system/src/Grav/Framework/Flex/Pages/Traits/PageAuthorsTrait.php index 5d3e968..d0934e1 100644 --- a/system/src/Grav/Framework/Flex/Pages/Traits/PageAuthorsTrait.php +++ b/system/src/Grav/Framework/Flex/Pages/Traits/PageAuthorsTrait.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Flex/Pages/Traits/PageContentTrait.php b/system/src/Grav/Framework/Flex/Pages/Traits/PageContentTrait.php index 6aff732..43dfee1 100644 --- a/system/src/Grav/Framework/Flex/Pages/Traits/PageContentTrait.php +++ b/system/src/Grav/Framework/Flex/Pages/Traits/PageContentTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -75,13 +75,13 @@ trait PageContentTrait 'template' => 'template', ]; - /** @var object */ + /** @var object|null */ protected $header; - /** @var string */ + /** @var string|null */ protected $_summary; - /** @var string */ + /** @var string|null */ protected $_content; /** @@ -100,6 +100,7 @@ trait PageContentTrait /** * @inheritdoc + * @return Header */ public function header($var = null) { @@ -288,7 +289,7 @@ trait PageContentTrait 'process', $var, function ($value) { - $value = array_replace(Grav::instance()['config']->get('system.pages.process', []), is_array($value) ? $value : []) ?? []; + $value = array_replace(Grav::instance()['config']->get('system.pages.process', []), is_array($value) ? $value : []); foreach ($value as $process => $status) { $value[$process] = (bool)$status; } @@ -664,6 +665,7 @@ trait PageContentTrait */ protected function processContent($content): string { + $content = is_string($content) ? $content : ''; $grav = Grav::instance(); /** @var Config $config */ @@ -676,7 +678,6 @@ trait PageContentTrait $twig_first = $this->getNestedProperty('header.twig_first') ?? $config->get('system.pages.twig_first', false); $never_cache_twig = $this->getNestedProperty('header.never_cache_twig') ?? $config->get('system.pages.never_cache_twig', false); - $cached = null; if ($cache_enable) { $cache = $this->getCache('render'); $key = md5($this->getCacheKey() . '-content'); @@ -688,12 +689,10 @@ trait PageContentTrait if ($process_twig && $never_cache_twig) { $this->_content = $this->processTwig($this->_content); } - } else { - $cached = null; } } - if (!$cached) { + if (null === $this->_content) { $markdown_options = []; if ($process_markdown) { // Build markdown options. @@ -746,6 +745,7 @@ trait PageContentTrait } if ($process_twig) { + \assert(is_string($this->_content)); $this->_content = $this->processTwig($this->_content); } } @@ -755,6 +755,8 @@ trait PageContentTrait } } + \assert(is_string($this->_content)); + // Handle summary divider $delimiter = $config->get('site.summary.delimiter', '==='); $divider_pos = mb_strpos($this->_content, "

{$delimiter}

"); diff --git a/system/src/Grav/Framework/Flex/Pages/Traits/PageLegacyTrait.php b/system/src/Grav/Framework/Flex/Pages/Traits/PageLegacyTrait.php index 6453bcf..d753803 100644 --- a/system/src/Grav/Framework/Flex/Pages/Traits/PageLegacyTrait.php +++ b/system/src/Grav/Framework/Flex/Pages/Traits/PageLegacyTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -17,13 +17,12 @@ use Grav\Common\Page\Interfaces\PageInterface; use Grav\Common\Page\Pages; use Grav\Common\Utils; use Grav\Common\Yaml; -use Grav\Framework\Cache\CacheInterface; use Grav\Framework\File\Formatter\MarkdownFormatter; use Grav\Framework\File\Formatter\YamlFormatter; use Grav\Framework\Filesystem\Filesystem; -use Grav\Framework\Flex\FlexDirectory; use Grav\Framework\Flex\Interfaces\FlexCollectionInterface; use Grav\Framework\Flex\Interfaces\FlexIndexInterface; +use Grav\Framework\Flex\Pages\FlexPageCollection; use Grav\Framework\Flex\Pages\FlexPageIndex; use Grav\Framework\Flex\Pages\FlexPageObject; use InvalidArgumentException; @@ -300,7 +299,7 @@ trait PageLegacyTrait $parentStorageKey = ltrim($filesystem->dirname("/{$this->getMasterKey()}"), '/'); - /** @var FlexPageIndex $index */ + /** @var FlexPageIndex> $index */ $index = $this->getFlexDirectory()->getIndex(); if ($parent) { @@ -323,8 +322,9 @@ trait PageLegacyTrait if ($this instanceof FlexPageObject) { $key = trim($parentKey . '/' . $this->folder(), '/'); $key = preg_replace(static::PAGE_ORDER_PREFIX_REGEX, '', $key); + \assert(is_string($key)); } else { - $key = trim($parentKey . '/' . basename($this->getKey()), '/'); + $key = trim($parentKey . '/' . Utils::basename($this->getKey()), '/'); } if ($index->containsKey($key)) { @@ -336,7 +336,7 @@ trait PageLegacyTrait } while ($index->containsKey($test)); $key = $test; } - $folder = basename($key); + $folder = Utils::basename($key); // Get the folder name. $order = $this->getProperty('order'); @@ -539,7 +539,7 @@ trait PageLegacyTrait if ($language) { $language = '.' . $language; } - $format = '.' . ($this->getProperty('format') ?? pathinfo($this->name(), PATHINFO_EXTENSION)); + $format = '.' . ($this->getProperty('format') ?? Utils::pathinfo($this->name(), PATHINFO_EXTENSION)); return $language . $format; } diff --git a/system/src/Grav/Framework/Flex/Pages/Traits/PageRoutableTrait.php b/system/src/Grav/Framework/Flex/Pages/Traits/PageRoutableTrait.php index 90773cd..c4c8cd7 100644 --- a/system/src/Grav/Framework/Flex/Pages/Traits/PageRoutableTrait.php +++ b/system/src/Grav/Framework/Flex/Pages/Traits/PageRoutableTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -15,10 +15,10 @@ use Grav\Common\Page\Interfaces\PageCollectionInterface; use Grav\Common\Page\Interfaces\PageInterface; use Grav\Common\Page\Pages; use Grav\Common\Uri; +use Grav\Common\Utils; use Grav\Framework\Filesystem\Filesystem; use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator; use RuntimeException; -use function dirname; use function is_string; /** @@ -375,7 +375,7 @@ trait PageRoutableTrait $value = $this->getMasterKey() ?: $this->getKey(); } - return basename($value) ?: null; + return Utils::basename($value) ?: null; } ); } diff --git a/system/src/Grav/Framework/Flex/Pages/Traits/PageTranslateTrait.php b/system/src/Grav/Framework/Flex/Pages/Traits/PageTranslateTrait.php index 877fec1..4bec685 100644 --- a/system/src/Grav/Framework/Flex/Pages/Traits/PageTranslateTrait.php +++ b/system/src/Grav/Framework/Flex/Pages/Traits/PageTranslateTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -26,6 +26,14 @@ trait PageTranslateTrait /** @var PageInterface[] */ private $_translations = []; + /** + * @return bool + */ + public function translated(): bool + { + return (bool)$this->translatedLanguages(true); + } + /** * @param string|null $languageCode * @param bool|null $fallback diff --git a/system/src/Grav/Framework/Flex/Storage/AbstractFilesystemStorage.php b/system/src/Grav/Framework/Flex/Storage/AbstractFilesystemStorage.php index 1934f50..ee04c51 100644 --- a/system/src/Grav/Framework/Flex/Storage/AbstractFilesystemStorage.php +++ b/system/src/Grav/Framework/Flex/Storage/AbstractFilesystemStorage.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -127,6 +127,10 @@ abstract class AbstractFilesystemStorage implements FlexStorageInterface $formatterClassName = $formatter['class'] ?? JsonFormatter::class; $formatterOptions = $formatter['options'] ?? []; + if (!is_a($formatterClassName, FileFormatterInterface::class, true)) { + throw new \InvalidArgumentException('Bad Data Formatter'); + } + $this->dataFormatter = new $formatterClassName($formatterOptions); } diff --git a/system/src/Grav/Framework/Flex/Storage/FileStorage.php b/system/src/Grav/Framework/Flex/Storage/FileStorage.php index eabd658..1864439 100644 --- a/system/src/Grav/Framework/Flex/Storage/FileStorage.php +++ b/system/src/Grav/Framework/Flex/Storage/FileStorage.php @@ -5,13 +5,14 @@ declare(strict_types=1); /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Framework\Flex\Storage; use FilesystemIterator; +use Grav\Common\Utils; use Grav\Framework\Flex\Interfaces\FlexStorageInterface; use RuntimeException; use SplFileInfo; @@ -125,7 +126,7 @@ class FileStorage extends FolderStorage */ protected function getKeyFromPath(string $path): string { - return basename($path, $this->dataFormatter->getDefaultFileExtension()); + return Utils::basename($path, $this->dataFormatter->getDefaultFileExtension()); } /** diff --git a/system/src/Grav/Framework/Flex/Storage/FolderStorage.php b/system/src/Grav/Framework/Flex/Storage/FolderStorage.php index 5b4f9f3..600e989 100644 --- a/system/src/Grav/Framework/Flex/Storage/FolderStorage.php +++ b/system/src/Grav/Framework/Flex/Storage/FolderStorage.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -224,6 +224,7 @@ class FolderStorage extends AbstractFilesystemStorage * @param string $src * @param string $dst * @return bool + * @throws RuntimeException */ public function copyRow(string $src, string $dst): bool { @@ -247,6 +248,7 @@ class FolderStorage extends AbstractFilesystemStorage /** * {@inheritdoc} * @see FlexStorageInterface::renameRow() + * @throws RuntimeException */ public function renameRow(string $src, string $dst): bool { @@ -350,7 +352,7 @@ class FolderStorage extends AbstractFilesystemStorage */ protected function getKeyFromPath(string $path): string { - return basename($path); + return Utils::basename($path); } /** @@ -634,7 +636,7 @@ class FolderStorage extends AbstractFilesystemStorage $flags = FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::CURRENT_AS_FILEINFO | FilesystemIterator::SKIP_DOTS | FilesystemIterator::UNIX_PATHS; $iterator = new FilesystemIterator($path, $flags); - $list = []; + $list = [[]]; /** @var SplFileInfo $info */ foreach ($iterator as $filename => $info) { if (!$info->isDir() || strpos($info->getFilename(), '.') === 0) { @@ -644,11 +646,7 @@ class FolderStorage extends AbstractFilesystemStorage $list[] = $this->buildIndexFromFilesystem($filename); } - if (!$list) { - return []; - } - - return count($list) > 1 ? array_merge(...$list) : $list[0]; + return array_merge(...$list); } /** @@ -690,7 +688,7 @@ class FolderStorage extends AbstractFilesystemStorage $pattern .= '/{FILE}{EXT}'; } else { $filesystem = Filesystem::getInstance(true); - $this->dataFile = basename($pattern, $extension); + $this->dataFile = Utils::basename($pattern, $extension); $pattern = $filesystem->dirname($pattern) . '/{FILE}{EXT}'; } } diff --git a/system/src/Grav/Framework/Flex/Storage/SimpleStorage.php b/system/src/Grav/Framework/Flex/Storage/SimpleStorage.php index 6faac9d..73e2b06 100644 --- a/system/src/Grav/Framework/Flex/Storage/SimpleStorage.php +++ b/system/src/Grav/Framework/Flex/Storage/SimpleStorage.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -13,6 +13,7 @@ namespace Grav\Framework\Flex\Storage; use Grav\Common\Data\Data; use Grav\Common\Filesystem\Folder; +use Grav\Common\Utils; use Grav\Framework\Filesystem\Filesystem; use InvalidArgumentException; use LogicException; @@ -30,7 +31,7 @@ class SimpleStorage extends AbstractFilesystemStorage protected $dataFolder; /** @var string */ protected $dataPattern; - /** @var string */ + /** @var string|null */ protected $prefix; /** @var array|null */ protected $data; @@ -53,9 +54,9 @@ class SimpleStorage extends AbstractFilesystemStorage $filesystem = Filesystem::getInstance(true); $extension = $this->dataFormatter->getDefaultFileExtension(); - $pattern = basename($options['folder']); + $pattern = Utils::basename($options['folder']); - $this->dataPattern = basename($pattern, $extension) . $extension; + $this->dataPattern = Utils::basename($pattern, $extension) . $extension; $this->dataFolder = $filesystem->dirname($options['folder']); $this->keyField = $options['key'] ?? 'storage_key'; $this->keyLen = (int)($options['key_len'] ?? 32); @@ -432,7 +433,7 @@ class SimpleStorage extends AbstractFilesystemStorage */ protected function getKeyFromPath(string $path): string { - return basename($path); + return Utils::basename($path); } /** diff --git a/system/src/Grav/Framework/Flex/Traits/FlexAuthorizeTrait.php b/system/src/Grav/Framework/Flex/Traits/FlexAuthorizeTrait.php index 8cbd1d5..ef4c433 100644 --- a/system/src/Grav/Framework/Flex/Traits/FlexAuthorizeTrait.php +++ b/system/src/Grav/Framework/Flex/Traits/FlexAuthorizeTrait.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php b/system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php index dabfe17..ad437eb 100644 --- a/system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php +++ b/system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php @@ -5,7 +5,7 @@ namespace Grav\Framework\Flex\Traits; /** * @package Grav\Framework\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -44,7 +44,7 @@ trait FlexMediaTrait } /** @var array */ - protected $_uploads; + protected $_uploads = []; /** * @return string|null @@ -119,7 +119,7 @@ trait FlexMediaTrait // Load settings for the field. $schema = $this->getBlueprint()->schema(); - $settings = $field && is_object($schema) ? (array)$schema->getProperty($field) : null; + $settings = (array)$schema->getProperty($field); if (!is_array($settings)) { return null; } @@ -165,6 +165,9 @@ trait FlexMediaTrait return $settings + ['accept' => '*', 'limit' => 1000, 'self' => true]; } + /** + * @return array + */ protected function getMediaFields(): array { // Load settings for the field. @@ -286,6 +289,7 @@ trait FlexMediaTrait /** * @return array */ + #[\ReturnTypeWillChange] public function __debugInfo() { return parent::__debugInfo() + [ @@ -342,7 +346,7 @@ trait FlexMediaTrait } // Calculate path without the retina scaling factor. - $realpath = $filesystem->pathname($filepath) . str_replace(['@3x', '@2x'], '', basename($filepath)); + $realpath = $filesystem->pathname($filepath) . str_replace(['@3x', '@2x'], '', Utils::basename($filepath)); $list[$filename] = [$file, $settings]; @@ -397,11 +401,11 @@ trait FlexMediaTrait } /** - * @return array + * @return array */ protected function getUpdatedMedia(): array { - return $this->_uploads ?? []; + return $this->_uploads; } /** @@ -504,7 +508,7 @@ trait FlexMediaTrait user_error(__METHOD__ . '() is deprecated since Grav 1.7, use Media class that implements MediaUploadInterface instead', E_USER_DEPRECATED); // Check the file extension. - $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); + $extension = strtolower(Utils::pathinfo($filename, PATHINFO_EXTENSION)); $grav = Grav::instance(); diff --git a/system/src/Grav/Framework/Flex/Traits/FlexRelatedDirectoryTrait.php b/system/src/Grav/Framework/Flex/Traits/FlexRelatedDirectoryTrait.php index 9f7380c..de98359 100644 --- a/system/src/Grav/Framework/Flex/Traits/FlexRelatedDirectoryTrait.php +++ b/system/src/Grav/Framework/Flex/Traits/FlexRelatedDirectoryTrait.php @@ -5,15 +5,15 @@ declare(strict_types=1); /** * @package Grav\Common\Flex * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Framework\Flex\Traits; -use Grav\Framework\Flex\FlexCollection; use Grav\Framework\Flex\FlexDirectory; use Grav\Framework\Flex\Interfaces\FlexCollectionInterface; +use Grav\Framework\Flex\Interfaces\FlexObjectInterface; use RuntimeException; use function in_array; @@ -26,7 +26,7 @@ trait FlexRelatedDirectoryTrait /** * @param string $type * @param string $property - * @return FlexCollectionInterface + * @return FlexCollectionInterface */ protected function getCollectionByProperty($type, $property) { @@ -34,9 +34,9 @@ trait FlexRelatedDirectoryTrait $collection = $directory->getCollection(); $list = $this->getNestedProperty($property) ?: []; - /** @var FlexCollection $collection */ + /** @var FlexCollectionInterface $collection */ $collection = $collection->filter(static function ($object) use ($list) { - return in_array($object->id, $list, true); + return in_array($object->getKey(), $list, true); }); return $collection; diff --git a/system/src/Grav/Framework/Form/FormFlash.php b/system/src/Grav/Framework/Form/FormFlash.php index 864e992..863eb4a 100644 --- a/system/src/Grav/Framework/Form/FormFlash.php +++ b/system/src/Grav/Framework/Form/FormFlash.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Form * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -343,7 +343,7 @@ class FormFlash implements FormFlashInterface // Prepare upload data for later save $data = [ - 'name' => basename($filename), + 'name' => Utils::basename($filename), 'type' => Utils::getMimeByLocalFile($filename), 'size' => filesize($filename), ]; diff --git a/system/src/Grav/Framework/Form/FormFlashFile.php b/system/src/Grav/Framework/Form/FormFlashFile.php index dc510e2..6c99599 100644 --- a/system/src/Grav/Framework/Form/FormFlashFile.php +++ b/system/src/Grav/Framework/Form/FormFlashFile.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Form * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -11,6 +11,7 @@ namespace Grav\Framework\Form; use Grav\Framework\Psr7\Stream; use InvalidArgumentException; +use JsonSerializable; use Psr\Http\Message\StreamInterface; use Psr\Http\Message\UploadedFileInterface; use RuntimeException; @@ -23,7 +24,7 @@ use function sprintf; * Class FormFlashFile * @package Grav\Framework\Form */ -class FormFlashFile implements UploadedFileInterface, \JsonSerializable +class FormFlashFile implements UploadedFileInterface, JsonSerializable { /** @var string */ private $field; @@ -175,6 +176,7 @@ class FormFlashFile implements UploadedFileInterface, \JsonSerializable /** * @return array */ + #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->upload; @@ -199,6 +201,7 @@ class FormFlashFile implements UploadedFileInterface, \JsonSerializable /** * @return array */ + #[\ReturnTypeWillChange] public function __debugInfo() { return [ diff --git a/system/src/Grav/Framework/Form/Interfaces/FormFactoryInterface.php b/system/src/Grav/Framework/Form/Interfaces/FormFactoryInterface.php index d21d4aa..6a85f66 100644 --- a/system/src/Grav/Framework/Form/Interfaces/FormFactoryInterface.php +++ b/system/src/Grav/Framework/Form/Interfaces/FormFactoryInterface.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Form * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Form/Interfaces/FormFlashInterface.php b/system/src/Grav/Framework/Form/Interfaces/FormFlashInterface.php index d51bd8d..44bee02 100644 --- a/system/src/Grav/Framework/Form/Interfaces/FormFlashInterface.php +++ b/system/src/Grav/Framework/Form/Interfaces/FormFlashInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Form * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Form/Interfaces/FormInterface.php b/system/src/Grav/Framework/Form/Interfaces/FormInterface.php index 9815646..0edfc42 100644 --- a/system/src/Grav/Framework/Form/Interfaces/FormInterface.php +++ b/system/src/Grav/Framework/Form/Interfaces/FormInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Form * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Form/Traits/FormTrait.php b/system/src/Grav/Framework/Form/Traits/FormTrait.php index 8dfed19..f4be9e7 100644 --- a/system/src/Grav/Framework/Form/Traits/FormTrait.php +++ b/system/src/Grav/Framework/Form/Traits/FormTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Form * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -57,16 +57,18 @@ trait FormTrait private $name; /** @var string */ private $id; + /** @var bool */ + private $enabled = true; /** @var string */ private $uniqueid; /** @var string */ private $sessionid; /** @var bool */ private $submitted; - /** @var ArrayAccess|Data|null */ + /** @var ArrayAccess|Data|null */ private $data; - /** @var array|UploadedFileInterface[] */ - private $files; + /** @var UploadedFileInterface[] */ + private $files = []; /** @var FormFlashInterface|null */ private $flash; /** @var string */ @@ -90,6 +92,30 @@ trait FormTrait $this->id = $id; } + /** + * @return void + */ + public function disable(): void + { + $this->enabled = false; + } + + /** + * @return void + */ + public function enable(): void + { + $this->enabled = true; + } + + /** + * @return bool + */ + public function isEnabled(): bool + { + return $this->enabled; + } + /** * @return string */ @@ -177,7 +203,7 @@ trait FormTrait */ public function getFiles(): array { - return $this->files ?? []; + return $this->files; } /** @@ -195,8 +221,8 @@ trait FormTrait */ public function getDefaultValue(string $name) { - $path = explode('.', $name) ?: []; - $offset = array_shift($path) ?? ''; + $path = explode('.', $name); + $offset = array_shift($path); $current = $this->getDefaultValues(); @@ -666,7 +692,7 @@ trait FormTrait throw new RuntimeException(sprintf('FlexForm: Bad HTTP method %s', $method)); } - $body = $request->getParsedBody(); + $body = (array)$request->getParsedBody(); $data = isset($body['data']) ? $this->decodeData($body['data']) : null; $flash = $this->getFlash(); @@ -685,7 +711,7 @@ trait FormTrait return [ $data, - $files ?? [] + $files ]; } @@ -695,6 +721,7 @@ trait FormTrait * @param ArrayAccess|Data|null $data * @return void * @throws ValidationException + * @phpstan-param ArrayAccess|Data|null $data * @throws Exception */ protected function validateData($data = null): void @@ -709,6 +736,7 @@ trait FormTrait * * @param ArrayAccess|Data|null $data * @return void + * @phpstan-param ArrayAccess|Data|null $data */ protected function filterData($data = null): void { @@ -771,9 +799,7 @@ trait FormTrait // Decode JSON encoded fields and merge them to data. if (isset($data['_json'])) { $data = array_replace_recursive($data, $this->jsonDecode($data['_json'])); - if (null === $data) { - throw new RuntimeException(__METHOD__ . '(): Unexpected error'); - } + unset($data['_json']); } diff --git a/system/src/Grav/Framework/Interfaces/RenderInterface.php b/system/src/Grav/Framework/Interfaces/RenderInterface.php index 7a7d9d3..5bc6e58 100644 --- a/system/src/Grav/Framework/Interfaces/RenderInterface.php +++ b/system/src/Grav/Framework/Interfaces/RenderInterface.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Interfaces * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Logger/Processors/UserProcessor.php b/system/src/Grav/Framework/Logger/Processors/UserProcessor.php new file mode 100644 index 0000000..f0ebef1 --- /dev/null +++ b/system/src/Grav/Framework/Logger/Processors/UserProcessor.php @@ -0,0 +1,34 @@ +exists()) { + $record['extra']['user'] = ['username' => $user->username, 'email' => $user->email]; + } + + return $record; + } +} diff --git a/system/src/Grav/Framework/Media/Interfaces/MediaCollectionInterface.php b/system/src/Grav/Framework/Media/Interfaces/MediaCollectionInterface.php index a3587d8..2bac98f 100644 --- a/system/src/Grav/Framework/Media/Interfaces/MediaCollectionInterface.php +++ b/system/src/Grav/Framework/Media/Interfaces/MediaCollectionInterface.php @@ -3,15 +3,21 @@ /** * @package Grav\Framework\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Framework\Media\Interfaces; +use ArrayAccess; +use Countable; +use Iterator; + /** * Class implements media collection interface. + * @extends ArrayAccess + * @extends Iterator */ -interface MediaCollectionInterface extends \ArrayAccess, \Countable, \Iterator +interface MediaCollectionInterface extends ArrayAccess, Countable, Iterator { } diff --git a/system/src/Grav/Framework/Media/Interfaces/MediaInterface.php b/system/src/Grav/Framework/Media/Interfaces/MediaInterface.php index 2c5a4b3..8688ad1 100644 --- a/system/src/Grav/Framework/Media/Interfaces/MediaInterface.php +++ b/system/src/Grav/Framework/Media/Interfaces/MediaInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Media/Interfaces/MediaManipulationInterface.php b/system/src/Grav/Framework/Media/Interfaces/MediaManipulationInterface.php index fef475f..bca9a22 100644 --- a/system/src/Grav/Framework/Media/Interfaces/MediaManipulationInterface.php +++ b/system/src/Grav/Framework/Media/Interfaces/MediaManipulationInterface.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Media/Interfaces/MediaObjectInterface.php b/system/src/Grav/Framework/Media/Interfaces/MediaObjectInterface.php index dc7f148..f4409c5 100644 --- a/system/src/Grav/Framework/Media/Interfaces/MediaObjectInterface.php +++ b/system/src/Grav/Framework/Media/Interfaces/MediaObjectInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Media * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Mime/MimeTypes.php b/system/src/Grav/Framework/Mime/MimeTypes.php index dadcddf..e3a5ea8 100644 --- a/system/src/Grav/Framework/Mime/MimeTypes.php +++ b/system/src/Grav/Framework/Mime/MimeTypes.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Mime * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Object/Access/ArrayAccessTrait.php b/system/src/Grav/Framework/Object/Access/ArrayAccessTrait.php index de5cc7a..4b67599 100644 --- a/system/src/Grav/Framework/Object/Access/ArrayAccessTrait.php +++ b/system/src/Grav/Framework/Object/Access/ArrayAccessTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Object * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -21,6 +21,7 @@ trait ArrayAccessTrait * @param mixed $offset An offset to check for. * @return bool Returns TRUE on success or FALSE on failure. */ + #[\ReturnTypeWillChange] public function offsetExists($offset) { return $this->hasProperty($offset); @@ -32,6 +33,7 @@ trait ArrayAccessTrait * @param mixed $offset The offset to retrieve. * @return mixed Can return all value types. */ + #[\ReturnTypeWillChange] public function offsetGet($offset) { return $this->getProperty($offset); @@ -44,6 +46,7 @@ trait ArrayAccessTrait * @param mixed $value The value to set. * @return void */ + #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { $this->setProperty($offset, $value); @@ -55,6 +58,7 @@ trait ArrayAccessTrait * @param mixed $offset The offset to unset. * @return void */ + #[\ReturnTypeWillChange] public function offsetUnset($offset) { $this->unsetProperty($offset); diff --git a/system/src/Grav/Framework/Object/Access/NestedArrayAccessTrait.php b/system/src/Grav/Framework/Object/Access/NestedArrayAccessTrait.php index e048565..d9acb10 100644 --- a/system/src/Grav/Framework/Object/Access/NestedArrayAccessTrait.php +++ b/system/src/Grav/Framework/Object/Access/NestedArrayAccessTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Object * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -21,6 +21,7 @@ trait NestedArrayAccessTrait * @param mixed $offset An offset to check for. * @return bool Returns TRUE on success or FALSE on failure. */ + #[\ReturnTypeWillChange] public function offsetExists($offset) { return $this->hasNestedProperty($offset); @@ -32,6 +33,7 @@ trait NestedArrayAccessTrait * @param mixed $offset The offset to retrieve. * @return mixed Can return all value types. */ + #[\ReturnTypeWillChange] public function offsetGet($offset) { return $this->getNestedProperty($offset); @@ -44,6 +46,7 @@ trait NestedArrayAccessTrait * @param mixed $value The value to set. * @return void */ + #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { $this->setNestedProperty($offset, $value); @@ -55,6 +58,7 @@ trait NestedArrayAccessTrait * @param mixed $offset The offset to unset. * @return void */ + #[\ReturnTypeWillChange] public function offsetUnset($offset) { $this->unsetNestedProperty($offset); diff --git a/system/src/Grav/Framework/Object/Access/NestedPropertyCollectionTrait.php b/system/src/Grav/Framework/Object/Access/NestedPropertyCollectionTrait.php index 120c1a4..561b6fe 100644 --- a/system/src/Grav/Framework/Object/Access/NestedPropertyCollectionTrait.php +++ b/system/src/Grav/Framework/Object/Access/NestedPropertyCollectionTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Object * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Object/Access/NestedPropertyTrait.php b/system/src/Grav/Framework/Object/Access/NestedPropertyTrait.php index 7409f4c..64107e0 100644 --- a/system/src/Grav/Framework/Object/Access/NestedPropertyTrait.php +++ b/system/src/Grav/Framework/Object/Access/NestedPropertyTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Object * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -42,8 +42,8 @@ trait NestedPropertyTrait public function getNestedProperty($property, $default = null, $separator = null) { $separator = $separator ?: '.'; - $path = explode($separator, $property) ?: []; - $offset = array_shift($path) ?? ''; + $path = explode($separator, $property); + $offset = array_shift($path); if (!$this->hasProperty($offset)) { return $default; @@ -85,8 +85,8 @@ trait NestedPropertyTrait public function setNestedProperty($property, $value, $separator = null) { $separator = $separator ?: '.'; - $path = explode($separator, $property) ?: []; - $offset = array_shift($path) ?? ''; + $path = explode($separator, $property); + $offset = array_shift($path); if (!$path) { $this->setProperty($offset, $value); @@ -127,8 +127,8 @@ trait NestedPropertyTrait public function unsetNestedProperty($property, $separator = null) { $separator = $separator ?: '.'; - $path = explode($separator, $property) ?: []; - $offset = array_shift($path) ?? ''; + $path = explode($separator, $property); + $offset = array_shift($path); if (!$path) { $this->unsetProperty($offset); diff --git a/system/src/Grav/Framework/Object/Access/OverloadedPropertyTrait.php b/system/src/Grav/Framework/Object/Access/OverloadedPropertyTrait.php index 0f0f89f..f417fba 100644 --- a/system/src/Grav/Framework/Object/Access/OverloadedPropertyTrait.php +++ b/system/src/Grav/Framework/Object/Access/OverloadedPropertyTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Object * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -21,6 +21,7 @@ trait OverloadedPropertyTrait * @param mixed $offset An offset to check for. * @return bool Returns TRUE on success or FALSE on failure. */ + #[\ReturnTypeWillChange] public function __isset($offset) { return $this->hasProperty($offset); @@ -32,6 +33,7 @@ trait OverloadedPropertyTrait * @param mixed $offset The offset to retrieve. * @return mixed Can return all value types. */ + #[\ReturnTypeWillChange] public function __get($offset) { return $this->getProperty($offset); @@ -44,6 +46,7 @@ trait OverloadedPropertyTrait * @param mixed $value The value to set. * @return void */ + #[\ReturnTypeWillChange] public function __set($offset, $value) { $this->setProperty($offset, $value); @@ -55,6 +58,7 @@ trait OverloadedPropertyTrait * @param mixed $offset The name value to unset * @return void */ + #[\ReturnTypeWillChange] public function __unset($offset) { $this->unsetProperty($offset); diff --git a/system/src/Grav/Framework/Object/ArrayObject.php b/system/src/Grav/Framework/Object/ArrayObject.php index 2bbe937..a38864c 100644 --- a/system/src/Grav/Framework/Object/ArrayObject.php +++ b/system/src/Grav/Framework/Object/ArrayObject.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Object * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -19,6 +19,7 @@ use Grav\Framework\Object\Property\ArrayPropertyTrait; /** * Array Objects keep the data in private array property. + * @implements ArrayAccess */ class ArrayObject implements NestedObjectInterface, ArrayAccess { diff --git a/system/src/Grav/Framework/Object/Base/ObjectCollectionTrait.php b/system/src/Grav/Framework/Object/Base/ObjectCollectionTrait.php index 3c172fe..e29370a 100644 --- a/system/src/Grav/Framework/Object/Base/ObjectCollectionTrait.php +++ b/system/src/Grav/Framework/Object/Base/ObjectCollectionTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Object * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -11,6 +11,7 @@ namespace Grav\Framework\Object\Base; use Grav\Framework\Compat\Serializable; use Grav\Framework\Object\Interfaces\ObjectInterface; +use InvalidArgumentException; use function call_user_func_array; use function get_class; use function is_callable; @@ -19,6 +20,9 @@ use function is_object; /** * ObjectCollection Trait * @package Grav\Framework\Object + * + * @template TKey as array-key + * @template T as ObjectInterface */ trait ObjectCollectionTrait { @@ -51,6 +55,7 @@ trait ObjectCollectionTrait } $class = get_class($this); + return $type . strtolower(substr($class, strrpos($class, '\\') + 1)); } @@ -167,7 +172,7 @@ trait ObjectCollectionTrait protected function doUnserialize(array $data) { if (!isset($data['key'], $data['type'], $data['elements']) || $data['type'] !== $this->getType()) { - throw new \InvalidArgumentException("Cannot unserialize '{$this->getType()}': Bad data"); + throw new InvalidArgumentException("Cannot unserialize '{$this->getType()}': Bad data"); } $this->setKey($data['key']); @@ -179,6 +184,7 @@ trait ObjectCollectionTrait * * @return array */ + #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->doSerialize(); @@ -189,6 +195,7 @@ trait ObjectCollectionTrait * * @return string */ + #[\ReturnTypeWillChange] public function __toString() { return $this->getKey(); @@ -207,14 +214,18 @@ trait ObjectCollectionTrait /** * Create a copy from this collection by cloning all objects in the collection. + * + * @return static */ public function copy() { $list = []; foreach ($this->getIterator() as $key => $value) { + /** @phpstan-ignore-next-line */ $list[$key] = is_object($value) ? clone $value : $value; } + /** @phpstan-var static */ return $this->createFrom($list); } @@ -330,6 +341,7 @@ trait ObjectCollectionTrait * * @param string $property * @return array + * @phpstan-return array */ public function group($property) { @@ -348,12 +360,13 @@ trait ObjectCollectionTrait * * @param string $property * @return static[] + * @phpstan-return array> */ public function collectionGroup($property) { $collections = []; foreach ($this->group($property) as $id => $elements) { - /** @var static $collection */ + /** @phpstan-var static $collection */ $collection = $this->createFrom($elements); $collections[$id] = $collection; diff --git a/system/src/Grav/Framework/Object/Base/ObjectTrait.php b/system/src/Grav/Framework/Object/Base/ObjectTrait.php index d4e324b..86df8c7 100644 --- a/system/src/Grav/Framework/Object/Base/ObjectTrait.php +++ b/system/src/Grav/Framework/Object/Base/ObjectTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Object * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -172,6 +172,7 @@ trait ObjectTrait * * @return array */ + #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->doSerialize(); @@ -182,6 +183,7 @@ trait ObjectTrait * * @return string */ + #[\ReturnTypeWillChange] public function __toString() { return $this->getKey(); diff --git a/system/src/Grav/Framework/Object/Collection/ObjectExpressionVisitor.php b/system/src/Grav/Framework/Object/Collection/ObjectExpressionVisitor.php index d8f1524..e8b12b7 100644 --- a/system/src/Grav/Framework/Object/Collection/ObjectExpressionVisitor.php +++ b/system/src/Grav/Framework/Object/Collection/ObjectExpressionVisitor.php @@ -3,12 +3,13 @@ /** * @package Grav\Framework\Object * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Framework\Object\Collection; +use ArrayAccess; use Closure; use Doctrine\Common\Collections\Expr\ClosureExpressionVisitor; use Doctrine\Common\Collections\Expr\Comparison; @@ -42,7 +43,7 @@ class ObjectExpressionVisitor extends ClosureExpressionVisitor $field = rtrim($field, ')'); } - if (isset($object[$field])) { + if ($object instanceof ArrayAccess && isset($object[$field])) { $value = $object[$field]; } else { $accessors = array('', 'get', 'is'); @@ -233,7 +234,7 @@ class ObjectExpressionVisitor extends ClosureExpressionVisitor }; default: - throw new RuntimeException("Unknown comparison operator: " . $comparison->getOperator()); + throw new RuntimeException('Unknown comparison operator: ' . $comparison->getOperator()); } } } diff --git a/system/src/Grav/Framework/Object/Interfaces/NestedObjectCollectionInterface.php b/system/src/Grav/Framework/Object/Interfaces/NestedObjectCollectionInterface.php index a2431be..9f00bab 100644 --- a/system/src/Grav/Framework/Object/Interfaces/NestedObjectCollectionInterface.php +++ b/system/src/Grav/Framework/Object/Interfaces/NestedObjectCollectionInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Object * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Object/Interfaces/NestedObjectInterface.php b/system/src/Grav/Framework/Object/Interfaces/NestedObjectInterface.php index 4d7a880..c144d8e 100644 --- a/system/src/Grav/Framework/Object/Interfaces/NestedObjectInterface.php +++ b/system/src/Grav/Framework/Object/Interfaces/NestedObjectInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Object * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Object/Interfaces/ObjectCollectionInterface.php b/system/src/Grav/Framework/Object/Interfaces/ObjectCollectionInterface.php index 8169c24..bd25f61 100644 --- a/system/src/Grav/Framework/Object/Interfaces/ObjectCollectionInterface.php +++ b/system/src/Grav/Framework/Object/Interfaces/ObjectCollectionInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Object * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Object/Interfaces/ObjectInterface.php b/system/src/Grav/Framework/Object/Interfaces/ObjectInterface.php index e8ec2c1..bc4d45d 100644 --- a/system/src/Grav/Framework/Object/Interfaces/ObjectInterface.php +++ b/system/src/Grav/Framework/Object/Interfaces/ObjectInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Object * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Object/LazyObject.php b/system/src/Grav/Framework/Object/LazyObject.php index 309c0d6..6be2558 100644 --- a/system/src/Grav/Framework/Object/LazyObject.php +++ b/system/src/Grav/Framework/Object/LazyObject.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Object * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -22,6 +22,7 @@ use Grav\Framework\Object\Property\LazyPropertyTrait; * not exist or is not initialized. * * @package Grav\Framework\Object + * @implements ArrayAccess */ class LazyObject implements NestedObjectInterface, ArrayAccess { diff --git a/system/src/Grav/Framework/Object/ObjectCollection.php b/system/src/Grav/Framework/Object/ObjectCollection.php index 3fdebce..94392af 100644 --- a/system/src/Grav/Framework/Object/ObjectCollection.php +++ b/system/src/Grav/Framework/Object/ObjectCollection.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Object * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -22,12 +22,13 @@ use function array_slice; * Class contains a collection of objects. * * @template TKey of array-key - * @template T + * @template T of \Grav\Framework\Object\Interfaces\ObjectInterface * @extends ArrayCollection * @implements NestedObjectCollectionInterface */ class ObjectCollection extends ArrayCollection implements NestedObjectCollectionInterface { + /** @phpstan-use ObjectCollectionTrait */ use ObjectCollectionTrait; use NestedPropertyCollectionTrait { NestedPropertyCollectionTrait::group insteadof ObjectCollectionTrait; @@ -65,6 +66,7 @@ class ObjectCollection extends ArrayCollection implements NestedObjectCollection */ public function limit($start, $limit = null) { + /** @phpstan-var static */ return $this->createFrom($this->slice($start, $limit)); } @@ -86,14 +88,11 @@ class ObjectCollection extends ArrayCollection implements NestedObjectCollection if ($orderings = $criteria->getOrderings()) { $next = null; - /** - * @var string $field - * @var string $ordering - */ foreach (array_reverse($orderings) as $field => $ordering) { $next = ObjectExpressionVisitor::sortByField($field, $ordering === Criteria::DESC ? -1 : 1, $next); } + /** @phpstan-ignore-next-line */ if ($next) { uasort($filtered, $next); } @@ -106,11 +105,13 @@ class ObjectCollection extends ArrayCollection implements NestedObjectCollection $filtered = array_slice($filtered, (int)$offset, $length); } + /** @phpstan-var static */ return $this->createFrom($filtered); } /** * @return array + * @phpstan-return array */ protected function getElements() { @@ -120,9 +121,11 @@ class ObjectCollection extends ArrayCollection implements NestedObjectCollection /** * @param array $elements * @return array + * @phpstan-return array */ protected function setElements(array $elements) { + /** @phpstan-var array $elements */ return $elements; } } diff --git a/system/src/Grav/Framework/Object/ObjectIndex.php b/system/src/Grav/Framework/Object/ObjectIndex.php index b7b416d..ab495bd 100644 --- a/system/src/Grav/Framework/Object/ObjectIndex.php +++ b/system/src/Grav/Framework/Object/ObjectIndex.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Object * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -24,8 +24,9 @@ use function is_object; * order to use the class. * * @template TKey of array-key - * @template T - * @extends AbstractIndexCollection + * @template T of \Grav\Framework\Object\Interfaces\ObjectInterface + * @template C of ObjectCollectionInterface + * @extends AbstractIndexCollection * @implements NestedObjectCollectionInterface */ abstract class ObjectIndex extends AbstractIndexCollection implements NestedObjectCollectionInterface @@ -34,7 +35,7 @@ abstract class ObjectIndex extends AbstractIndexCollection implements NestedObje protected static $type; /** @var string */ - private $_key; + protected $_key; /** * @param bool $prefix @@ -94,6 +95,7 @@ abstract class ObjectIndex extends AbstractIndexCollection implements NestedObje * @param string $property Object property to be updated. * @param string $value New value. * @return ObjectCollectionInterface + * @phpstan-return C */ public function setProperty($property, $value) { @@ -104,6 +106,7 @@ abstract class ObjectIndex extends AbstractIndexCollection implements NestedObje * @param string $property Object property to be defined. * @param mixed $default Default value. * @return ObjectCollectionInterface + * @phpstan-return C */ public function defProperty($property, $default) { @@ -113,6 +116,7 @@ abstract class ObjectIndex extends AbstractIndexCollection implements NestedObje /** * @param string $property Object property to be unset. * @return ObjectCollectionInterface + * @phpstan-return C */ public function unsetProperty($property) { @@ -145,6 +149,7 @@ abstract class ObjectIndex extends AbstractIndexCollection implements NestedObje * @param mixed $value New value. * @param string|null $separator Separator, defaults to '.' * @return ObjectCollectionInterface + * @phpstan-return C */ public function setNestedProperty($property, $value, $separator = null) { @@ -156,6 +161,7 @@ abstract class ObjectIndex extends AbstractIndexCollection implements NestedObje * @param mixed $default Default value. * @param string|null $separator Separator, defaults to '.' * @return ObjectCollectionInterface + * @phpstan-return C */ public function defNestedProperty($property, $default, $separator = null) { @@ -166,6 +172,7 @@ abstract class ObjectIndex extends AbstractIndexCollection implements NestedObje * @param string $property Object property to be unset. * @param string|null $separator Separator, defaults to '.' * @return ObjectCollectionInterface + * @phpstan-return C */ public function unsetNestedProperty($property, $separator = null) { @@ -176,15 +183,17 @@ abstract class ObjectIndex extends AbstractIndexCollection implements NestedObje * Create a copy from this collection by cloning all objects in the collection. * * @return static - * @return static + * @return static */ public function copy() { $list = []; foreach ($this->getIterator() as $key => $value) { + /** @phpstan-ignore-next-line */ $list[$key] = is_object($value) ? clone $value : $value; } + /** @phpstan-var static */ return $this->createFrom($list); } @@ -199,6 +208,7 @@ abstract class ObjectIndex extends AbstractIndexCollection implements NestedObje /** * @param array $ordering * @return ObjectCollectionInterface + * @phpstan-return C */ public function orderBy(array $ordering) { @@ -231,6 +241,7 @@ abstract class ObjectIndex extends AbstractIndexCollection implements NestedObje * * @param string $property * @return ObjectCollectionInterface[] + * @phpstan-return C[] */ public function collectionGroup($property) { @@ -238,14 +249,18 @@ abstract class ObjectIndex extends AbstractIndexCollection implements NestedObje } /** - * {@inheritDoc} + * @param Criteria $criteria + * @return ObjectCollectionInterface + * @phpstan-return C */ public function matching(Criteria $criteria) { - /** @var ObjectCollectionInterface $collection */ $collection = $this->loadCollection($this->getEntries()); - return $collection->matching($criteria); + /** @phpstan-var C $matching */ + $matching = $collection->matching($criteria); + + return $matching; } /** @@ -253,6 +268,7 @@ abstract class ObjectIndex extends AbstractIndexCollection implements NestedObje * @param array $arguments * @return mixed */ + #[\ReturnTypeWillChange] abstract public function __call($name, $arguments); /** diff --git a/system/src/Grav/Framework/Object/Property/ArrayPropertyTrait.php b/system/src/Grav/Framework/Object/Property/ArrayPropertyTrait.php index fa3fed1..8ab4c41 100644 --- a/system/src/Grav/Framework/Object/Property/ArrayPropertyTrait.php +++ b/system/src/Grav/Framework/Object/Property/ArrayPropertyTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Object * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -97,7 +97,7 @@ trait ArrayPropertyTrait */ protected function getElements() { - return array_filter($this->_elements, function ($val) { + return array_filter($this->_elements, static function ($val) { return $val !== null; }); } diff --git a/system/src/Grav/Framework/Object/Property/LazyPropertyTrait.php b/system/src/Grav/Framework/Object/Property/LazyPropertyTrait.php index d5baddb..e689259 100644 --- a/system/src/Grav/Framework/Object/Property/LazyPropertyTrait.php +++ b/system/src/Grav/Framework/Object/Property/LazyPropertyTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Object * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -87,8 +87,7 @@ trait LazyPropertyTrait */ protected function doUnsetProperty($property) { - $this->hasObjectProperty($property) ? - $this->unsetObjectProperty($property) : $this->unsetArrayProperty($property); + $this->hasObjectProperty($property) ? $this->unsetObjectProperty($property) : $this->unsetArrayProperty($property); } /** diff --git a/system/src/Grav/Framework/Object/Property/MixedPropertyTrait.php b/system/src/Grav/Framework/Object/Property/MixedPropertyTrait.php index 584e812..1b57aab 100644 --- a/system/src/Grav/Framework/Object/Property/MixedPropertyTrait.php +++ b/system/src/Grav/Framework/Object/Property/MixedPropertyTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Object * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Object/Property/ObjectPropertyTrait.php b/system/src/Grav/Framework/Object/Property/ObjectPropertyTrait.php index 435220a..bb0195d 100644 --- a/system/src/Grav/Framework/Object/Property/ObjectPropertyTrait.php +++ b/system/src/Grav/Framework/Object/Property/ObjectPropertyTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Object * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Object/PropertyObject.php b/system/src/Grav/Framework/Object/PropertyObject.php index dbeb5c4..d9cdde8 100644 --- a/system/src/Grav/Framework/Object/PropertyObject.php +++ b/system/src/Grav/Framework/Object/PropertyObject.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Object * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -19,6 +19,8 @@ use Grav\Framework\Object\Property\ObjectPropertyTrait; /** * Property Objects keep their data in protected object properties. + * + * @implements ArrayAccess */ class PropertyObject implements NestedObjectInterface, ArrayAccess { diff --git a/system/src/Grav/Framework/Pagination/AbstractPagination.php b/system/src/Grav/Framework/Pagination/AbstractPagination.php index 0be21ec..a4d902a 100644 --- a/system/src/Grav/Framework/Pagination/AbstractPagination.php +++ b/system/src/Grav/Framework/Pagination/AbstractPagination.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Pagination * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -234,7 +234,9 @@ class AbstractPagination implements PaginationInterface /** * @return ArrayIterator + * @phpstan-return ArrayIterator */ + #[\ReturnTypeWillChange] public function getIterator() { $this->loadItems(); diff --git a/system/src/Grav/Framework/Pagination/AbstractPaginationPage.php b/system/src/Grav/Framework/Pagination/AbstractPaginationPage.php index 95ff8af..072fea7 100644 --- a/system/src/Grav/Framework/Pagination/AbstractPaginationPage.php +++ b/system/src/Grav/Framework/Pagination/AbstractPaginationPage.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Pagination * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -41,7 +41,7 @@ abstract class AbstractPaginationPage implements PaginationPageInterface */ public function getOptions(): array { - return $this->options ?? []; + return $this->options; } /** diff --git a/system/src/Grav/Framework/Pagination/Interfaces/PaginationInterface.php b/system/src/Grav/Framework/Pagination/Interfaces/PaginationInterface.php index 56d3361..1a82147 100644 --- a/system/src/Grav/Framework/Pagination/Interfaces/PaginationInterface.php +++ b/system/src/Grav/Framework/Pagination/Interfaces/PaginationInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Pagination * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -16,6 +16,7 @@ use IteratorAggregate; /** * Interface PaginationInterface * @package Grav\Framework\Pagination\Interfaces + * @extends IteratorAggregate */ interface PaginationInterface extends Countable, IteratorAggregate { diff --git a/system/src/Grav/Framework/Pagination/Interfaces/PaginationPageInterface.php b/system/src/Grav/Framework/Pagination/Interfaces/PaginationPageInterface.php index b90594d..26c0692 100644 --- a/system/src/Grav/Framework/Pagination/Interfaces/PaginationPageInterface.php +++ b/system/src/Grav/Framework/Pagination/Interfaces/PaginationPageInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Pagination * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Pagination/Pagination.php b/system/src/Grav/Framework/Pagination/Pagination.php index e602872..8f2fa20 100644 --- a/system/src/Grav/Framework/Pagination/Pagination.php +++ b/system/src/Grav/Framework/Pagination/Pagination.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Pagination * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Pagination/PaginationPage.php b/system/src/Grav/Framework/Pagination/PaginationPage.php index 63ceb5f..e64abe9 100644 --- a/system/src/Grav/Framework/Pagination/PaginationPage.php +++ b/system/src/Grav/Framework/Pagination/PaginationPage.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Pagination * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Psr7/AbstractUri.php b/system/src/Grav/Framework/Psr7/AbstractUri.php index 85d802f..145437a 100644 --- a/system/src/Grav/Framework/Psr7/AbstractUri.php +++ b/system/src/Grav/Framework/Psr7/AbstractUri.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Psr7 * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -263,6 +263,7 @@ abstract class AbstractUri implements UriInterface /** * @return string */ + #[\ReturnTypeWillChange] public function __toString() { return $this->getUrl(); diff --git a/system/src/Grav/Framework/Psr7/Request.php b/system/src/Grav/Framework/Psr7/Request.php index cd65fd9..795a1a4 100644 --- a/system/src/Grav/Framework/Psr7/Request.php +++ b/system/src/Grav/Framework/Psr7/Request.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Psr7 * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Psr7/Response.php b/system/src/Grav/Framework/Psr7/Response.php index 6cb67e0..6189c4a 100644 --- a/system/src/Grav/Framework/Psr7/Response.php +++ b/system/src/Grav/Framework/Psr7/Response.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Psr7 * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -53,6 +53,7 @@ class Response implements ResponseInterface * @param int $options Json encoding options * @param int $depth Json encoding max depth * @return static + * @phpstan-param positive-int $depth */ public function withJson($data, int $status = null, int $options = 0, int $depth = 512): ResponseInterface { @@ -257,7 +258,7 @@ class Response implements ResponseInterface } $output .= self::EOL; - $output .= (string) $response->getBody(); + $output .= $response->getBody(); return $output; } diff --git a/system/src/Grav/Framework/Psr7/ServerRequest.php b/system/src/Grav/Framework/Psr7/ServerRequest.php index 4084834..cb5bcbb 100644 --- a/system/src/Grav/Framework/Psr7/ServerRequest.php +++ b/system/src/Grav/Framework/Psr7/ServerRequest.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Psr7 * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -98,13 +98,8 @@ class ServerRequest implements ServerRequestInterface public function getCookieParam($key, $default = null) { $cookies = $this->getRequest()->getCookieParams(); - $result = $default; - if (isset($cookies[$key])) { - $result = $cookies[$key]; - } - - return $result; + return $cookies[$key] ?? $default; } /** @@ -238,13 +233,8 @@ class ServerRequest implements ServerRequestInterface public function getQueryParam($key, $default = null) { $getParams = $this->getQueryParams(); - $result = $default; - if (isset($getParams[$key])) { - $result = $getParams[$key]; - } - - return $result; + return $getParams[$key] ?? $default; } /** diff --git a/system/src/Grav/Framework/Psr7/Stream.php b/system/src/Grav/Framework/Psr7/Stream.php index a5a1043..6546bdc 100644 --- a/system/src/Grav/Framework/Psr7/Stream.php +++ b/system/src/Grav/Framework/Psr7/Stream.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Psr7 * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Psr7/Traits/MessageDecoratorTrait.php b/system/src/Grav/Framework/Psr7/Traits/MessageDecoratorTrait.php index 39e3818..c09a03c 100644 --- a/system/src/Grav/Framework/Psr7/Traits/MessageDecoratorTrait.php +++ b/system/src/Grav/Framework/Psr7/Traits/MessageDecoratorTrait.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Psr7 * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Psr7/Traits/RequestDecoratorTrait.php b/system/src/Grav/Framework/Psr7/Traits/RequestDecoratorTrait.php index 13f4c6f..49f2a92 100644 --- a/system/src/Grav/Framework/Psr7/Traits/RequestDecoratorTrait.php +++ b/system/src/Grav/Framework/Psr7/Traits/RequestDecoratorTrait.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Psr7 * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Psr7/Traits/ResponseDecoratorTrait.php b/system/src/Grav/Framework/Psr7/Traits/ResponseDecoratorTrait.php index 7b62c3c..4bbc736 100644 --- a/system/src/Grav/Framework/Psr7/Traits/ResponseDecoratorTrait.php +++ b/system/src/Grav/Framework/Psr7/Traits/ResponseDecoratorTrait.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Psr7 * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Psr7/Traits/ServerRequestDecoratorTrait.php b/system/src/Grav/Framework/Psr7/Traits/ServerRequestDecoratorTrait.php index 33c26e0..9222219 100644 --- a/system/src/Grav/Framework/Psr7/Traits/ServerRequestDecoratorTrait.php +++ b/system/src/Grav/Framework/Psr7/Traits/ServerRequestDecoratorTrait.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Psr7 * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Psr7/Traits/StreamDecoratorTrait.php b/system/src/Grav/Framework/Psr7/Traits/StreamDecoratorTrait.php index db1e746..448323f 100644 --- a/system/src/Grav/Framework/Psr7/Traits/StreamDecoratorTrait.php +++ b/system/src/Grav/Framework/Psr7/Traits/StreamDecoratorTrait.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Psr7 * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -33,6 +33,7 @@ trait StreamDecoratorTrait /** * @return void */ + #[\ReturnTypeWillChange] public function __destruct() { $this->stream->close(); diff --git a/system/src/Grav/Framework/Psr7/Traits/UploadedFileDecoratorTrait.php b/system/src/Grav/Framework/Psr7/Traits/UploadedFileDecoratorTrait.php index e0c65bd..eec3add 100644 --- a/system/src/Grav/Framework/Psr7/Traits/UploadedFileDecoratorTrait.php +++ b/system/src/Grav/Framework/Psr7/Traits/UploadedFileDecoratorTrait.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Psr7 * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Psr7/Traits/UriDecorationTrait.php b/system/src/Grav/Framework/Psr7/Traits/UriDecorationTrait.php index 607d757..73adb64 100644 --- a/system/src/Grav/Framework/Psr7/Traits/UriDecorationTrait.php +++ b/system/src/Grav/Framework/Psr7/Traits/UriDecorationTrait.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Psr7 * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Psr7/UploadedFile.php b/system/src/Grav/Framework/Psr7/UploadedFile.php index bfa63cd..c3ed62e 100644 --- a/system/src/Grav/Framework/Psr7/UploadedFile.php +++ b/system/src/Grav/Framework/Psr7/UploadedFile.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Psr7 * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Psr7/Uri.php b/system/src/Grav/Framework/Psr7/Uri.php index c920b33..63fe04e 100644 --- a/system/src/Grav/Framework/Psr7/Uri.php +++ b/system/src/Grav/Framework/Psr7/Uri.php @@ -5,7 +5,7 @@ declare(strict_types=1); /** * @package Grav\Framework\Psr7 * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -24,9 +24,6 @@ class Uri implements UriInterface { use UriDecorationTrait; - /** @var array Array of Uri query. */ - private $queryParams; - public function __construct(string $uri = '') { $this->uri = new \Nyholm\Psr7\Uri($uri); diff --git a/system/src/Grav/Framework/RequestHandler/Exception/InvalidArgumentException.php b/system/src/Grav/Framework/RequestHandler/Exception/InvalidArgumentException.php index 451e586..2456ea1 100644 --- a/system/src/Grav/Framework/RequestHandler/Exception/InvalidArgumentException.php +++ b/system/src/Grav/Framework/RequestHandler/Exception/InvalidArgumentException.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\RequestHandler * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/RequestHandler/Exception/NotFoundException.php b/system/src/Grav/Framework/RequestHandler/Exception/NotFoundException.php index 28a226a..ce79ab2 100644 --- a/system/src/Grav/Framework/RequestHandler/Exception/NotFoundException.php +++ b/system/src/Grav/Framework/RequestHandler/Exception/NotFoundException.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\RequestHandler * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -21,9 +21,6 @@ use function in_array; */ class NotFoundException extends RequestException { - /** @var ServerRequestInterface */ - private $request; - /** * NotFoundException constructor. * @param ServerRequestInterface $request @@ -37,9 +34,4 @@ class NotFoundException extends RequestException parent::__construct($request, 'Not Found', 404, $previous); } } - - public function getRequest(): ServerRequestInterface - { - return $this->request; - } } diff --git a/system/src/Grav/Framework/RequestHandler/Exception/NotHandledException.php b/system/src/Grav/Framework/RequestHandler/Exception/NotHandledException.php index 1b18473..7b4a0e7 100644 --- a/system/src/Grav/Framework/RequestHandler/Exception/NotHandledException.php +++ b/system/src/Grav/Framework/RequestHandler/Exception/NotHandledException.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\RequestHandler * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/RequestHandler/Exception/PageExpiredException.php b/system/src/Grav/Framework/RequestHandler/Exception/PageExpiredException.php index 747c0ca..f752167 100644 --- a/system/src/Grav/Framework/RequestHandler/Exception/PageExpiredException.php +++ b/system/src/Grav/Framework/RequestHandler/Exception/PageExpiredException.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\RequestHandler * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/RequestHandler/Exception/RequestException.php b/system/src/Grav/Framework/RequestHandler/Exception/RequestException.php index 6079964..678b267 100644 --- a/system/src/Grav/Framework/RequestHandler/Exception/RequestException.php +++ b/system/src/Grav/Framework/RequestHandler/Exception/RequestException.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\RequestHandler * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/RequestHandler/Middlewares/Exceptions.php b/system/src/Grav/Framework/RequestHandler/Middlewares/Exceptions.php index a9935ee..36ef3a5 100644 --- a/system/src/Grav/Framework/RequestHandler/Middlewares/Exceptions.php +++ b/system/src/Grav/Framework/RequestHandler/Middlewares/Exceptions.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\RequestHandler * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -11,9 +11,12 @@ declare(strict_types=1); namespace Grav\Framework\RequestHandler\Middlewares; +use Grav\Common\Data\ValidationException; use Grav\Common\Debugger; use Grav\Common\Grav; use Grav\Framework\Psr7\Response; +use JsonException; +use JsonSerializable; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; @@ -27,16 +30,34 @@ use function get_class; */ class Exceptions implements MiddlewareInterface { + /** + * @param ServerRequestInterface $request + * @param RequestHandlerInterface $handler + * @return ResponseInterface + * @throws JsonException + */ public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { try { return $handler->handle($request); } catch (Throwable $exception) { + $code = $exception->getCode(); + if ($exception instanceof ValidationException) { + $message = $exception->getMessage(); + } else { + $message = htmlspecialchars($exception->getMessage(), ENT_QUOTES | ENT_HTML5, 'UTF-8'); + } + + $extra = $exception instanceof JsonSerializable ? $exception->jsonSerialize() : []; + $response = [ + 'code' => $code, + 'status' => 'error', + 'message' => $message, 'error' => [ - 'code' => $exception->getCode(), - 'message' => $exception->getMessage(), - ] + 'code' => $code, + 'message' => $message, + ] + $extra ]; /** @var Debugger $debugger */ @@ -51,9 +72,9 @@ class Exceptions implements MiddlewareInterface } /** @var string $json */ - $json = json_encode($response); + $json = json_encode($response, JSON_THROW_ON_ERROR); - return new Response($exception->getCode() ?: 500, ['Content-Type' => 'application/json'], $json); + return new Response($code ?: 500, ['Content-Type' => 'application/json'], $json); } } } diff --git a/system/src/Grav/Framework/RequestHandler/RequestHandler.php b/system/src/Grav/Framework/RequestHandler/RequestHandler.php index 5b03805..2ce2427 100644 --- a/system/src/Grav/Framework/RequestHandler/RequestHandler.php +++ b/system/src/Grav/Framework/RequestHandler/RequestHandler.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\RequestHandler * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -12,9 +12,11 @@ declare(strict_types=1); namespace Grav\Framework\RequestHandler; use Grav\Framework\RequestHandler\Traits\RequestHandlerTrait; +use Pimple\Container; use Psr\Container\ContainerInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; +use function assert; /** * Class RequestHandler @@ -47,7 +49,11 @@ class RequestHandler implements RequestHandlerInterface */ public function addCallable(string $name, callable $callable): self { - $this->container[$name] = $callable; + if (null !== $this->container) { + assert($this->container instanceof Container); + $this->container[$name] = $callable; + } + array_unshift($this->middleware, $name); return $this; @@ -62,7 +68,11 @@ class RequestHandler implements RequestHandlerInterface */ public function addMiddleware(string $name, MiddlewareInterface $middleware): self { - $this->container[$name] = $middleware; + if (null !== $this->container) { + assert($this->container instanceof Container); + $this->container[$name] = $middleware; + } + array_unshift($this->middleware, $name); return $this; diff --git a/system/src/Grav/Framework/RequestHandler/Traits/RequestHandlerTrait.php b/system/src/Grav/Framework/RequestHandler/Traits/RequestHandlerTrait.php index 1b5a79e..ff82889 100644 --- a/system/src/Grav/Framework/RequestHandler/Traits/RequestHandlerTrait.php +++ b/system/src/Grav/Framework/RequestHandler/Traits/RequestHandlerTrait.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\RequestHandler * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Route/Route.php b/system/src/Grav/Framework/Route/Route.php index f9a7fa7..d4d9078 100644 --- a/system/src/Grav/Framework/Route/Route.php +++ b/system/src/Grav/Framework/Route/Route.php @@ -3,12 +3,13 @@ /** * @package Grav\Framework\Route * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Framework\Route; +use Grav\Framework\Uri\Uri; use Grav\Framework\Uri\UriFactory; use InvalidArgumentException; use function array_slice; @@ -318,7 +319,7 @@ class Route } /** - * @return \Grav\Framework\Uri\Uri + * @return Uri */ public function getUri() { @@ -344,6 +345,7 @@ class Route * @return string * @deprecated 1.6 Use ->toString(true) or ->getUri() instead. */ + #[\ReturnTypeWillChange] public function __toString() { user_error(__CLASS__ . '::' . __FUNCTION__ . '() will change in the future to return route, not relative url: use ->toString(true) or ->getUri() instead.', E_USER_DEPRECATED); diff --git a/system/src/Grav/Framework/Route/RouteFactory.php b/system/src/Grav/Framework/Route/RouteFactory.php index 68baf55..fa1a76c 100644 --- a/system/src/Grav/Framework/Route/RouteFactory.php +++ b/system/src/Grav/Framework/Route/RouteFactory.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Route * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Session/Exceptions/SessionException.php b/system/src/Grav/Framework/Session/Exceptions/SessionException.php index 5ad948b..a07e87d 100644 --- a/system/src/Grav/Framework/Session/Exceptions/SessionException.php +++ b/system/src/Grav/Framework/Session/Exceptions/SessionException.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Session * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Session/Messages.php b/system/src/Grav/Framework/Session/Messages.php index 5d854a2..37c62a9 100644 --- a/system/src/Grav/Framework/Session/Messages.php +++ b/system/src/Grav/Framework/Session/Messages.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Session * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Session/Session.php b/system/src/Grav/Framework/Session/Session.php index 3feae5a..108563e 100644 --- a/system/src/Grav/Framework/Session/Session.php +++ b/system/src/Grav/Framework/Session/Session.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Session * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -189,7 +189,11 @@ class Session implements SessionInterface return $this; } - $sessionName = session_name(); + $sessionName = $this->getName(); + if (null === $sessionName) { + return $this; + } + $sessionExists = isset($_COOKIE[$sessionName]); // Protection against invalid session cookie names throwing exception: http://php.net/manual/en/function.session-id.php#116836 @@ -341,7 +345,7 @@ class Session implements SessionInterface $this->removeCookie(); setcookie( - session_name(), + $name, '', $this->getCookieOptions(-42000) ); @@ -392,6 +396,7 @@ class Session implements SessionInterface /** * @inheritdoc */ + #[\ReturnTypeWillChange] public function getIterator() { return new ArrayIterator($_SESSION); @@ -408,6 +413,7 @@ class Session implements SessionInterface /** * @inheritdoc */ + #[\ReturnTypeWillChange] public function __isset($name) { return isset($_SESSION[$name]); @@ -416,6 +422,7 @@ class Session implements SessionInterface /** * @inheritdoc */ + #[\ReturnTypeWillChange] public function __get($name) { return $_SESSION[$name] ?? null; @@ -424,6 +431,7 @@ class Session implements SessionInterface /** * @inheritdoc */ + #[\ReturnTypeWillChange] public function __set($name, $value) { $_SESSION[$name] = $value; @@ -432,6 +440,7 @@ class Session implements SessionInterface /** * @inheritdoc */ + #[\ReturnTypeWillChange] public function __unset($name) { unset($_SESSION[$name]); @@ -478,9 +487,15 @@ class Session implements SessionInterface { $this->removeCookie(); + $sessionName = $this->getName(); + $sessionId = $this->getId(); + if (null === $sessionName || null === $sessionId) { + return; + } + setcookie( - session_name(), - session_id(), + $sessionName, + $sessionId, $this->getCookieOptions() ); } diff --git a/system/src/Grav/Framework/Session/SessionInterface.php b/system/src/Grav/Framework/Session/SessionInterface.php index 43ca40f..fa2873d 100644 --- a/system/src/Grav/Framework/Session/SessionInterface.php +++ b/system/src/Grav/Framework/Session/SessionInterface.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Session * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -16,6 +16,7 @@ use RuntimeException; /** * Class Session * @package Grav\Framework\Session + * @extends IteratorAggregate */ interface SessionInterface extends IteratorAggregate { @@ -107,7 +108,9 @@ interface SessionInterface extends IteratorAggregate * Retrieve an external iterator * * @return ArrayIterator Return an ArrayIterator of $_SESSION + * @phpstan-return ArrayIterator */ + #[\ReturnTypeWillChange] public function getIterator(); /** @@ -123,6 +126,7 @@ interface SessionInterface extends IteratorAggregate * @param string $name * @return bool */ + #[\ReturnTypeWillChange] public function __isset($name); /** @@ -131,6 +135,7 @@ interface SessionInterface extends IteratorAggregate * @param string $name * @return mixed */ + #[\ReturnTypeWillChange] public function __get($name); /** @@ -140,6 +145,7 @@ interface SessionInterface extends IteratorAggregate * @param mixed $value * @return void */ + #[\ReturnTypeWillChange] public function __set($name, $value); /** @@ -148,5 +154,6 @@ interface SessionInterface extends IteratorAggregate * @param string $name * @return void */ + #[\ReturnTypeWillChange] public function __unset($name); } diff --git a/system/src/Grav/Framework/Uri/Uri.php b/system/src/Grav/Framework/Uri/Uri.php index c43496e..6355a1f 100644 --- a/system/src/Grav/Framework/Uri/Uri.php +++ b/system/src/Grav/Framework/Uri/Uri.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Uri * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Framework/Uri/UriFactory.php b/system/src/Grav/Framework/Uri/UriFactory.php index f7724b7..ebfec03 100644 --- a/system/src/Grav/Framework/Uri/UriFactory.php +++ b/system/src/Grav/Framework/Uri/UriFactory.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Uri * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -125,7 +125,7 @@ class UriFactory $encodedUrl = preg_replace_callback( '%[^:/@?&=#]+%u', - function ($matches) { + static function ($matches) { return rawurlencode($matches[0]); }, $url diff --git a/system/src/Grav/Framework/Uri/UriPartsFilter.php b/system/src/Grav/Framework/Uri/UriPartsFilter.php index 71a5135..260711b 100644 --- a/system/src/Grav/Framework/Uri/UriPartsFilter.php +++ b/system/src/Grav/Framework/Uri/UriPartsFilter.php @@ -3,7 +3,7 @@ /** * @package Grav\Framework\Uri * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Installer/Install.php b/system/src/Grav/Installer/Install.php index 4df9dcd..0f23e89 100644 --- a/system/src/Grav/Installer/Install.php +++ b/system/src/Grav/Installer/Install.php @@ -3,7 +3,7 @@ /** * @package Grav\Installer * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ @@ -36,9 +36,11 @@ final class Install 'php' => [ 'name' => 'PHP', 'versions' => [ - '7.4' => '7.4.0', + '8.1' => '8.1.0', + '8.0' => '8.0.0', + '7.4' => '7.4.1', '7.3' => '7.3.6', - '' => '7.4.12' + '' => '8.0.13' ] ], 'grav' => [ @@ -114,7 +116,7 @@ final class Install /** @var string|null */ private $location; - /** @var VersionUpdater */ + /** @var VersionUpdater|null */ private $updater; /** @var static */ @@ -151,6 +153,7 @@ final class Install * @param string|null $zip * @return void */ + #[\ReturnTypeWillChange] public function __invoke(?string $zip) { $this->zip = $zip; @@ -249,6 +252,11 @@ ERR; } try { + if (null === $this->updater) { + $versions = Versions::instance(USER_DIR . 'config/versions.yaml'); + $this->updater = new VersionUpdater('core/grav', __DIR__ . '/updates', $this->getVersion(), $versions); + } + // Update user/config/version.yaml before copying the files to avoid frontend from setting the version schema. $this->updater->install(); @@ -279,7 +287,7 @@ ERR; public function finalize(): void { // Finalize can be run without installing Grav first. - if (!$this->updater) { + if (null === $this->updater) { $versions = Versions::instance(USER_DIR . 'config/versions.yaml'); $this->updater = new VersionUpdater('core/grav', __DIR__ . '/updates', GRAV_VERSION, $versions); $this->updater->install(); diff --git a/system/src/Grav/Installer/InstallException.php b/system/src/Grav/Installer/InstallException.php index 145b7c9..cdd5b7a 100644 --- a/system/src/Grav/Installer/InstallException.php +++ b/system/src/Grav/Installer/InstallException.php @@ -3,7 +3,7 @@ /** * @package Grav\Installer * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Installer/VersionUpdate.php b/system/src/Grav/Installer/VersionUpdate.php index faaf2a2..1fde783 100644 --- a/system/src/Grav/Installer/VersionUpdate.php +++ b/system/src/Grav/Installer/VersionUpdate.php @@ -3,6 +3,7 @@ namespace Grav\Installer; use Closure; +use Grav\Common\Utils; /** * Class VersionUpdate @@ -50,7 +51,7 @@ final class VersionUpdate public function getPatch(): string { - return $this->date; + return $this->patch; } public function getUpdater(): VersionUpdater diff --git a/system/src/Grav/Installer/Versions.php b/system/src/Grav/Installer/Versions.php index 03f3b0b..92a64ce 100644 --- a/system/src/Grav/Installer/Versions.php +++ b/system/src/Grav/Installer/Versions.php @@ -3,7 +3,7 @@ /** * @package Grav\Installer * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ diff --git a/system/src/Grav/Installer/YamlUpdater.php b/system/src/Grav/Installer/YamlUpdater.php index 9af2055..fe1c7f1 100644 --- a/system/src/Grav/Installer/YamlUpdater.php +++ b/system/src/Grav/Installer/YamlUpdater.php @@ -3,12 +3,13 @@ /** * @package Grav\Installer * - * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved. + * @copyright Copyright (c) 2015 - 2022 Trilby Media, LLC. All rights reserved. * @license MIT License; see LICENSE file for details. */ namespace Grav\Installer; +use Grav\Common\Utils; use Symfony\Component\Yaml\Yaml; use function assert; use function count; diff --git a/system/src/Twig/DeferredExtension/DeferredNodeVisitorCompat.php b/system/src/Twig/DeferredExtension/DeferredNodeVisitorCompat.php index 8c441bb..1ff7fd4 100644 --- a/system/src/Twig/DeferredExtension/DeferredNodeVisitorCompat.php +++ b/system/src/Twig/DeferredExtension/DeferredNodeVisitorCompat.php @@ -22,16 +22,28 @@ final class DeferredNodeVisitorCompat implements NodeVisitorInterface { private $hasDeferred = false; - public function enterNode(\Twig_NodeInterface $node, Environment $env) : Node + /** + * @param \Twig_NodeInterface $node + * @param Environment $env + * @return Node + */ + public function enterNode(\Twig_NodeInterface $node, Environment $env): Node { if (!$this->hasDeferred && $node instanceof DeferredBlockNode) { $this->hasDeferred = true; } + \assert($node instanceof Node); + return $node; } - public function leaveNode(\Twig_NodeInterface $node, Environment $env) : ?Node + /** + * @param \Twig_NodeInterface $node + * @param Environment $env + * @return Node|null + */ + public function leaveNode(\Twig_NodeInterface $node, Environment $env): ?Node { if ($this->hasDeferred && $node instanceof ModuleNode) { $node->setNode('constructor_end', new Node([new DeferredExtensionNode(), $node->getNode('constructor_end')])); @@ -39,9 +51,14 @@ final class DeferredNodeVisitorCompat implements NodeVisitorInterface $this->hasDeferred = false; } + \assert($node instanceof Node); + return $node; } + /** + * @return int + */ public function getPriority() : int { return 0; diff --git a/tmp/forms/ma7jev9sc9l2lbinlr2fk21s6o/a4f153b35cbb7417c5038f5663e60d53/index.yaml b/tmp/forms/ma7jev9sc9l2lbinlr2fk21s6o/a4f153b35cbb7417c5038f5663e60d53/index.yaml deleted file mode 100644 index ebc68fc..0000000 --- a/tmp/forms/ma7jev9sc9l2lbinlr2fk21s6o/a4f153b35cbb7417c5038f5663e60d53/index.yaml +++ /dev/null @@ -1,32 +0,0 @@ -form: flex-pages -unique_id: a4f153b35cbb7417c5038f5663e60d53 -url: '/pages/home/evenements/_intervenants/_table-ronde-1/:add' -user: - username: admin - email: ouidade@figureslibres.io -timestamps: - created: 1633076732 - updated: 1633076732 -data: null -files: { } -object: - type: pages - key: home/evenements/_intervenants/_table-ronde-1 - storage_key: '' - timestamp: 0 - serialized: - __META: { } - storage_key: '' - parent_key: 01.home/06.evenements/03._intervenants - order: false - folder: _table-ronde-1 - template: personnes - lang: '' - root: false - route: /home/evenements/_intervenants - name: modular/personnes - modular: '1' - header: - title: 'Table ronde 1' - body_classes: modular - frontmatter: "title: 'Table ronde 1'\nbody_classes: modular\n" diff --git a/webserver-configs/htaccess.txt b/webserver-configs/htaccess.txt index 83063ae..098c582 100644 --- a/webserver-configs/htaccess.txt +++ b/webserver-configs/htaccess.txt @@ -59,9 +59,9 @@ RewriteRule .* index.php [L] # Block all direct access for these folders RewriteRule ^(\.git|cache|bin|logs|backup|webserver-configs|tests)/(.*) error [F] # Block access to specific file types for these system folders -RewriteRule ^(system|vendor)/(.*)\.(txt|xml|md|html|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F] +RewriteRule ^(system|vendor)/(.*)\.(txt|xml|md|html|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F] # Block access to specific file types for these user folders -RewriteRule ^(user)/(.*)\.(txt|md|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F] +RewriteRule ^(user)/(.*)\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ error [F] # Block all direct access to .md files: RewriteRule \.md$ error [F] # Block all direct access to files and folders beginning with a dot diff --git a/webserver-configs/lighttpd.conf b/webserver-configs/lighttpd.conf index 512362f..f14c5a8 100644 --- a/webserver-configs/lighttpd.conf +++ b/webserver-configs/lighttpd.conf @@ -33,7 +33,7 @@ $HTTP["url"] =~ "^/grav_path/(LICENSE\.txt|composer\.json|composer\.lock|nginx\. $HTTP["url"] =~ "^/grav_path/(\.git|cache|bin|logs|backup|tests)/(.*)" { url.access-deny = ("") } -$HTTP["url"] =~ "^/grav_path/(system|user|vendor)/(.*)\.(txt|md|html|yaml|yml|php|twig|sh|bat)$" { +$HTTP["url"] =~ "^/grav_path/(system|user|vendor)/(.*)\.(txt|md|html|json|yaml|yml|php|twig|sh|bat)$" { url.access-deny = ("") } $HTTP["url"] =~ "^/grav_path/(\.(.*))" { diff --git a/webserver-configs/nginx.conf b/webserver-configs/nginx.conf index bbca7d3..fc7d06b 100644 --- a/webserver-configs/nginx.conf +++ b/webserver-configs/nginx.conf @@ -20,9 +20,9 @@ server { # deny all direct access for these folders location ~* /(\.git|cache|bin|logs|backup|tests)/.*$ { return 403; } # deny running scripts inside core system folders - location ~* /(system|vendor)/.*\.(txt|xml|md|html|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ { return 403; } + location ~* /(system|vendor)/.*\.(txt|xml|md|html|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ { return 403; } # deny running scripts inside user folder - location ~* /user/.*\.(txt|md|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ { return 403; } + location ~* /user/.*\.(txt|md|json|yaml|yml|php|pl|py|cgi|twig|sh|bat)$ { return 403; } # deny access to specific files in the root folder location ~ /(LICENSE\.txt|composer\.lock|composer\.json|nginx\.conf|web\.config|htaccess\.txt|\.htaccess) { return 403; } ## End - Security diff --git a/webserver-configs/web.config b/webserver-configs/web.config index 892ce92..1c351a3 100644 --- a/webserver-configs/web.config +++ b/webserver-configs/web.config @@ -18,7 +18,7 @@ - + @@ -26,11 +26,11 @@ - + - +