├── .editorconfig ├── .github ├── dependabot.yml └── workflows │ ├── check.yaml │ └── release.yaml ├── .gitignore ├── .htaccess ├── .vscode ├── extensions.json └── settings.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── SECURITY.md ├── backup └── .gitkeep ├── bin └── serve ├── cache └── .gitkeep ├── composer.json ├── composer.lock ├── formwork ├── .php-cs-fixer.php ├── .rector.php ├── bootstrap.php ├── config │ ├── site.yaml │ └── system.yaml ├── fields │ ├── array.php │ ├── checkbox.php │ ├── color.php │ ├── date.php │ ├── duration.php │ ├── dynamic │ │ └── vars.php │ ├── email.php │ ├── file.php │ ├── files.php │ ├── image.php │ ├── images.php │ ├── markdown.php │ ├── number.php │ ├── page.php │ ├── password.php │ ├── range.php │ ├── select.php │ ├── slug.php │ ├── tags.php │ ├── template.php │ ├── text.php │ ├── textarea.php │ ├── togglegroup.php │ └── upload.php ├── methods.php ├── phpstan-baseline.neon ├── phpstan.neon ├── routes.php ├── schemes │ └── config │ │ └── system.yaml ├── server.php ├── src │ ├── Assets │ │ ├── Asset.php │ │ ├── AssetCollection.php │ │ ├── Assets.php │ │ └── Exceptions │ │ │ └── AssetNotFoundException.php │ ├── Backup │ │ ├── Backupper.php │ │ └── Utils │ │ │ └── ZipErrors.php │ ├── Cache │ │ ├── AbstractCache.php │ │ ├── CacheItem.php │ │ ├── CacheItemInterface.php │ │ └── FilesCache.php │ ├── Cms │ │ ├── App.php │ │ └── Site.php │ ├── Commands │ │ └── ServeCommand.php │ ├── Config │ │ ├── Config.php │ │ └── Exceptions │ │ │ ├── ConfigResolutionException.php │ │ │ └── UnresolvedConfigException.php │ ├── Controllers │ │ ├── AbstractController.php │ │ ├── AssetsController.php │ │ ├── ErrorsController.php │ │ ├── ErrorsControllerInterface.php │ │ ├── FilesController.php │ │ └── PageController.php │ ├── Data │ │ ├── AbstractCollection.php │ │ ├── Collection.php │ │ ├── CollectionDataProxy.php │ │ ├── Contracts │ │ │ ├── ArraySerializable.php │ │ │ ├── Arrayable.php │ │ │ └── Paginable.php │ │ ├── Exceptions │ │ │ └── InvalidValueException.php │ │ ├── Pagination.php │ │ └── Traits │ │ │ ├── DataArrayable.php │ │ │ ├── DataCountable.php │ │ │ ├── DataCountableIterator.php │ │ │ ├── DataGetter.php │ │ │ ├── DataIterator.php │ │ │ ├── DataMultipleGetter.php │ │ │ ├── DataMultipleSetter.php │ │ │ └── DataSetter.php │ ├── Debug │ │ ├── CodeDumper.php │ │ └── Debug.php │ ├── Exceptions │ │ ├── RecursionException.php │ │ └── TranslatedException.php │ ├── Fields │ │ ├── Dynamic │ │ │ └── DynamicFieldValue.php │ │ ├── Exceptions │ │ │ └── ValidationException.php │ │ ├── Field.php │ │ ├── FieldCollection.php │ │ ├── FieldFactory.php │ │ └── Layout │ │ │ ├── Layout.php │ │ │ ├── Section.php │ │ │ └── SectionCollection.php │ ├── Files │ │ ├── Exceptions │ │ │ └── FileUriGenerationException.php │ │ ├── File.php │ │ ├── FileCollection.php │ │ ├── FileFactory.php │ │ ├── FileUriGenerator.php │ │ └── Services │ │ │ └── FileUploader.php │ ├── Http │ │ ├── Client.php │ │ ├── Exceptions │ │ │ └── ConnectionException.php │ │ ├── FileResponse.php │ │ ├── Files │ │ │ └── UploadedFile.php │ │ ├── FilesData.php │ │ ├── Header.php │ │ ├── HeadersData.php │ │ ├── JsonResponse.php │ │ ├── RedirectResponse.php │ │ ├── Request.php │ │ ├── RequestData.php │ │ ├── RequestMethod.php │ │ ├── RequestType.php │ │ ├── Response.php │ │ ├── ResponseHeaders.php │ │ ├── ResponseInterface.php │ │ ├── ResponseStatus.php │ │ ├── ResponseStatusType.php │ │ ├── ServerData.php │ │ ├── Session │ │ │ ├── MessageType.php │ │ │ ├── Messages.php │ │ │ └── Session.php │ │ └── Utils │ │ │ ├── Cookie.php │ │ │ ├── DeviceType.php │ │ │ ├── Header.php │ │ │ ├── IpAnonymizer.php │ │ │ └── Visitor.php │ ├── Images │ │ ├── ColorProfile │ │ │ ├── ColorProfile.php │ │ │ ├── ColorSpace.php │ │ │ ├── DeviceClass.php │ │ │ └── RenderingIntent.php │ │ ├── Decoder │ │ │ ├── DecoderInterface.php │ │ │ ├── GifDecoder.php │ │ │ ├── JpegDecoder.php │ │ │ ├── PngDecoder.php │ │ │ ├── SvgDecoder.php │ │ │ └── WebpDecoder.php │ │ ├── Exif │ │ │ ├── ExifData.php │ │ │ ├── ExifDateTime.php │ │ │ ├── ExifReader.php │ │ │ └── tables │ │ │ │ └── Exif.php │ │ ├── Handler │ │ │ ├── AbstractHandler.php │ │ │ ├── Exceptions │ │ │ │ └── UnsupportedFeatureException.php │ │ │ ├── GifHandler.php │ │ │ ├── HandlerInterface.php │ │ │ ├── JpegHandler.php │ │ │ ├── PngHandler.php │ │ │ ├── SvgHandler.php │ │ │ └── WebpHandler.php │ │ ├── Image.php │ │ ├── ImageFactory.php │ │ ├── ImageInfo.php │ │ └── Transform │ │ │ ├── AbstractTransform.php │ │ │ ├── Blur.php │ │ │ ├── BlurMode.php │ │ │ ├── Brightness.php │ │ │ ├── Colorize.php │ │ │ ├── Contrast.php │ │ │ ├── Crop.php │ │ │ ├── Desaturate.php │ │ │ ├── EdgeDetect.php │ │ │ ├── Emboss.php │ │ │ ├── Flip.php │ │ │ ├── FlipDirection.php │ │ │ ├── Invert.php │ │ │ ├── Pixelate.php │ │ │ ├── Resize.php │ │ │ ├── ResizeMode.php │ │ │ ├── Rotate.php │ │ │ ├── Scale.php │ │ │ ├── Sharpen.php │ │ │ ├── Smoothen.php │ │ │ ├── TransformCollection.php │ │ │ └── TransformInterface.php │ ├── Interpolator │ │ ├── Errors │ │ │ └── SyntaxError.php │ │ ├── Exceptions │ │ │ └── InterpolationException.php │ │ ├── Interpolator.php │ │ ├── NodeInterpolator.php │ │ ├── Nodes │ │ │ ├── AbstractNode.php │ │ │ ├── ArgumentsNode.php │ │ │ ├── ArrayKeysNode.php │ │ │ ├── ArrayNode.php │ │ │ ├── IdentifierNode.php │ │ │ ├── ImplicitArrayKeyNode.php │ │ │ ├── NumberNode.php │ │ │ └── StringNode.php │ │ ├── Parser.php │ │ ├── ParserInterface.php │ │ ├── Token.php │ │ ├── TokenStream.php │ │ ├── Tokenizer.php │ │ └── TokenizerInterface.php │ ├── Languages │ │ ├── Language.php │ │ ├── LanguageCodes.php │ │ ├── LanguageCollection.php │ │ ├── Languages.php │ │ └── LanguagesFactory.php │ ├── Log │ │ ├── Log.php │ │ └── Registry.php │ ├── Metadata │ │ ├── Metadata.php │ │ └── MetadataCollection.php │ ├── Model │ │ ├── Attributes │ │ │ └── ReadonlyModelProperty.php │ │ └── Model.php │ ├── Pages │ │ ├── ContentFile.php │ │ ├── Exceptions │ │ │ └── PageNotFoundException.php │ │ ├── Page.php │ │ ├── PageCollection.php │ │ ├── PageCollectionFactory.php │ │ ├── PageFactory.php │ │ ├── Pagination.php │ │ ├── PaginationFactory.php │ │ └── Traits │ │ │ ├── PageStatus.php │ │ │ ├── PageTraversal.php │ │ │ ├── PageUid.php │ │ │ ├── PageUri.php │ │ │ └── PaginationUri.php │ ├── Panel │ │ ├── ContentHistory │ │ │ ├── ContentHistory.php │ │ │ ├── ContentHistoryEvent.php │ │ │ ├── ContentHistoryItem.php │ │ │ └── ContentHistoryItemCollection.php │ │ ├── Controllers │ │ │ ├── AbstractController.php │ │ │ ├── AssetsController.php │ │ │ ├── AuthenticationController.php │ │ │ ├── BackupController.php │ │ │ ├── CacheController.php │ │ │ ├── DashboardController.php │ │ │ ├── ErrorsController.php │ │ │ ├── FilesController.php │ │ │ ├── OptionsController.php │ │ │ ├── PagesController.php │ │ │ ├── RegisterController.php │ │ │ ├── StatisticsController.php │ │ │ ├── ToolsController.php │ │ │ ├── UpdatesController.php │ │ │ └── UsersController.php │ │ ├── Modals │ │ │ ├── Modal.php │ │ │ ├── ModalButton.php │ │ │ ├── ModalButtonCollection.php │ │ │ ├── ModalCollection.php │ │ │ ├── ModalFactory.php │ │ │ └── Modals.php │ │ ├── Navigation │ │ │ ├── NavigationItem.php │ │ │ └── NavigationItemCollection.php │ │ ├── Panel.php │ │ ├── Security │ │ │ ├── AccessLimiter.php │ │ │ └── Password.php │ │ └── Utils │ │ │ └── DateFormats.php │ ├── Parsers │ │ ├── AbstractEncoder.php │ │ ├── AbstractParser.php │ │ ├── Extensions │ │ │ └── CommonMark │ │ │ │ ├── FormworkExtension.php │ │ │ │ ├── ImageAltProcessor.php │ │ │ │ ├── ImageRenderer.php │ │ │ │ └── LinkBaseProcessor.php │ │ ├── Json.php │ │ ├── Markdown.php │ │ ├── Php.php │ │ └── Yaml.php │ ├── Router │ │ ├── CompiledRoute.php │ │ ├── Exceptions │ │ │ ├── InvalidRouteException.php │ │ │ └── RouteNotFoundException.php │ │ ├── Route.php │ │ ├── RouteCollection.php │ │ ├── RouteFilter.php │ │ ├── RouteFilterCollection.php │ │ ├── RouteParams.php │ │ └── Router.php │ ├── Sanitizer │ │ ├── DomSanitizer.php │ │ ├── HtmlSanitizer.php │ │ ├── Parser │ │ │ ├── DomParserInterface.php │ │ │ ├── Html5Parser.php │ │ │ └── PhpDomParser.php │ │ ├── Reference │ │ │ ├── HtmlReference.php │ │ │ └── SvgReference.php │ │ ├── SanitizeElementsMethod.php │ │ └── SvgSanitizer.php │ ├── Schemes │ │ ├── Scheme.php │ │ ├── SchemeFactory.php │ │ ├── SchemeOptions.php │ │ └── Schemes.php │ ├── Security │ │ └── CsrfToken.php │ ├── Services │ │ ├── Container.php │ │ ├── Exceptions │ │ │ └── ServiceResolutionException.php │ │ ├── Loaders │ │ │ ├── ConfigServiceLoader.php │ │ │ ├── PanelServiceLoader.php │ │ │ ├── SchemesServiceLoader.php │ │ │ ├── SiteServiceLoader.php │ │ │ ├── TemplatesServiceLoader.php │ │ │ ├── TranslationsServiceLoader.php │ │ │ └── UsersServiceLoader.php │ │ ├── ResolutionAwareServiceLoaderInterface.php │ │ ├── ServiceDefinition.php │ │ └── ServiceLoaderInterface.php │ ├── Statistics │ │ └── Statistics.php │ ├── Templates │ │ ├── Template.php │ │ ├── TemplateCollection.php │ │ ├── TemplateFactory.php │ │ └── Templates.php │ ├── Traits │ │ ├── Methods.php │ │ ├── SingletonClass.php │ │ └── StaticClass.php │ ├── Translations │ │ ├── Translation.php │ │ └── Translations.php │ ├── Updater │ │ ├── SemVer.php │ │ └── Updater.php │ ├── Users │ │ ├── ColorScheme.php │ │ ├── Exceptions │ │ │ ├── AuthenticationFailedException.php │ │ │ ├── UserImageNotFoundException.php │ │ │ ├── UserNotFoundException.php │ │ │ └── UserNotLoggedException.php │ │ ├── Permissions.php │ │ ├── Role.php │ │ ├── RoleCollection.php │ │ ├── User.php │ │ ├── UserCollection.php │ │ ├── UserFactory.php │ │ └── Users.php │ ├── Utils │ │ ├── Arr.php │ │ ├── Constraint.php │ │ ├── Date.php │ │ ├── Exceptions │ │ │ ├── FileNotFoundException.php │ │ │ └── FileSystemException.php │ │ ├── FileSystem.php │ │ ├── Html.php │ │ ├── MimeType.php │ │ ├── Path.php │ │ ├── Str.php │ │ ├── Text.php │ │ ├── Uri.php │ │ └── scripts │ │ │ └── update-mime-types.php │ └── View │ │ ├── Exceptions │ │ └── RenderingException.php │ │ ├── Renderer.php │ │ ├── View.php │ │ └── ViewFactory.php ├── translations │ ├── de.yaml │ ├── en.yaml │ ├── es.yaml │ ├── fr.yaml │ ├── it.yaml │ ├── pl.yaml │ ├── pt.yaml │ ├── ru.yaml │ └── uk.yaml └── views │ └── errors │ ├── error.php │ ├── install.php │ ├── maintenance.php │ ├── panel │ └── assets.php │ ├── partials │ ├── debug.php │ ├── footer.php │ └── header.php │ └── phpversion.php ├── index.php ├── panel ├── .prettierrc ├── .stylelintrc.yaml ├── .yarnrc.yml ├── appconfig.php ├── assets │ ├── css │ │ └── .gitkeep │ ├── icons │ │ └── svg │ │ │ ├── arrow-arrow-left-right-up-down.svg │ │ │ ├── arrow-down.svg │ │ │ ├── arrow-left-circle.svg │ │ │ ├── arrow-left-down-right-up.svg │ │ │ ├── arrow-left-down.svg │ │ │ ├── arrow-left-right.svg │ │ │ ├── arrow-left-up-right-down.svg │ │ │ ├── arrow-left-up.svg │ │ │ ├── arrow-left.svg │ │ │ ├── arrow-right-circle.svg │ │ │ ├── arrow-right-down.svg │ │ │ ├── arrow-right-up-box.svg │ │ │ ├── arrow-right-up.svg │ │ │ ├── arrow-right.svg │ │ │ ├── arrow-up-down-left-right.svg │ │ │ ├── arrow-up-down.svg │ │ │ ├── arrow-up.svg │ │ │ ├── arrows-rotate-clockwise.svg │ │ │ ├── arrows-rotate-counterclockwise.svg │ │ │ ├── bars.svg │ │ │ ├── blockquote.svg │ │ │ ├── bold.svg │ │ │ ├── bolt.svg │ │ │ ├── cache-clear.svg │ │ │ ├── cache.svg │ │ │ ├── calendar-clock.svg │ │ │ ├── calendar.svg │ │ │ ├── camera-flash.svg │ │ │ ├── camera-metering-average.svg │ │ │ ├── camera-metering-evaluative.svg │ │ │ ├── camera-metering-partial.svg │ │ │ ├── camera-metering-spot.svg │ │ │ ├── camera-no-flash.svg │ │ │ ├── check-circle.svg │ │ │ ├── check-square.svg │ │ │ ├── check.svg │ │ │ ├── chevron-down.svg │ │ │ ├── chevron-left.svg │ │ │ ├── chevron-right.svg │ │ │ ├── chevron-up.svg │ │ │ ├── circle-small-fill.svg │ │ │ ├── circle-small.svg │ │ │ ├── circle.svg │ │ │ ├── clock-rotate-left.svg │ │ │ ├── clock-rotate-right.svg │ │ │ ├── clock.svg │ │ │ ├── cloud-download.svg │ │ │ ├── cloud-upload.svg │ │ │ ├── cloud.svg │ │ │ ├── code.svg │ │ │ ├── desktop.svg │ │ │ ├── ellipsis-h.svg │ │ │ ├── ellipsis-v.svg │ │ │ ├── exclamation-circle.svg │ │ │ ├── exclamation-octagon.svg │ │ │ ├── exclamation-square.svg │ │ │ ├── exclamation-triangle.svg │ │ │ ├── exclamation.svg │ │ │ ├── eye-slash.svg │ │ │ ├── eye.svg │ │ │ ├── file-archive.svg │ │ │ ├── file-backup.svg │ │ │ ├── file-binary.svg │ │ │ ├── file-document.svg │ │ │ ├── file-exclamation.svg │ │ │ ├── file-icons.svg │ │ │ ├── file-image.svg │ │ │ ├── file-list.svg │ │ │ ├── file-page.svg │ │ │ ├── file-pdf.svg │ │ │ ├── file-presentation.svg │ │ │ ├── file-spreadsheet.svg │ │ │ ├── file-text.svg │ │ │ ├── file-video.svg │ │ │ ├── file.svg │ │ │ ├── globe.svg │ │ │ ├── grabber.svg │ │ │ ├── home.svg │ │ │ ├── hourglass.svg │ │ │ ├── image.svg │ │ │ ├── indent-decrease.svg │ │ │ ├── indent-increase.svg │ │ │ ├── info-circle.svg │ │ │ ├── info-square.svg │ │ │ ├── info.svg │ │ │ ├── italic.svg │ │ │ ├── link.svg │ │ │ ├── list-ordered.svg │ │ │ ├── list-unordered.svg │ │ │ ├── list.svg │ │ │ ├── markdown.svg │ │ │ ├── minus-circle.svg │ │ │ ├── minus-square.svg │ │ │ ├── minus.svg │ │ │ ├── mobile.svg │ │ │ ├── movie.svg │ │ │ ├── octagon.svg │ │ │ ├── page-blank.svg │ │ │ ├── page-compile.svg │ │ │ ├── page-down.svg │ │ │ ├── page-error.svg │ │ │ ├── page-exclamation.svg │ │ │ ├── page-home.svg │ │ │ ├── page-listing.svg │ │ │ ├── page-up.svg │ │ │ ├── page.svg │ │ │ ├── pencil.svg │ │ │ ├── plus-circle.svg │ │ │ ├── plus-square.svg │ │ │ ├── plus.svg │ │ │ ├── quotes.svg │ │ │ ├── readmore.svg │ │ │ ├── reorder-v.svg │ │ │ ├── rotate-left.svg │ │ │ ├── rotate-right.svg │ │ │ ├── search.svg │ │ │ ├── section.svg │ │ │ ├── sitemap.svg │ │ │ ├── sparks.svg │ │ │ ├── square.svg │ │ │ ├── tablet.svg │ │ │ ├── tag.svg │ │ │ ├── template.svg │ │ │ ├── times-circle.svg │ │ │ ├── times-octagon.svg │ │ │ ├── times-square.svg │ │ │ ├── times.svg │ │ │ ├── translate.svg │ │ │ ├── trash.svg │ │ │ ├── triangle.svg │ │ │ ├── user-image-slash.svg │ │ │ ├── user-image.svg │ │ │ └── user.svg │ ├── images │ │ ├── icon.png │ │ ├── icon.svg │ │ └── user-image.svg │ └── js │ │ └── .gitkeep ├── eslint.config.js ├── logs │ └── .gitkeep ├── methods.php ├── modals │ ├── changes.yaml │ ├── deleteFile.yaml │ ├── deleteFileItem.yaml │ ├── deletePage.yaml │ ├── deleteUser.yaml │ ├── deleteUserImage.yaml │ ├── images.yaml │ ├── link.yaml │ ├── newPage.yaml │ ├── newUser.yaml │ ├── renameFile.yaml │ ├── renameFileItem.yaml │ └── uploadFile.yaml ├── navigation.php ├── package.json ├── routes.php ├── schemes │ └── forms │ │ └── register.yaml ├── src │ ├── scss │ │ ├── components │ │ │ ├── _animations.scss │ │ │ ├── _badges.scss │ │ │ ├── _base.scss │ │ │ ├── _buttons.scss │ │ │ ├── _caption.scss │ │ │ ├── _charts.scss │ │ │ ├── _colors.scss │ │ │ ├── _columns.scss │ │ │ ├── _dropdowns.scss │ │ │ ├── _errors.scss │ │ │ ├── _files-list.scss │ │ │ ├── _forms.scss │ │ │ ├── _functions.scss │ │ │ ├── _header.scss │ │ │ ├── _icon.scss │ │ │ ├── _login.scss │ │ │ ├── _logo.scss │ │ │ ├── _mixins.scss │ │ │ ├── _modals.scss │ │ │ ├── _notifications.scss │ │ │ ├── _options.scss │ │ │ ├── _pages-tree.scss │ │ │ ├── _pages.scss │ │ │ ├── _panel.scss │ │ │ ├── _section.scss │ │ │ ├── _sidebar.scss │ │ │ ├── _sortable.scss │ │ │ ├── _spinner.scss │ │ │ ├── _statistics.scss │ │ │ ├── _table.scss │ │ │ ├── _tabs.scss │ │ │ ├── _tooltip.scss │ │ │ ├── _typography.scss │ │ │ ├── _users.scss │ │ │ ├── _utils.scss │ │ │ ├── _variables.scss │ │ │ └── forms │ │ │ │ ├── _forms-array.scss │ │ │ │ ├── _forms-base.scss │ │ │ │ ├── _forms-checkbox.scss │ │ │ │ ├── _forms-color.scss │ │ │ │ ├── _forms-date.scss │ │ │ │ ├── _forms-duration.scss │ │ │ │ ├── _forms-editor.scss │ │ │ │ ├── _forms-image.scss │ │ │ │ ├── _forms-range.scss │ │ │ │ ├── _forms-tags.scss │ │ │ │ ├── _forms-togglegroup.scss │ │ │ │ └── _forms-upload.scss │ │ ├── panel.scss │ │ └── vendor │ │ │ └── chartist.css │ └── ts │ │ ├── app.ts │ │ ├── components │ │ ├── color-scheme.ts │ │ ├── dropdowns.ts │ │ ├── files.ts │ │ ├── fileslist.ts │ │ ├── form.ts │ │ ├── forms.ts │ │ ├── icons.ts │ │ ├── inputs │ │ │ ├── array-input.ts │ │ │ ├── color-input.ts │ │ │ ├── date-input.ts │ │ │ ├── duration-input.ts │ │ │ ├── editor-input.ts │ │ │ ├── editor │ │ │ │ ├── code │ │ │ │ │ ├── menu.ts │ │ │ │ │ └── view.ts │ │ │ │ └── markdown │ │ │ │ │ ├── commands.ts │ │ │ │ │ ├── inputrules.ts │ │ │ │ │ ├── keymap.ts │ │ │ │ │ ├── linktooltip.ts │ │ │ │ │ ├── menu.ts │ │ │ │ │ └── view.ts │ │ │ ├── image-picker.ts │ │ │ ├── range-input.ts │ │ │ ├── select-input.ts │ │ │ ├── slug-input.ts │ │ │ ├── tags-input.ts │ │ │ ├── togglegroup-input.ts │ │ │ └── upload-input.ts │ │ ├── modal.ts │ │ ├── modals.ts │ │ ├── navigation.ts │ │ ├── notification.ts │ │ ├── notifications.ts │ │ ├── sections.ts │ │ ├── statistics-chart.ts │ │ ├── tooltip.ts │ │ ├── tooltips.ts │ │ └── views │ │ │ ├── backups.ts │ │ │ ├── dashboard.ts │ │ │ ├── pages.ts │ │ │ ├── statistics.ts │ │ │ └── updates.ts │ │ ├── polyfills │ │ └── request-submit.ts │ │ └── utils │ │ ├── arrays.ts │ │ ├── cookies.ts │ │ ├── dimensions.ts │ │ ├── events.ts │ │ ├── forms.ts │ │ ├── math.ts │ │ ├── numbers.ts │ │ ├── request.ts │ │ ├── selectors.ts │ │ └── validation.ts ├── translations │ ├── de.yaml │ ├── en.yaml │ ├── es.yaml │ ├── fr.yaml │ ├── it.yaml │ ├── pl.yaml │ ├── pt.yaml │ ├── ru.yaml │ └── uk.yaml ├── tsconfig.json ├── views │ ├── authentication │ │ └── login.php │ ├── dashboard │ │ └── index.php │ ├── errors │ │ └── error.php │ ├── fields.php │ ├── fields │ │ ├── array.php │ │ ├── checkbox.php │ │ ├── color.php │ │ ├── date.php │ │ ├── duration.php │ │ ├── email.php │ │ ├── file.php │ │ ├── files.php │ │ ├── image.php │ │ ├── images.php │ │ ├── layout │ │ │ ├── default.php │ │ │ └── sections.php │ │ ├── markdown.php │ │ ├── number.php │ │ ├── page.php │ │ ├── page │ │ │ └── imagepicker.php │ │ ├── partials │ │ │ ├── description.php │ │ │ └── label.php │ │ ├── password.php │ │ ├── range.php │ │ ├── select.php │ │ ├── slug.php │ │ ├── tabs.php │ │ ├── tags.php │ │ ├── template.php │ │ ├── text.php │ │ ├── textarea.php │ │ ├── togglegroup.php │ │ └── upload.php │ ├── files │ │ ├── edit.php │ │ └── index.php │ ├── layouts │ │ ├── fields │ │ │ └── field.php │ │ ├── login.php │ │ └── panel.php │ ├── modals │ │ └── modal.php │ ├── options │ │ ├── site.php │ │ ├── system.php │ │ └── tabs.php │ ├── pages │ │ ├── editor.php │ │ ├── index.php │ │ └── tree.php │ ├── partials │ │ ├── files │ │ │ ├── file │ │ │ │ ├── item.php │ │ │ │ └── list.php │ │ │ └── images │ │ │ │ ├── exif │ │ │ │ └── data.php │ │ │ │ ├── info │ │ │ │ ├── exif.php │ │ │ │ └── info.php │ │ │ │ └── position │ │ │ │ └── map.php │ │ ├── login │ │ │ └── notification.php │ │ ├── pages │ │ │ ├── info.php │ │ │ └── status.php │ │ ├── scripts.php │ │ └── sidebar.php │ ├── register │ │ └── register.php │ ├── statistics │ │ └── index.php │ ├── tools │ │ ├── backups.php │ │ ├── info.php │ │ ├── tabs.php │ │ └── updates.php │ └── users │ │ ├── index.php │ │ └── profile.php └── yarn.lock └── site ├── config ├── site.yaml └── system.yaml ├── files └── .gitkeep ├── pages ├── 1-about │ └── page.md ├── 2-blog │ ├── 20180615-hello-world │ │ └── post.md │ ├── 20180616-another-blog-post │ │ └── post.md │ └── blog.md ├── 3-typography │ └── page.md ├── error │ └── page.md └── index │ ├── formwork.png │ ├── formwork.png.meta.yaml │ ├── page.md │ ├── panel.png │ └── panel.png.meta.yaml ├── schemes ├── config │ └── site.yaml ├── files │ ├── file.yaml │ └── image.yaml ├── pages │ ├── blog.yaml │ ├── default.yaml │ ├── page.yaml │ └── post.yaml └── users │ └── user.yaml ├── statistics └── .gitkeep ├── templates ├── assets │ ├── css │ │ ├── style.css │ │ └── style.min.css │ └── js │ │ ├── script.js │ │ └── script.min.js ├── blog.php ├── controllers │ └── blog.php ├── default.php ├── layouts │ └── site.php ├── page.php ├── partials │ ├── cover-image.php │ ├── menu.php │ ├── meta.php │ ├── pagination.php │ └── tags.php └── post.php ├── translations └── .gitkeep └── users ├── accounts └── .gitkeep ├── images └── .gitkeep └── roles ├── admin.yaml └── user.yaml /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | max_line_length = off 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | insert_final_newline = false 15 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "composer" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | ignore: 8 | - dependency-name: "*" 9 | update-types: ["version-update:semver-major"] 10 | 11 | - package-ecosystem: "npm" 12 | directory: "/panel" 13 | schedule: 14 | interval: "monthly" 15 | ignore: 16 | - dependency-name: "*" 17 | update-types: ["version-update:semver-major"] 18 | -------------------------------------------------------------------------------- /.github/workflows/check.yaml: -------------------------------------------------------------------------------- 1 | name: Check 2 | 3 | on: 4 | push: 5 | branches: 6 | - 2.x 7 | pull_request: 8 | branches: 9 | - 2.x 10 | 11 | permissions: 12 | contents: read 13 | 14 | jobs: 15 | check: 16 | 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - name: Setup PHP 21 | uses: shivammathur/setup-php@v2 22 | with: 23 | php-version: '8.3' 24 | tools: composer 25 | 26 | - uses: actions/checkout@v4 27 | 28 | - name: Validate composer.json and composer.lock 29 | run: composer validate --strict 30 | 31 | - name: Install dependencies 32 | run: composer install 33 | 34 | - name: Check coding style 35 | run: composer fix:check 36 | 37 | - name: Run PHPStan 38 | run: composer phpstan 39 | 40 | - name: Run Rector 41 | run: composer rector:check 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .php-cs-fixer.cache 3 | 4 | /backup/* 5 | /cache/* 6 | /logs/* 7 | /vendor/* 8 | 9 | /panel/.yarn/* 10 | /panel/assets/css/* 11 | /panel/assets/js/* 12 | /panel/logs/* 13 | /panel/node_modules/* 14 | 15 | /site/statistics/* 16 | /site/users/accounts/* 17 | /site/users/images/* 18 | 19 | !.gitkeep 20 | -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | AddDefaultCharset utf-8 2 | 3 | 4 | AddCharset utf-8 .css .js .svg 5 | 6 | 7 | 8 | ## Enable rewrite rules 9 | RewriteEngine on 10 | 11 | ## Let all URI be processed by index.php 12 | RewriteCond %{REQUEST_FILENAME} !-f 13 | RewriteRule ^.* index.php [L] 14 | 15 | ## Prevent direct access to Formwork folders 16 | RewriteRule ^(panel|backup|bin|cache|formwork|site|vendor)/.* index.php [L,NC] 17 | 18 | ## Prevent access to specific files 19 | RewriteRule ^(.*)\.(md|yml|yaml|json|neon)/?$ index.php [L,NC] 20 | RewriteRule ^(LICENSE|composer\.lock)/?$ index.php [L,NC] 21 | RewriteRule (^|/)\.(?!well-known)/? index.php [L,NC] 22 | 23 | 24 | ## Disable access to directory indexes 25 | Options -Indexes 26 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "bmewburn.vscode-intelephense-client", 4 | "dbaeumer.vscode-eslint", 5 | "editorconfig.editorconfig", 6 | "esbenp.prettier-vscode", 7 | "stylelint.vscode-stylelint" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.workingDirectories": ["./panel"], 3 | "eslint.experimental.useFlatConfig": true, 4 | "prettier.tabWidth": 4, 5 | "prettier.useEditorConfig": true, 6 | "prettier.embeddedLanguageFormatting": "off", 7 | "stylelint.configBasedir": "./panel", 8 | } 9 | -------------------------------------------------------------------------------- /backup/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getformwork/formwork/bb179db76b6b2ef14ccc69c8575304e816c535a9/backup/.gitkeep -------------------------------------------------------------------------------- /bin/serve: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | start(); 9 | -------------------------------------------------------------------------------- /cache/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getformwork/formwork/bb179db76b6b2ef14ccc69c8575304e816c535a9/cache/.gitkeep -------------------------------------------------------------------------------- /formwork/bootstrap.php: -------------------------------------------------------------------------------- 1 | =')) { 5 | require __DIR__ . '/views/errors/phpversion.php'; 6 | exit; 7 | } 8 | 9 | // Check if Composer autoloader is available 10 | if (file_exists(ROOT_PATH . '/vendor/autoload.php')) { 11 | require ROOT_PATH . '/vendor/autoload.php'; 12 | } else { 13 | require __DIR__ . '/views/errors/install.php'; 14 | exit; 15 | } 16 | -------------------------------------------------------------------------------- /formwork/config/site.yaml: -------------------------------------------------------------------------------- 1 | author: '' 2 | 3 | languages: 4 | available: [] 5 | httpPreferred: false 6 | 7 | defaultTemplate: page 8 | 9 | description: '' 10 | 11 | maintenance: 12 | enabled: false 13 | page: null 14 | 15 | metadata: [] 16 | 17 | path: '${%ROOT_PATH%}/site' 18 | 19 | routeAliases: [] 20 | 21 | statistics: 22 | enabled: true 23 | trackLocalhost: false 24 | visitsDelay: 15 25 | path: '${site.path}/statistics' 26 | registries: 27 | sessions: 'sessions.json' 28 | visits: 'visits.json' 29 | uniqueVisits: 'uniqueVisits.json' 30 | visitors: 'visitors.json' 31 | pageViews: 'pageViews.json' 32 | sources: 'sources.json' 33 | devices: 'devices.json' 34 | cleanup: 35 | ttl: 86400 36 | probability: 5 37 | -------------------------------------------------------------------------------- /formwork/fields/checkbox.php: -------------------------------------------------------------------------------- 1 | function (Field $field, $value) { 11 | if (Constraint::isTruthy($value)) { 12 | return true; 13 | } 14 | 15 | if (Constraint::isFalsy($value)) { 16 | return false; 17 | } 18 | 19 | if ($value === null) { 20 | return false; 21 | } 22 | 23 | throw new ValidationException(sprintf('Invalid value for field "%s" of type "%s"', $field->name(), $field->type())); 24 | }, 25 | ]; 26 | }; 27 | -------------------------------------------------------------------------------- /formwork/fields/color.php: -------------------------------------------------------------------------------- 1 | function (Field $field, $value) { 11 | if (!Constraint::matchesRegex($value, '/^#[0-9A-Fa-f]{6}$/')) { 12 | throw new ValidationException(sprintf('Invalid value for field "%s" of type "%s"', $field->name(), $field->type())); 13 | } 14 | 15 | return strtolower($value); 16 | }, 17 | ]; 18 | }; 19 | -------------------------------------------------------------------------------- /formwork/fields/dynamic/vars.php: -------------------------------------------------------------------------------- 1 | $app, 11 | 12 | 'site' => $app->site(), 13 | 14 | 'dateFormats' => [ 15 | 'date' => DateFormats::date(), 16 | 'hour' => DateFormats::hour(), 17 | 'timezones' => DateFormats::timezones(), 18 | ], 19 | 20 | 'languages' => [ 21 | 'names' => LanguageCodes::names(), 22 | ], 23 | 24 | 'mimeTypes' => [ 25 | 'getExtensionTypes' => MimeType::extensionTypes(...), 26 | ], 27 | ]; 28 | }; 29 | -------------------------------------------------------------------------------- /formwork/fields/template.php: -------------------------------------------------------------------------------- 1 | function (Field $field) use ($site) { 9 | return $site->templates()->get($field->value()); 10 | }, 11 | 12 | 'validate' => function (Field $field, $value) { 13 | if ($value === '') { 14 | return null; 15 | } 16 | 17 | return $value; 18 | }, 19 | ]; 20 | }; 21 | -------------------------------------------------------------------------------- /formwork/fields/togglegroup.php: -------------------------------------------------------------------------------- 1 | function (Field $field, $value) { 10 | if (Constraint::isTruthy($value)) { 11 | return true; 12 | } 13 | 14 | if (Constraint::isFalsy($value)) { 15 | return false; 16 | } 17 | 18 | if (is_numeric($value)) { 19 | // This reliably casts numeric values to int or float 20 | return $value + 0; 21 | } 22 | 23 | return $value; 24 | }, 25 | ]; 26 | }; 27 | -------------------------------------------------------------------------------- /formwork/phpstan-baseline.neon: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getformwork/formwork/bb179db76b6b2ef14ccc69c8575304e816c535a9/formwork/phpstan-baseline.neon -------------------------------------------------------------------------------- /formwork/phpstan.neon: -------------------------------------------------------------------------------- 1 | includes: 2 | - phpstan-baseline.neon 3 | - phar://phpstan.phar/conf/bleedingEdge.neon 4 | parameters: 5 | level: 8 6 | paths: 7 | - ../ 8 | excludePaths: 9 | - views 10 | - ../panel/node_modules(?) 11 | - ../panel/views 12 | - ../site/templates 13 | - ../vendor 14 | scanFiles: 15 | - ../index.php 16 | ignoreErrors: 17 | - '#^Call to an undefined method Formwork\\Data\\CollectionDataProxy\:\:#' 18 | - '#^Call to an undefined method Formwork\\Fields\\Field\:\:#' 19 | - '#^Call to an undefined method Formwork\\Pages\\Page\:\:#' 20 | - '#^Call to an undefined method Formwork\\Cms\\Site\:\:#' 21 | - '#^Call to an undefined method Formwork\\Users\\User\:\:#' 22 | -------------------------------------------------------------------------------- /formwork/src/Assets/Exceptions/AssetNotFoundException.php: -------------------------------------------------------------------------------- 1 | config->get('system.files.paths.site'), $routeParams->get('name')); 18 | 19 | if (FileSystem::isFile($path, assertExists: false)) { 20 | return new FileResponse($path); 21 | } 22 | 23 | return $this->forward(PageController::class, 'error'); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /formwork/src/Data/Contracts/ArraySerializable.php: -------------------------------------------------------------------------------- 1 | $data 11 | */ 12 | public static function fromArray(array $data): self; 13 | } 14 | -------------------------------------------------------------------------------- /formwork/src/Data/Contracts/Arrayable.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | public function toArray(): array; 13 | } 14 | -------------------------------------------------------------------------------- /formwork/src/Data/Contracts/Paginable.php: -------------------------------------------------------------------------------- 1 | identifier; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /formwork/src/Data/Traits/DataArrayable.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | protected array $data = []; 16 | 17 | public function toArray(): array 18 | { 19 | return $this->data; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /formwork/src/Data/Traits/DataCountable.php: -------------------------------------------------------------------------------- 1 | data); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /formwork/src/Data/Traits/DataCountableIterator.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | protected array $data = []; 13 | 14 | /** 15 | * Return whether a key is present 16 | */ 17 | public function has(string $key): bool 18 | { 19 | return Arr::has($this->data, $key); 20 | } 21 | 22 | /** 23 | * Get data by key returning a default value if key is not present 24 | */ 25 | public function get(string $key, mixed $default = null): mixed 26 | { 27 | return Arr::get($this->data, $key, $default); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /formwork/src/Data/Traits/DataIterator.php: -------------------------------------------------------------------------------- 1 | data); 17 | } 18 | 19 | public function current(): mixed 20 | { 21 | return current($this->data); 22 | } 23 | 24 | /** 25 | * @return int|string|null 26 | */ 27 | public function key(): mixed 28 | { 29 | return key($this->data); 30 | } 31 | 32 | public function next(): void 33 | { 34 | next($this->data); 35 | } 36 | 37 | public function valid(): bool 38 | { 39 | return $this->key() !== null; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /formwork/src/Data/Traits/DataMultipleSetter.php: -------------------------------------------------------------------------------- 1 | $keysAndValues 13 | */ 14 | public function setMultiple(array $keysAndValues): void 15 | { 16 | foreach ($keysAndValues as $key => $value) { 17 | $this->set($key, $value); 18 | } 19 | } 20 | 21 | /** 22 | * Remove multiple values 23 | * 24 | * @param list $keys 25 | */ 26 | public function removeMultiple(array $keys): void 27 | { 28 | foreach ($keys as $key) { 29 | $this->remove($key); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /formwork/src/Data/Traits/DataSetter.php: -------------------------------------------------------------------------------- 1 | 11 | */ 12 | protected array $data = []; 13 | 14 | /** 15 | * Set a data value by key 16 | */ 17 | public function set(string $key, mixed $value): void 18 | { 19 | Arr::set($this->data, $key, $value); 20 | } 21 | 22 | /** 23 | * Remove a data value by key 24 | */ 25 | public function remove(string $key): void 26 | { 27 | Arr::remove($this->data, $key); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /formwork/src/Exceptions/RecursionException.php: -------------------------------------------------------------------------------- 1 | languageString; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /formwork/src/Fields/Exceptions/ValidationException.php: -------------------------------------------------------------------------------- 1 | > $sections 17 | */ 18 | public function __construct(array $sections, Translation $translation) 19 | { 20 | parent::__construct(Arr::map($sections, fn($section) => new Section($section, $translation))); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /formwork/src/Files/Exceptions/FileUriGenerationException.php: -------------------------------------------------------------------------------- 1 | |list $data 16 | */ 17 | public function __construct(array $data = []) 18 | { 19 | if (!Arr::isAssociative($data)) { 20 | $data = Arr::mapKeys($data, fn($key, File $file) => $file->name()); 21 | } 22 | 23 | parent::__construct($data); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /formwork/src/Http/Exceptions/ConnectionException.php: -------------------------------------------------------------------------------- 1 | getAll(), fn(UploadedFile $uploadedFile) => $uploadedFile->isEmpty()); 19 | } 20 | 21 | /** 22 | * Get all uploaded files 23 | * 24 | * @return array 25 | */ 26 | public function getAll(): array 27 | { 28 | return Arr::flatten($this->data); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /formwork/src/Http/HeadersData.php: -------------------------------------------------------------------------------- 1 | $data 9 | */ 10 | public function __construct(array $data) 11 | { 12 | $this->initialize($data); 13 | } 14 | 15 | /** 16 | * Initialize headers data 17 | * 18 | * @param array $headers 19 | */ 20 | protected function initialize(array $headers): void 21 | { 22 | $this->data = Header::fixHeaderNames($headers); 23 | ksort($this->data); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /formwork/src/Http/RedirectResponse.php: -------------------------------------------------------------------------------- 1 | $uri, 11 | ]; 12 | parent::__construct('', $responseStatus, $headers); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /formwork/src/Http/RequestData.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class RequestData implements Arrayable, Countable, Iterator 16 | { 17 | use DataArrayable; 18 | use DataCountableIterator; 19 | use DataMultipleGetter; 20 | 21 | /** 22 | * @param array $data 23 | */ 24 | public function __construct(array $data) 25 | { 26 | $this->data = $data; 27 | } 28 | 29 | /** 30 | * Return whether data is present 31 | */ 32 | public function isEmpty(): bool 33 | { 34 | return count($this) === 0; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /formwork/src/Http/RequestMethod.php: -------------------------------------------------------------------------------- 1 | 16 | */ 17 | public function getHeaders(): array 18 | { 19 | $headers = Arr::filter($this->data, function ($value, $key) { 20 | switch (true) { 21 | case Str::startsWith($key, 'HTTP_'): 22 | case in_array($key, ['CONTENT_TYPE', 'CONTENT_LENGTH']): 23 | return true; 24 | 25 | default: 26 | return false; 27 | } 28 | }); 29 | 30 | return Arr::mapKeys($headers, fn($key) => Str::after($key, 'HTTP_')); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /formwork/src/Http/Session/MessageType.php: -------------------------------------------------------------------------------- 1 | $options 17 | */ 18 | public function make(string $path, array $options = []): Image 19 | { 20 | /** 21 | * @var array 22 | */ 23 | $defaults = $this->config->get('system.images', []); 24 | 25 | return new Image($path, [...$defaults, ...$options]); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /formwork/src/Images/Transform/AbstractTransform.php: -------------------------------------------------------------------------------- 1 | getProperties() as $property) { 16 | $data[$property->getName()] = $property->getValue($this); 17 | } 18 | 19 | return $data; 20 | } 21 | 22 | public function getSpecifier(): string 23 | { 24 | $arguments = []; 25 | 26 | foreach ($this->toArray() as $key => $value) { 27 | $arguments[] = $key . ': ' . Php::encode($value); 28 | } 29 | 30 | return Str::afterLast(static::class, '\\') . '(' . implode(', ', $arguments) . ')'; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /formwork/src/Images/Transform/BlurMode.php: -------------------------------------------------------------------------------- 1 | amount); 28 | return $gdImage; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /formwork/src/Images/Transform/Desaturate.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | private const array DIRECTIONS = [ 16 | 'Horizontal' => IMG_FLIP_HORIZONTAL, 17 | 'Vertical' => IMG_FLIP_VERTICAL, 18 | 'Both' => IMG_FLIP_BOTH, 19 | ]; 20 | 21 | public function __construct( 22 | private FlipDirection $flipDirection, 23 | ) {} 24 | 25 | public static function fromArray(array $data): self 26 | { 27 | return new self($data['direction']); 28 | } 29 | 30 | public function apply(GdImage $gdImage, ImageInfo $imageInfo): GdImage 31 | { 32 | imageflip($gdImage, self::DIRECTIONS[$this->flipDirection->name]); 33 | return $gdImage; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /formwork/src/Images/Transform/FlipDirection.php: -------------------------------------------------------------------------------- 1 | amount); 22 | return $gdImage; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /formwork/src/Images/Transform/ResizeMode.php: -------------------------------------------------------------------------------- 1 | angle, $backgroundColor) 26 | ?: throw new RuntimeException('Cannot rotate image'); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /formwork/src/Images/Transform/Sharpen.php: -------------------------------------------------------------------------------- 1 | everyItem()->getSpecifier()->values()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /formwork/src/Images/Transform/TransformInterface.php: -------------------------------------------------------------------------------- 1 | $vars 11 | */ 12 | public static function interpolate(string $string, array $vars): mixed 13 | { 14 | $nodeInterpolator = new NodeInterpolator(Parser::parseTokenStream(Tokenizer::tokenizeString($string)), $vars); 15 | return $nodeInterpolator->interpolate(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /formwork/src/Interpolator/Nodes/AbstractNode.php: -------------------------------------------------------------------------------- 1 | value; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /formwork/src/Interpolator/Nodes/ArgumentsNode.php: -------------------------------------------------------------------------------- 1 | $value 11 | */ 12 | public function __construct(array $value) 13 | { 14 | $this->value = $value; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /formwork/src/Interpolator/Nodes/ArrayKeysNode.php: -------------------------------------------------------------------------------- 1 | $value 11 | */ 12 | public function __construct(array $value) 13 | { 14 | $this->value = $value; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /formwork/src/Interpolator/Nodes/ArrayNode.php: -------------------------------------------------------------------------------- 1 | $value 11 | */ 12 | public function __construct( 13 | array $value, 14 | protected ArrayKeysNode $arrayKeysNode, 15 | ) { 16 | $this->value = $value; 17 | } 18 | 19 | /** 20 | * Get the array keys node 21 | */ 22 | public function keys(): ArrayKeysNode 23 | { 24 | return $this->arrayKeysNode; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /formwork/src/Interpolator/Nodes/IdentifierNode.php: -------------------------------------------------------------------------------- 1 | value = $value; 15 | } 16 | 17 | /** 18 | * Return node arguments 19 | */ 20 | public function arguments(): ?ArgumentsNode 21 | { 22 | return $this->argumentsNode; 23 | } 24 | 25 | /** 26 | * Return the node used to traverse 27 | */ 28 | public function traverse(): ?AbstractNode 29 | { 30 | return $this->node; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /formwork/src/Interpolator/Nodes/ImplicitArrayKeyNode.php: -------------------------------------------------------------------------------- 1 | value = $value; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /formwork/src/Interpolator/Nodes/StringNode.php: -------------------------------------------------------------------------------- 1 | value = $value; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /formwork/src/Interpolator/ParserInterface.php: -------------------------------------------------------------------------------- 1 | $data 16 | */ 17 | public function __construct(array $data) 18 | { 19 | parent::__construct(Arr::fromEntries(Arr::map($data, fn(string $code) => [$code, new Language($code)]))); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /formwork/src/Log/Log.php: -------------------------------------------------------------------------------- 1 | set($timestamp, $message); 23 | return $timestamp; 24 | } 25 | 26 | public function save(): void 27 | { 28 | if (count($this->storage) > $this->limit) { 29 | $this->storage = array_slice($this->storage, -$this->limit, null, true); 30 | } 31 | parent::save(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /formwork/src/Metadata/MetadataCollection.php: -------------------------------------------------------------------------------- 1 | $data 17 | */ 18 | public function __construct(array $data) 19 | { 20 | parent::__construct(); 21 | $this->setMultiple($data); 22 | } 23 | 24 | /** 25 | * Set a metadata 26 | * 27 | * @param string $value 28 | */ 29 | public function set(string $key, $value): void 30 | { 31 | $this->data[$key] = new Metadata($key, $value); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /formwork/src/Model/Attributes/ReadonlyModelProperty.php: -------------------------------------------------------------------------------- 1 | $data 15 | */ 16 | public function make(array $data): PageCollection 17 | { 18 | return new PageCollection($data, $this->paginationFactory); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /formwork/src/Pages/PageFactory.php: -------------------------------------------------------------------------------- 1 | $data 17 | */ 18 | public function make(array $data = []): Page 19 | { 20 | return $this->container->build(Page::class, ['data' => $data]); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /formwork/src/Pages/Pagination.php: -------------------------------------------------------------------------------- 1 | app->site(), $this->router); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /formwork/src/Pages/Traits/PageUid.php: -------------------------------------------------------------------------------- 1 | uid)) { 25 | return $this->uid; 26 | } 27 | 28 | $id = $this->contentRelativePath() ?: spl_object_hash($this); 29 | 30 | return $this->uid = Str::chunk(substr(hash('sha256', (string) $id), 0, 32), 8, '-'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /formwork/src/Panel/ContentHistory/ContentHistoryEvent.php: -------------------------------------------------------------------------------- 1 | config->get('system.panel.paths.modals'), $id . '.yaml'); 25 | 26 | $data = FileSystem::exists($path) ? Yaml::parseFile($path) : []; 27 | 28 | return new Modal($id, $data, $this->translations->getCurrent(), $this->fieldFactory); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /formwork/src/Panel/Modals/Modals.php: -------------------------------------------------------------------------------- 1 | has($name)) { 19 | $this->set($name, $this->modalFactory->make($name)); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /formwork/src/Panel/Navigation/NavigationItemCollection.php: -------------------------------------------------------------------------------- 1 | $options 13 | */ 14 | abstract public static function encode(mixed $data, array $options = []): string; 15 | 16 | /** 17 | * Encode an array of data to a given file 18 | * 19 | * @param array $options 20 | */ 21 | public static function encodeToFile(mixed $data, string $file, array $options = []): bool 22 | { 23 | return FileSystem::write($file, static::encode($data, $options)); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /formwork/src/Parsers/AbstractParser.php: -------------------------------------------------------------------------------- 1 | $options 16 | */ 17 | abstract public static function parse(string $input, array $options = []): mixed; 18 | 19 | /** 20 | * Parse file contents 21 | * 22 | * @param array $options 23 | */ 24 | public static function parseFile(string $file, array $options = []): mixed 25 | { 26 | return static::parse(FileSystem::read($file), $options); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /formwork/src/Parsers/Yaml.php: -------------------------------------------------------------------------------- 1 | $options 13 | * 14 | * @return array 15 | */ 16 | public static function parse(string $input, array $options = []): array 17 | { 18 | return (array) SymfonyYaml::parse($input); 19 | } 20 | 21 | /** 22 | * Encode data to YAML format 23 | * 24 | * @param array $options 25 | */ 26 | public static function encode(mixed $data, array $options = []): string 27 | { 28 | if (empty($data)) { 29 | return ''; 30 | } 31 | return SymfonyYaml::dump($data, inline: 4); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /formwork/src/Router/CompiledRoute.php: -------------------------------------------------------------------------------- 1 | $params 9 | */ 10 | public function __construct( 11 | protected string $path, 12 | protected string $regex, 13 | protected array $params, 14 | ) {} 15 | 16 | /** 17 | * Get route path 18 | */ 19 | public function path(): string 20 | { 21 | return $this->path; 22 | } 23 | 24 | /** 25 | * Get compiled route regex 26 | */ 27 | public function regex(): string 28 | { 29 | return $this->regex; 30 | } 31 | 32 | /** 33 | * Get route params 34 | * 35 | * @return list 36 | */ 37 | public function params(): array 38 | { 39 | return $this->params; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /formwork/src/Router/Exceptions/InvalidRouteException.php: -------------------------------------------------------------------------------- 1 | set($route->getName(), $route); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /formwork/src/Router/RouteFilterCollection.php: -------------------------------------------------------------------------------- 1 | set($filter->getName(), $filter); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /formwork/src/Router/RouteParams.php: -------------------------------------------------------------------------------- 1 | $data 16 | */ 17 | public function __construct(array $data) 18 | { 19 | $this->data = $data; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /formwork/src/Sanitizer/HtmlSanitizer.php: -------------------------------------------------------------------------------- 1 | 17 | */ 18 | protected array $allowedUriSchemes = ['http', 'https', 'mailto']; 19 | 20 | /** 21 | * @var array> 22 | */ 23 | protected array $elementSanitizers = [ 24 | 'svg' => SvgSanitizer::class, 25 | ]; 26 | } 27 | -------------------------------------------------------------------------------- /formwork/src/Sanitizer/Parser/DomParserInterface.php: -------------------------------------------------------------------------------- 1 | dom = new HTML5(); 16 | } 17 | 18 | public function parse(string $string): ?DOMDocumentFragment 19 | { 20 | return $this->dom->loadHTMLFragment($string); 21 | } 22 | 23 | public function serialize(?DOMNode $domNode = null): string 24 | { 25 | return $this->dom->saveHTML($domNode); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /formwork/src/Sanitizer/SanitizeElementsMethod.php: -------------------------------------------------------------------------------- 1 | $data 17 | */ 18 | public function make(string $id, array $data = []): Scheme 19 | { 20 | return $this->container->build(Scheme::class, compact('id', 'data')); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /formwork/src/Schemes/SchemeOptions.php: -------------------------------------------------------------------------------- 1 | $data 16 | */ 17 | public function __construct(array $data) 18 | { 19 | $this->data = $data; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /formwork/src/Services/Exceptions/ServiceResolutionException.php: -------------------------------------------------------------------------------- 1 | loadFromPath(SYSTEM_PATH . '/config/', defaultConfig: true); 16 | $config->loadFromPath(ROOT_PATH . '/site/config/'); 17 | 18 | $config->resolve([ 19 | '%ROOT_PATH%' => ROOT_PATH, 20 | '%SYSTEM_PATH%' => SYSTEM_PATH, 21 | ]); 22 | 23 | date_default_timezone_set($config->get('system.date.timezone')); 24 | 25 | return $config; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /formwork/src/Services/Loaders/SiteServiceLoader.php: -------------------------------------------------------------------------------- 1 | config->get('site'); 19 | 20 | return $container->build(Site::class, ['data' => [ 21 | ...$config, 22 | 'contentPath' => $this->config->get('system.pages.path'), 23 | ]]); 24 | } 25 | 26 | /** 27 | * @param Site $service 28 | */ 29 | public function onResolved(object $service, Container $container): void 30 | { 31 | $service->load(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /formwork/src/Services/ResolutionAwareServiceLoaderInterface.php: -------------------------------------------------------------------------------- 1 | 'light', 18 | self::Dark => 'dark', 19 | self::Auto => 'light dark', 20 | }; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /formwork/src/Users/Exceptions/AuthenticationFailedException.php: -------------------------------------------------------------------------------- 1 | id; 23 | } 24 | 25 | /** 26 | * Return role title 27 | */ 28 | public function title(): string 29 | { 30 | return Str::interpolate($this->title, fn($key) => $this->translations->getCurrent()->translate($key)); 31 | } 32 | 33 | /** 34 | * Return image path 35 | */ 36 | public function permissions(): Permissions 37 | { 38 | return $this->permissions; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /formwork/src/Users/RoleCollection.php: -------------------------------------------------------------------------------- 1 | $data 17 | */ 18 | public function make(array $data, Role $role): User 19 | { 20 | return $this->container->build(User::class, compact('data', 'role')); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /formwork/src/Users/Users.php: -------------------------------------------------------------------------------- 1 | insert('errors.partials.header') ?> 2 |

Oops, something went wrong!

3 |

Formwork encountered an error while serving your request.
If you are the maintainer of this site, please check Formwork configuration or the server log for errors.

4 |

Report an issue to GitHub

5 | 6 | insert('errors.partials.debug') ?> 7 | 8 | insert('errors.partials.footer') ?> 9 | -------------------------------------------------------------------------------- /formwork/views/errors/install.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

The site is currently offline
due to technical problems

4 |

If you are the maintainer of this site, please run composer install. Composer autoloader was not found.

5 | -------------------------------------------------------------------------------- /formwork/views/errors/maintenance.php: -------------------------------------------------------------------------------- 1 | insert('errors.partials.header') ?> 2 |

We are coming back soon

3 |

The website is currently in maintenance mode.

4 | insert('errors.partials.footer') ?> -------------------------------------------------------------------------------- /formwork/views/errors/panel/assets.php: -------------------------------------------------------------------------------- 1 | 2 |

The administration panel is currently offline due to technical problems

3 |

Required panel assets were not found. If you are the maintainer of this site, please run cd panel; yarn install && yarn build

4 | -------------------------------------------------------------------------------- /formwork/views/errors/partials/footer.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /formwork/views/errors/phpversion.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

The site is currently offline
due to technical problems

4 |

If you are the maintainer of this site, please switch to a PHP version supported by the installed release of Formwork.

5 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | run(); 11 | -------------------------------------------------------------------------------- /panel/.prettierrc: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /panel/.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | -------------------------------------------------------------------------------- /panel/assets/css/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getformwork/formwork/bb179db76b6b2ef14ccc69c8575304e816c535a9/panel/assets/css/.gitkeep -------------------------------------------------------------------------------- /panel/assets/icons/svg/arrow-arrow-left-right-up-down.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/arrow-down.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/arrow-left-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/arrow-left-down-right-up.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/arrow-left-down.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/arrow-left-right.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/arrow-left-up-right-down.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/arrow-left-up.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/arrow-left.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/arrow-right-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/arrow-right-down.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/arrow-right-up-box.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/arrow-right-up.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/arrow-right.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/arrow-up-down-left-right.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/arrow-up-down.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/arrow-up.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/arrows-rotate-clockwise.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/arrows-rotate-counterclockwise.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/bars.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/blockquote.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/bold.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/bolt.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/calendar-clock.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/calendar.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/camera-flash.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/camera-metering-average.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/camera-metering-evaluative.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/camera-metering-partial.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/camera-metering-spot.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/camera-no-flash.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/check-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/check-square.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/check.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/chevron-down.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/chevron-left.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/chevron-right.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/chevron-up.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/circle-small-fill.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/circle-small.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/circle.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/clock-rotate-left.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/clock-rotate-right.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/clock.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/cloud-download.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/cloud-upload.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/cloud.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/code.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/desktop.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/ellipsis-h.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/ellipsis-v.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/exclamation-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/exclamation-octagon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/exclamation-square.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/exclamation-triangle.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/exclamation.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/eye-slash.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/eye.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/file-archive.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/file-backup.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/file-binary.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/file-document.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/file-exclamation.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/file-icons.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/file-image.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/file-list.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/file-page.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/file-presentation.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/file-spreadsheet.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/file-text.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/file-video.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/file.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/grabber.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/home.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/image.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/indent-decrease.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/indent-increase.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/info-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/info-square.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/info.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/italic.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/link.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/list-unordered.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/list.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/markdown.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/minus-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/minus-square.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/minus.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/mobile.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/movie.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/octagon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/page-blank.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/page-compile.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/page-down.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/page-error.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/page-exclamation.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/page-home.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/page-listing.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/page-up.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/page.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/pencil.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/plus-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/plus-square.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/plus.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/quotes.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/readmore.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/reorder-v.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/rotate-left.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/rotate-right.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/search.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/section.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/sitemap.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/square.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/tablet.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/tag.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/template.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/times-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/times-octagon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/times-square.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/times.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/trash.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/triangle.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/user-image-slash.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/user-image.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/icons/svg/user.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getformwork/formwork/bb179db76b6b2ef14ccc69c8575304e816c535a9/panel/assets/images/icon.png -------------------------------------------------------------------------------- /panel/assets/images/user-image.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /panel/assets/js/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getformwork/formwork/bb179db76b6b2ef14ccc69c8575304e816c535a9/panel/assets/js/.gitkeep -------------------------------------------------------------------------------- /panel/logs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getformwork/formwork/bb179db76b6b2ef14ccc69c8575304e816c535a9/panel/logs/.gitkeep -------------------------------------------------------------------------------- /panel/methods.php: -------------------------------------------------------------------------------- 1 | $panel->assets(...), 8 | 9 | 'modals' => $panel->modals(...), 10 | 11 | 'icon' => fn(string $icon) => $panel->assets()->get('/icons/svg/' . $icon . '.svg')->content(), 12 | ]; 13 | }; 14 | -------------------------------------------------------------------------------- /panel/modals/changes.yaml: -------------------------------------------------------------------------------- 1 | title: '{{panel.pages.changes.detected}}' 2 | 3 | message: '{{panel.pages.changes.detected.prompt}}' 4 | 5 | form: false 6 | 7 | buttons: 8 | dismiss: 9 | action: dismiss 10 | icon: times-circle 11 | label: '{{panel.modal.action.cancel}}' 12 | variant: secondary 13 | 14 | continue: 15 | action: command 16 | icon: exclamation-circle 17 | label: '{{panel.modal.action.continue}}' 18 | align: right 19 | command: continue 20 | -------------------------------------------------------------------------------- /panel/modals/deleteFile.yaml: -------------------------------------------------------------------------------- 1 | title: '{{panel.pages.deleteFile}}' 2 | 3 | message: '{{panel.pages.deleteFile.prompt}}' 4 | 5 | buttons: 6 | dismiss: 7 | action: dismiss 8 | icon: times-circle 9 | label: '{{panel.modal.action.cancel}}' 10 | variant: secondary 11 | 12 | delete: 13 | action: submit 14 | icon: trash 15 | label: '{{panel.modal.action.delete}}' 16 | align: right 17 | variant: danger 18 | -------------------------------------------------------------------------------- /panel/modals/deleteFileItem.yaml: -------------------------------------------------------------------------------- 1 | title: '{{panel.pages.deleteFile}}' 2 | 3 | message: '{{panel.pages.deleteFile.prompt}}' 4 | 5 | buttons: 6 | dismiss: 7 | action: dismiss 8 | icon: times-circle 9 | label: '{{panel.modal.action.cancel}}' 10 | variant: secondary 11 | 12 | delete: 13 | action: command 14 | icon: trash 15 | label: '{{panel.modal.action.delete}}' 16 | align: right 17 | variant: danger 18 | command: delete-file 19 | -------------------------------------------------------------------------------- /panel/modals/deletePage.yaml: -------------------------------------------------------------------------------- 1 | title: '{{panel.pages.deletePage}}' 2 | 3 | message: '{{panel.pages.deletePage.prompt}}' 4 | 5 | buttons: 6 | dismiss: 7 | action: dismiss 8 | icon: times-circle 9 | label: '{{panel.modal.action.cancel}}' 10 | variant: secondary 11 | 12 | delete: 13 | action: submit 14 | icon: trash 15 | label: '{{panel.modal.action.delete}}' 16 | align: right 17 | variant: danger 18 | -------------------------------------------------------------------------------- /panel/modals/deleteUser.yaml: -------------------------------------------------------------------------------- 1 | title: '{{panel.users.deleteUser}}' 2 | 3 | message: '{{panel.users.deleteUser.prompt}}' 4 | 5 | buttons: 6 | dismiss: 7 | action: dismiss 8 | icon: times-circle 9 | label: '{{panel.modal.action.cancel}}' 10 | variant: secondary 11 | 12 | delete: 13 | action: submit 14 | icon: trash 15 | label: '{{panel.modal.action.delete}}' 16 | align: right 17 | variant: danger 18 | -------------------------------------------------------------------------------- /panel/modals/deleteUserImage.yaml: -------------------------------------------------------------------------------- 1 | title: '{{panel.user.image.delete}}' 2 | 3 | message: '{{panel.user.image.delete.prompt}}' 4 | 5 | buttons: 6 | dismiss: 7 | action: dismiss 8 | icon: times-circle 9 | label: '{{panel.modal.action.cancel}}' 10 | variant: secondary 11 | 12 | delete: 13 | action: submit 14 | icon: trash 15 | label: '{{panel.modal.action.delete}}' 16 | align: right 17 | variant: danger 18 | -------------------------------------------------------------------------------- /panel/modals/images.yaml: -------------------------------------------------------------------------------- 1 | title: '{{panel.modal.images.title}}' 2 | 3 | size: large 4 | 5 | fields: 6 | imagepicker: 7 | type: page.imagepicker 8 | 9 | buttons: 10 | dismiss: 11 | action: dismiss 12 | icon: times-circle 13 | label: '{{panel.modal.action.cancel}}' 14 | variant: secondary 15 | 16 | pick: 17 | action: command 18 | icon: check-circle 19 | label: '{{panel.modal.action.continue}}' 20 | align: right 21 | command: pick-image 22 | -------------------------------------------------------------------------------- /panel/modals/link.yaml: -------------------------------------------------------------------------------- 1 | title: '{{panel.modal.link.title}}' 2 | 3 | form: false 4 | 5 | fields: 6 | text: 7 | type: text 8 | label: '{{panel.modal.link.text}}' 9 | required: true 10 | 11 | uri: 12 | type: textarea 13 | label: '{{panel.modal.link.uri}}' 14 | required: true 15 | rows: 3 16 | 17 | buttons: 18 | dismiss: 19 | action: dismiss 20 | icon: times-circle 21 | label: '{{panel.modal.action.cancel}}' 22 | variant: secondary 23 | 24 | insert: 25 | action: command 26 | icon: check-circle 27 | label: '{{panel.modal.action.continue}}' 28 | align: right 29 | command: insert-link 30 | 31 | remove: 32 | action: command 33 | icon: trash 34 | label: '{{panel.modal.action.delete}}' 35 | align: right 36 | command: remove-link 37 | variant: link 38 | -------------------------------------------------------------------------------- /panel/modals/renameFile.yaml: -------------------------------------------------------------------------------- 1 | title: '{{panel.pages.renameFile}}' 2 | 3 | fields: 4 | filename: 5 | type: text 6 | label: '{{panel.pages.renameFile.name}}' 7 | required: true 8 | 9 | buttons: 10 | dismiss: 11 | action: dismiss 12 | icon: times-circle 13 | label: '{{panel.modal.action.cancel}}' 14 | variant: secondary 15 | 16 | submit: 17 | action: submit 18 | icon: pencil 19 | label: '{{panel.modal.action.rename}}' 20 | align: right 21 | -------------------------------------------------------------------------------- /panel/modals/renameFileItem.yaml: -------------------------------------------------------------------------------- 1 | title: '{{panel.pages.renameFile}}' 2 | 3 | fields: 4 | filename: 5 | type: text 6 | label: '{{panel.pages.renameFile.name}}' 7 | required: true 8 | 9 | buttons: 10 | dismiss: 11 | action: dismiss 12 | icon: times-circle 13 | label: '{{panel.modal.action.cancel}}' 14 | variant: secondary 15 | 16 | submit: 17 | action: command 18 | icon: pencil 19 | label: '{{panel.modal.action.rename}}' 20 | align: right 21 | command: rename-file 22 | -------------------------------------------------------------------------------- /panel/modals/uploadFile.yaml: -------------------------------------------------------------------------------- 1 | title: '{{panel.files.upload}}' 2 | 3 | size: large 4 | 5 | action: /files/upload/ 6 | 7 | fields: 8 | files: 9 | type: upload 10 | label: '{{panel.files.files}}' 11 | multiple: true 12 | 13 | parent: 14 | type: page 15 | label: '{{panel.files.parent}}' 16 | collection@: site.descendants 17 | required: true 18 | allowSite: true 19 | 20 | buttons: 21 | dismiss: 22 | action: dismiss 23 | icon: times-circle 24 | label: '{{panel.modal.action.cancel}}' 25 | variant: secondary 26 | 27 | submit: 28 | action: submit 29 | icon: cloud-upload 30 | label: '{{panel.modal.action.upload}}' 31 | align: right 32 | -------------------------------------------------------------------------------- /panel/src/scss/components/_animations.scss: -------------------------------------------------------------------------------- 1 | @keyframes fade-in-right { 2 | from { 3 | opacity: 0; 4 | transform: translate3d(150%, 0, 0); 5 | } 6 | 7 | to { 8 | opacity: 1; 9 | transform: none; 10 | } 11 | } 12 | 13 | @keyframes fade-out-right { 14 | from { 15 | opacity: 1; 16 | transform: none; 17 | } 18 | 19 | to { 20 | opacity: 0; 21 | transform: translate3d(150%, 0, 0); 22 | } 23 | } 24 | 25 | @keyframes shake { 26 | 0%, 27 | 100% { 28 | transform: translate3d(0, 0, 0); 29 | } 30 | 31 | 10%, 32 | 50%, 33 | 90% { 34 | transform: translate3d(-4px, 0, 0); 35 | } 36 | 37 | 30%, 38 | 70% { 39 | transform: translate3d(16px, 0, 0); 40 | } 41 | } 42 | 43 | @keyframes spin { 44 | 0% { 45 | transform: rotate(0deg); 46 | } 47 | 48 | 100% { 49 | transform: rotate(360deg); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /panel/src/scss/components/_caption.scss: -------------------------------------------------------------------------------- 1 | .caption { 2 | font-size: 0.875rem; 3 | font-weight: 600; 4 | letter-spacing: 1px; 5 | text-transform: uppercase; 6 | } 7 | -------------------------------------------------------------------------------- /panel/src/scss/components/_forms.scss: -------------------------------------------------------------------------------- 1 | @use "forms/forms-base"; 2 | 3 | @use "forms/forms-color"; 4 | @use "forms/forms-array"; 5 | @use "forms/forms-checkbox"; 6 | @use "forms/forms-date"; 7 | @use "forms/forms-duration"; 8 | @use "forms/forms-editor"; 9 | @use "forms/forms-image"; 10 | @use "forms/forms-range"; 11 | @use "forms/forms-tags"; 12 | @use "forms/forms-togglegroup"; 13 | @use "forms/forms-upload"; 14 | -------------------------------------------------------------------------------- /panel/src/scss/components/_functions.scss: -------------------------------------------------------------------------------- 1 | @use "sass:color"; 2 | 3 | @use "sass:string"; 4 | 5 | @function gcd($a, $b) { 6 | @if $b == 0 { 7 | @return $a; 8 | } @else { 9 | @return gcd($b, $a % $b); 10 | } 11 | } 12 | 13 | @function tint($color, $percentage) { 14 | @return color.mix(#fff, $color, $percentage); 15 | } 16 | 17 | @function shade($color, $percentage) { 18 | @return color.mix(#000, $color, $percentage); 19 | } 20 | 21 | @function urlencode-color($color) { 22 | @return "%23" + string.slice("#{$color}", 2); 23 | } 24 | -------------------------------------------------------------------------------- /panel/src/scss/components/_icon.scss: -------------------------------------------------------------------------------- 1 | .icon { 2 | display: inline-block; 3 | width: 1em; 4 | min-width: 16px; 5 | height: 1em; 6 | min-height: 16px; 7 | vertical-align: -0.125em; 8 | } 9 | -------------------------------------------------------------------------------- /panel/src/scss/components/_logo.scss: -------------------------------------------------------------------------------- 1 | @use "variables" as *; 2 | 3 | @use "sass:map"; 4 | 5 | .logo { 6 | margin-bottom: 2rem; 7 | color: var(--color-base-100); 8 | font-size: $font-size-h5; 9 | font-weight: 600; 10 | line-height: 1.5rem; 11 | text-align: center; 12 | @media (min-width: map.get($breakpoints, "md")) { 13 | text-align: left; 14 | } 15 | } 16 | 17 | .logo img { 18 | width: 1.5rem; 19 | height: 1.5rem; 20 | margin-right: 0.25rem; 21 | vertical-align: -0.25rem; 22 | } 23 | 24 | .logo a { 25 | padding-top: $focusring-width; 26 | color: var(--color-base-100); 27 | } 28 | -------------------------------------------------------------------------------- /panel/src/scss/components/_options.scss: -------------------------------------------------------------------------------- 1 | @use "variables" as *; 2 | 3 | .options-form .form-checkbox-label { 4 | margin-bottom: 0.5rem; 5 | font-size: $font-size-md; 6 | } 7 | 8 | .info-data { 9 | width: 100%; 10 | margin-bottom: 1rem; 11 | table-layout: fixed; 12 | } 13 | 14 | .info-data td { 15 | overflow: hidden; 16 | padding: 0.5rem; 17 | vertical-align: top; 18 | } 19 | 20 | .info-data-key { 21 | width: 25%; 22 | } 23 | 24 | .info-data-value { 25 | width: 75%; 26 | font-family: $mono-font-family; 27 | font-size: $font-size-sm; 28 | overflow-wrap: break-word; 29 | } 30 | -------------------------------------------------------------------------------- /panel/src/scss/components/_sortable.scss: -------------------------------------------------------------------------------- 1 | .sortable-handle .icon { 2 | display: inline-block; 3 | margin-bottom: 0; 4 | color: var(--color-base-300); 5 | cursor: grab; 6 | } 7 | 8 | .sortable-chosen, 9 | .sortable-ghost { 10 | background-color: var(--color-base-900); 11 | cursor: grabbing; 12 | } 13 | 14 | .sortable-chosen * { 15 | cursor: grabbing; 16 | } 17 | 18 | .sortable-fallback { 19 | box-shadow: 0 0 0.75rem 0.25rem var(--color-shadow-lg); 20 | } 21 | 22 | .sortable-fallback::before, 23 | .sortable-fallback::after { 24 | display: none; 25 | } 26 | -------------------------------------------------------------------------------- /panel/src/scss/components/_statistics.scss: -------------------------------------------------------------------------------- 1 | .statistics-histogram-cell { 2 | position: relative; 3 | } 4 | 5 | .statistics-histogram-cell::before { 6 | position: absolute; 7 | right: 0; 8 | bottom: -1px; 9 | left: 0; 10 | height: 0.125rem; 11 | background-color: var(--color-base-500); 12 | content: " "; 13 | } 14 | 15 | .statistics-histogram-cell::after { 16 | position: absolute; 17 | right: 0; 18 | bottom: -1px; 19 | left: 0; 20 | width: var(--percentage); 21 | height: 0.125rem; 22 | background-color: var(--color-accent-600); 23 | content: " "; 24 | } 25 | -------------------------------------------------------------------------------- /panel/src/scss/components/_table.scss: -------------------------------------------------------------------------------- 1 | .table { 2 | width: 100%; 3 | border-collapse: collapse; 4 | table-layout: fixed; 5 | } 6 | 7 | .table-header { 8 | padding: 0.5rem; 9 | font-weight: 600; 10 | text-align: left; 11 | } 12 | 13 | .table-cell { 14 | padding: 0.5rem; 15 | } 16 | 17 | .table-bordered .table-cell { 18 | border-top: 1px solid var(--color-base-600); 19 | border-bottom: 1px solid var(--color-base-600); 20 | } 21 | 22 | .table-striped tbody > tr:nth-child(2n + 1) { 23 | background-color: var(--color-base-800); 24 | } 25 | 26 | .table-hoverable tbody > tr:hover { 27 | background-color: var(--color-base-800); 28 | } 29 | 30 | .table-striped.table-hoverable tbody > tr:hover { 31 | background-color: var(--color-base-700); 32 | } 33 | -------------------------------------------------------------------------------- /panel/src/scss/components/forms/_forms-array.scss: -------------------------------------------------------------------------------- 1 | @use "../mixins" as *; 2 | 3 | .form-input-array { 4 | margin-bottom: 0.5rem; 5 | @include user-select-none; 6 | } 7 | 8 | .form-input-array .sortable-handle .icon { 9 | vertical-align: middle; 10 | } 11 | 12 | .form-input-array-row { 13 | display: flex; 14 | padding: 0.25rem 0.5rem; 15 | margin: 0 (-0.5rem); 16 | white-space: nowrap; 17 | } 18 | 19 | .form-input-array-key, 20 | .form-input-array-value { 21 | display: inline-block; 22 | margin-bottom: 0; 23 | } 24 | 25 | .form-input-array-key { 26 | width: 30%; 27 | margin-right: 0.25rem; 28 | } 29 | 30 | .form-input-array-value { 31 | margin-right: 0.25rem; 32 | } 33 | 34 | .form-input-array-add, 35 | .form-input-array-remove { 36 | padding: 0; 37 | margin-right: 0.25rem; 38 | } 39 | 40 | .form-input-array .sortable-chosen:not(.sortable-drag) { 41 | background-color: var(--color-accent-900); 42 | } 43 | -------------------------------------------------------------------------------- /panel/src/scss/components/forms/_forms-color.scss: -------------------------------------------------------------------------------- 1 | @use "../mixins" as *; 2 | @use "../variables" as *; 3 | 4 | .form-input-color { 5 | width: 2rem; 6 | height: 2rem; 7 | padding: 0; 8 | border: none; 9 | border: 1px solid var(--color-base-500); 10 | border-radius: $border-radius; 11 | appearance: none; 12 | background-color: transparent; 13 | cursor: pointer; 14 | } 15 | 16 | .form-input-color-value { 17 | align-self: center; 18 | margin-left: 0.5rem; 19 | font-size: $font-size-sm; 20 | } 21 | 22 | .form-input-color::-webkit-color-swatch-wrapper { 23 | padding: 0; 24 | } 25 | 26 | .form-input-color::-webkit-color-swatch { 27 | border: none; 28 | } 29 | 30 | .form-input-color::-moz-color-swatch { 31 | border: none; 32 | } 33 | -------------------------------------------------------------------------------- /panel/src/ts/components/forms.ts: -------------------------------------------------------------------------------- 1 | import { $$ } from "../utils/selectors"; 2 | import { Form } from "./form"; 3 | 4 | export class Forms { 5 | [name: string]: Form; 6 | 7 | constructor() { 8 | $$("[data-form]").forEach((element: HTMLFormElement) => { 9 | if (element.dataset.form) { 10 | this[element.dataset.form] = new Form(element); 11 | } 12 | }); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /panel/src/ts/components/icons.ts: -------------------------------------------------------------------------------- 1 | import { app } from "../app"; 2 | 3 | const cache = new Map(); 4 | 5 | export function passIcon(icon: string, callback: (iconData: string) => void) { 6 | if (cache.has(icon)) { 7 | callback(cache.get(icon)); 8 | return; 9 | } 10 | 11 | const request = new XMLHttpRequest(); 12 | 13 | request.onload = function () { 14 | const data = this.status === 200 ? this.response : ""; 15 | if (data !== "") { 16 | cache.set(icon, data); 17 | } 18 | callback(data); 19 | }; 20 | 21 | request.open("GET", `${app.config.baseUri}assets/icons/svg/${icon}.svg`); 22 | request.send(); 23 | } 24 | 25 | export function insertIcon(icon: string, element: HTMLElement, position: InsertPosition = "afterbegin") { 26 | passIcon(icon, (data) => element.insertAdjacentHTML(position, data)); 27 | } 28 | -------------------------------------------------------------------------------- /panel/src/ts/components/inputs/togglegroup-input.ts: -------------------------------------------------------------------------------- 1 | import { $ } from "../../utils/selectors"; 2 | 3 | export class TogglegroupInput { 4 | readonly name: string; 5 | 6 | readonly element: HTMLFieldSetElement; 7 | 8 | constructor(fieldset: HTMLFieldSetElement) { 9 | this.element = fieldset; 10 | 11 | this.name = fieldset.id; 12 | 13 | $(`label[for="${this.name}"]`)?.addEventListener("click", () => { 14 | $(`input:checked`, this.element)?.focus(); 15 | }); 16 | } 17 | 18 | get value() { 19 | return ($(`input:checked`, this.element) as HTMLInputElement).value; 20 | } 21 | 22 | set value(value: string) { 23 | const input = $(`input[value="${value}"]`, this.element) as HTMLInputElement; 24 | if (input) { 25 | input.checked = true; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /panel/src/ts/components/modals.ts: -------------------------------------------------------------------------------- 1 | import { $$ } from "../utils/selectors"; 2 | import { Modal } from "./modal"; 3 | 4 | export class Modals { 5 | [id: string]: Modal; 6 | constructor() { 7 | $$(".modal").forEach((element: HTMLElement) => (this[element.id] = new Modal(element))); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /panel/src/ts/components/notifications.ts: -------------------------------------------------------------------------------- 1 | import { $$ } from "../utils/selectors"; 2 | import { Notification } from "./notification"; 3 | 4 | export class Notifications { 5 | constructor() { 6 | let delay = 0; 7 | 8 | $$("meta[name=notification]").forEach((element: HTMLMetaElement) => { 9 | window.setTimeout(() => { 10 | const data = JSON.parse(element.content); 11 | const notification = new Notification(data.text, data.type, { 12 | interval: data.interval, 13 | icon: data.icon, 14 | }); 15 | notification.show(); 16 | }, delay); 17 | delay += 500; 18 | (element.parentNode as ParentNode).removeChild(element); 19 | }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /panel/src/ts/components/sections.ts: -------------------------------------------------------------------------------- 1 | import { $$ } from "../utils/selectors"; 2 | 3 | export class Sections { 4 | constructor() { 5 | $$(".collapsible .section-header").forEach((element) => { 6 | element.addEventListener("click", () => { 7 | const section = element.parentNode as HTMLElement; 8 | section.classList.toggle("collapsed"); 9 | }); 10 | }); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /panel/src/ts/components/views/statistics.ts: -------------------------------------------------------------------------------- 1 | import { $ } from "../../utils/selectors"; 2 | import { StatisticsChart } from "../statistics-chart"; 3 | 4 | export class Statistics { 5 | constructor() { 6 | const chart = $(".statistics-chart"); 7 | if (chart) { 8 | const chartData = chart.dataset.chartData; 9 | if (chartData) { 10 | new StatisticsChart(chart, JSON.parse(chartData)); 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /panel/src/ts/utils/arrays.ts: -------------------------------------------------------------------------------- 1 | export function arrayEquals(array1: Array, array2: Array) { 2 | if (array1.length !== array2.length) { 3 | return false; 4 | } 5 | for (let i = 0; i < array1.length; i++) { 6 | if (array1[i] !== array2[i]) { 7 | return false; 8 | } 9 | } 10 | return true; 11 | } 12 | -------------------------------------------------------------------------------- /panel/src/ts/utils/cookies.ts: -------------------------------------------------------------------------------- 1 | export function getCookies() { 2 | const result: Record = {}; 3 | const cookies = document.cookie.split(";"); 4 | for (const cookie of cookies) { 5 | const nameAndValue = cookie.split("=", 2); 6 | if (nameAndValue.length === 2) { 7 | result[nameAndValue[0].trim()] = decodeURIComponent(nameAndValue[1].trim()); 8 | } 9 | } 10 | return result; 11 | } 12 | 13 | export function setCookie(name: string, value: string, options: Record) { 14 | let cookie = `${name}=${value}`; 15 | for (const option in options) { 16 | cookie += `;${option}=${options[option]}`; 17 | } 18 | document.cookie = cookie; 19 | } 20 | -------------------------------------------------------------------------------- /panel/src/ts/utils/dimensions.ts: -------------------------------------------------------------------------------- 1 | export function getOuterWidth(element: HTMLElement) { 2 | const style = getComputedStyle(element); 3 | return element.offsetWidth + parseInt(style.marginLeft) + parseInt(style.marginRight); 4 | } 5 | 6 | export function getOuterHeight(element: HTMLElement) { 7 | const style = getComputedStyle(element); 8 | return element.offsetHeight + parseInt(style.marginTop) + parseInt(style.marginBottom); 9 | } 10 | -------------------------------------------------------------------------------- /panel/src/ts/utils/math.ts: -------------------------------------------------------------------------------- 1 | export function clamp(value: number, min: number, max: number) { 2 | return Math.min(Math.max(value, min), max); 3 | } 4 | 5 | export function mod(x: number, y: number) { 6 | // Return x mod y (always rounded downwards, differs from x % y which is the remainder) 7 | return x - y * Math.floor(x / y); 8 | } 9 | -------------------------------------------------------------------------------- /panel/src/ts/utils/numbers.ts: -------------------------------------------------------------------------------- 1 | export function getSafeInteger(value: number) { 2 | const max = Number.MAX_SAFE_INTEGER; 3 | const min = -max; 4 | if (value > max) { 5 | return max; 6 | } 7 | if (value < min) { 8 | return min; 9 | } 10 | return value; 11 | } 12 | -------------------------------------------------------------------------------- /panel/src/ts/utils/selectors.ts: -------------------------------------------------------------------------------- 1 | export function $(selector: string, parent: ParentNode = document): HTMLElement | null { 2 | return parent.querySelector(selector); 3 | } 4 | 5 | export function $$(selector: string, parent: ParentNode = document): NodeListOf { 6 | return parent.querySelectorAll(selector); 7 | } 8 | -------------------------------------------------------------------------------- /panel/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "./src/ts/**/*.ts" 4 | ], 5 | "compilerOptions": { 6 | "esModuleInterop": true, 7 | "isolatedModules": true, 8 | "lib": ["ES2017", "DOM"], 9 | "noEmit": true, 10 | "noImplicitAny": true, 11 | "noImplicitThis": true, 12 | "strictNullChecks": true, 13 | "useDefineForClassFields": true, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /panel/views/fields.php: -------------------------------------------------------------------------------- 1 | insert('fields.layout.' . $fields->layout()->type(), ['sections' => $fields->layout()->sections()]) ?> -------------------------------------------------------------------------------- /panel/views/fields/checkbox.php: -------------------------------------------------------------------------------- 1 |
2 | 15 |
16 | insert('fields.partials.description') ?> -------------------------------------------------------------------------------- /panel/views/fields/color.php: -------------------------------------------------------------------------------- 1 | layout('fields.field') ?> 2 |
3 |
4 | attr([ 5 | 'class' => ['form-input-color', $field->get('class')], 6 | 'type' => 'color', 7 | 'id' => $field->name(), 8 | 'name' => $field->formName(), 9 | 'value' => $field->value(), 10 | 'placeholder' => $field->placeholder(), 11 | 'required' => $field->isRequired(), 12 | 'disabled' => $field->isDisabled(), 13 | 'hidden' => $field->isHidden(), 14 | ]) ?>> 15 | escape($field->value()) ?> 16 |
17 |
-------------------------------------------------------------------------------- /panel/views/fields/date.php: -------------------------------------------------------------------------------- 1 | layout('fields.field') ?> 2 |
3 | icon($field->get('icon', 'calendar-clock')) ?> 4 | attr([ 5 | 'type' => 'text', 6 | 'class' => ['form-input', 'form-input-date'], 7 | 'id' => $field->name(), 8 | 'name' => $field->formName(), 9 | 'value' => $field->toDateTimeString(), 10 | 'placeholder' => $field->placeholder(), 11 | 'required' => $field->isRequired(), 12 | 'disabled' => $field->isDisabled(), 13 | 'hidden' => $field->isHidden(), 14 | 'data-time' => $field->hasTime() ? 'true' : 'false', 15 | ]) ?>> 16 | icon('times-circle') ?> 17 |
18 | -------------------------------------------------------------------------------- /panel/views/fields/layout/default.php: -------------------------------------------------------------------------------- 1 |
2 | 3 | isVisible()) : ?> 4 | insert('fields.' . $field->type(), ['field' => $field]) ?> 5 | 6 | 7 |
-------------------------------------------------------------------------------- /panel/views/fields/page/imagepicker.php: -------------------------------------------------------------------------------- 1 | layout('fields.field') ?> 2 |
3 | icon('image') ?> 4 |

translate('panel.modal.images.noImages') ?>

5 |
6 | parent()->model())?->has('files')): ?> 7 | 15 | -------------------------------------------------------------------------------- /panel/views/fields/partials/description.php: -------------------------------------------------------------------------------- 1 | has('description')) : ?> 2 |
markdown($field->get('description')) ?>
3 | -------------------------------------------------------------------------------- /panel/views/fields/partials/label.php: -------------------------------------------------------------------------------- 1 | has('label')) : ?> 2 | 3 | has('suggestion')) : ?>(escape($field->get('suggestion')) ?>) 4 | -------------------------------------------------------------------------------- /panel/views/fields/select.php: -------------------------------------------------------------------------------- 1 | layout('fields.field') ?> 2 |
3 | has('icon')) : ?> 4 | icon($field->get('icon')) ?> 5 | 6 | 18 |
-------------------------------------------------------------------------------- /panel/views/fields/tabs.php: -------------------------------------------------------------------------------- 1 | 9 | insert('fields', ['fields' => $field->get('fields')]) ?> -------------------------------------------------------------------------------- /panel/views/fields/template.php: -------------------------------------------------------------------------------- 1 | layout('fields.field') ?> 2 |
3 | icon($field->get('icon', 'template')) ?> 4 | 16 |
-------------------------------------------------------------------------------- /panel/views/fields/textarea.php: -------------------------------------------------------------------------------- 1 | layout('fields.field') ?> 2 | -------------------------------------------------------------------------------- /panel/views/files/index.php: -------------------------------------------------------------------------------- 1 | layout('panel') ?> 2 | modals()->add('uploadFile') ?> 3 | 4 |
5 |
6 |
translate('panel.files.files') ?>
7 |
8 | user()->permissions()->has('files.upload')) : ?> 9 | 10 | 11 |
12 |
13 | 14 |
15 |
16 | insert('partials.files.file.list', ['name' => 'view-files', 'files' => $files, 'columns' => ['parent', 'date', 'size']]) ?> 17 |
18 |
19 |
20 | -------------------------------------------------------------------------------- /panel/views/layouts/fields/field.php: -------------------------------------------------------------------------------- 1 | insert('fields.partials.label') ?> 2 | content() ?> 3 | insert('fields.partials.description') ?> 4 | -------------------------------------------------------------------------------- /panel/views/options/site.php: -------------------------------------------------------------------------------- 1 | layout('panel') ?> 2 | 3 | modals()->add('changes') ?> 4 | 5 |
6 |
7 |
translate('panel.options.options') ?>
8 |
9 | 10 | 11 |
12 |
13 | 14 |
15 | insert('fields', ['fields' => $fields]) ?> 16 |
17 |
-------------------------------------------------------------------------------- /panel/views/options/system.php: -------------------------------------------------------------------------------- 1 | layout('panel') ?> 2 | 3 | modals()->add('changes') ?> 4 | 5 |
6 |
7 |
translate('panel.options.options') ?>
8 |
9 | 10 | 11 |
12 |
13 | 14 |
15 | insert('fields', ['fields' => $fields]) ?> 16 |
17 |
-------------------------------------------------------------------------------- /panel/views/options/tabs.php: -------------------------------------------------------------------------------- 1 |
2 | 3 | user()->permissions()->has('options.' . $tab)) : ?> 4 | translate('panel.options.' . $tab) ?> 5 | 6 | 7 |
-------------------------------------------------------------------------------- /panel/views/partials/files/images/exif/data.php: -------------------------------------------------------------------------------- 1 | 2 | parsedTags() as $key => $value) : ?> 3 | 4 | 5 | 6 | 7 | 8 |
escape($key) ?>escape(is_array($value) ? implode(', ', $value) : (string) $value) ?>
-------------------------------------------------------------------------------- /panel/views/partials/files/images/position/map.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /panel/views/partials/login/notification.php: -------------------------------------------------------------------------------- 1 | notifications()) : ?> 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /panel/views/partials/pages/status.php: -------------------------------------------------------------------------------- 1 | status() === 'published') : ?> 2 | icon('circle-small-fill') ?> 3 | status() === 'notPublished') : ?> 4 | icon('circle-small-fill') ?> 5 | -------------------------------------------------------------------------------- /panel/views/partials/scripts.php: -------------------------------------------------------------------------------- 1 | assets()->scripts() as $script): ?> 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /panel/views/register/register.php: -------------------------------------------------------------------------------- 1 | layout('login') ?> 2 |
translate('panel.register.register') ?>
3 |

translate('panel.register.createUser') ?>

4 |
5 | 6 | insert('fields.' . $field->type(), ['field' => $field]) ?> 7 | 8 | 9 | 10 |
-------------------------------------------------------------------------------- /panel/views/tools/tabs.php: -------------------------------------------------------------------------------- 1 |
2 | 3 | user()->permissions()->has('tools.' . $tab)) : ?> 4 | translate('panel.tools.' . $tab) ?> 5 | 6 | 7 |
-------------------------------------------------------------------------------- /site/config/site.yaml: -------------------------------------------------------------------------------- 1 | title: Formwork 2 | languages: 3 | available: 4 | - en 5 | -------------------------------------------------------------------------------- /site/config/system.yaml: -------------------------------------------------------------------------------- 1 | files: 2 | allowedExtensions: 3 | - .jpg 4 | - .jpeg 5 | - .png 6 | - .gif 7 | - .svg 8 | - .webp 9 | - .mp4 10 | - .webm 11 | -------------------------------------------------------------------------------- /site/files/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getformwork/formwork/bb179db76b6b2ef14ccc69c8575304e816c535a9/site/files/.gitkeep -------------------------------------------------------------------------------- /site/pages/1-about/page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: About 3 | --- 4 | # About 5 | ## This is an about page 6 | You can use this page to display information about you or your organization. -------------------------------------------------------------------------------- /site/pages/2-blog/20180615-hello-world/post.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Hello World!' 3 | publishDate: '2018-06-15 00:00:00' 4 | --- 5 | This is an example of blog post. -------------------------------------------------------------------------------- /site/pages/2-blog/20180616-another-blog-post/post.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Another Blog Post' 3 | publishDate: '2018-06-16 00:00:00' 4 | summary: "This is the summary of another blog post.\n\n💡 Tip: click on post title to reveal the rest of content." 5 | --- 6 | 7 | ➡️ Here we go. This is the rest of the blog post. -------------------------------------------------------------------------------- /site/pages/2-blog/blog.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Blog 3 | --- 4 | -------------------------------------------------------------------------------- /site/pages/error/page.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 'Page Not Found' 3 | routable: false 4 | cacheable: false 5 | icon: page-error 6 | allowChildren: false 7 | responseStatus: 404 8 | --- 9 | # Error 404 10 | The page you are looking for does not exist or it has been moved. -------------------------------------------------------------------------------- /site/pages/index/formwork.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getformwork/formwork/bb179db76b6b2ef14ccc69c8575304e816c535a9/site/pages/index/formwork.png -------------------------------------------------------------------------------- /site/pages/index/formwork.png.meta.yaml: -------------------------------------------------------------------------------- 1 | alt: 'Screenshot of Formwork home page with a link to the administration panel and a dashboard preview' 2 | -------------------------------------------------------------------------------- /site/pages/index/panel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getformwork/formwork/bb179db76b6b2ef14ccc69c8575304e816c535a9/site/pages/index/panel.png -------------------------------------------------------------------------------- /site/pages/index/panel.png.meta.yaml: -------------------------------------------------------------------------------- 1 | alt: 'Screenshot of Formwork administration panel' 2 | -------------------------------------------------------------------------------- /site/schemes/files/file.yaml: -------------------------------------------------------------------------------- 1 | title: File 2 | -------------------------------------------------------------------------------- /site/schemes/files/image.yaml: -------------------------------------------------------------------------------- 1 | title: Image 2 | 3 | extend: files.file 4 | 5 | layout: 6 | type: sections 7 | sections: 8 | metadata: 9 | label: '{{file.metadata}}' 10 | fields: [alt] 11 | 12 | fields: 13 | alt: 14 | type: text 15 | label: '{{file.metadata.alternativeText}}' 16 | -------------------------------------------------------------------------------- /site/schemes/pages/post.yaml: -------------------------------------------------------------------------------- 1 | title: Blog Post 2 | 3 | extend: pages.page 4 | 5 | options: 6 | default: false 7 | children: false 8 | num: date 9 | 10 | layout: 11 | sections: 12 | content: 13 | fields: [title, coverImage, tags, summary, content] 14 | 15 | fields: 16 | summary: 17 | type: markdown 18 | label: '{{page.summary}}' 19 | rows: 5 20 | 21 | coverImage: 22 | type: image 23 | label: '{{page.image}}' 24 | 25 | tags: 26 | type: tags 27 | label: '{{page.tags}}' 28 | placeholder: '{{page.noTags}}' 29 | -------------------------------------------------------------------------------- /site/statistics/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getformwork/formwork/bb179db76b6b2ef14ccc69c8575304e816c535a9/site/statistics/.gitkeep -------------------------------------------------------------------------------- /site/templates/default.php: -------------------------------------------------------------------------------- 1 | layout('site') ?> 2 |
3 |
4 |

escape($page->title()) ?>

5 | content() ?> 6 |
7 |
-------------------------------------------------------------------------------- /site/templates/page.php: -------------------------------------------------------------------------------- 1 | layout('site') ?> 2 |
3 |
4 |
5 | content() ?> 6 |
7 |
8 |
-------------------------------------------------------------------------------- /site/templates/partials/cover-image.php: -------------------------------------------------------------------------------- 1 | has('coverImage') && ($image = $page->coverImage())) : ?> 2 |
3 | -------------------------------------------------------------------------------- /site/templates/partials/menu.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /site/templates/partials/meta.php: -------------------------------------------------------------------------------- 1 | metadata() as $meta) : ?> 2 | isCharset()) : ?> 3 | 4 | isHTTPEquiv()) : ?> 5 | 6 | 7 | prefix() === 'og' ? 'property' : 'name' ?>="escapeAttr($meta->name()) ?>" content="escapeAttr($meta->content()) ?>"> 8 | 9 | -------------------------------------------------------------------------------- /site/templates/partials/pagination.php: -------------------------------------------------------------------------------- 1 | hasPages()) : ?> 2 | 14 | -------------------------------------------------------------------------------- /site/templates/partials/tags.php: -------------------------------------------------------------------------------- 1 | has('tags')) : ?> 2 |
3 | tags() as $tag) : ?> 4 | 5 | 6 |
7 | -------------------------------------------------------------------------------- /site/templates/post.php: -------------------------------------------------------------------------------- 1 | layout('site') ?> 2 |
3 |
4 |
5 |

escape($page->title()) ?>

6 | insert('_tags', ['post' => $page, 'blog' => $page->parent()]) ?> 7 | summary() ?>content() ?> 8 |
9 |
10 |
-------------------------------------------------------------------------------- /site/translations/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getformwork/formwork/bb179db76b6b2ef14ccc69c8575304e816c535a9/site/translations/.gitkeep -------------------------------------------------------------------------------- /site/users/accounts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getformwork/formwork/bb179db76b6b2ef14ccc69c8575304e816c535a9/site/users/accounts/.gitkeep -------------------------------------------------------------------------------- /site/users/images/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/getformwork/formwork/bb179db76b6b2ef14ccc69c8575304e816c535a9/site/users/images/.gitkeep -------------------------------------------------------------------------------- /site/users/roles/admin.yaml: -------------------------------------------------------------------------------- 1 | title: '{{user.role.admin}}' 2 | 3 | permissions: 4 | dashboard: true 5 | cache: true 6 | backup: true 7 | pages: true 8 | files: true 9 | options: true 10 | updates: true 11 | users: true 12 | statistics: true 13 | tools: true 14 | -------------------------------------------------------------------------------- /site/users/roles/user.yaml: -------------------------------------------------------------------------------- 1 | title: '{{user.role.user}}' 2 | 3 | permissions: 4 | dashboard: true 5 | pages: true 6 | files: true 7 | --------------------------------------------------------------------------------