├── .editorconfig ├── .github ├── ISSUE_TEMPLATE.md └── ISSUE_TEMPLATE │ ├── Bug_report.md │ └── Feature_request.md ├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── LICENSE.md ├── README.md ├── bin ├── build_directus.sh ├── build_subtree.sh ├── directus └── runtests.sh ├── composer.json ├── config ├── api_sample.php └── migrations.php ├── docs ├── .vuepress │ ├── config.js │ └── override.styl ├── README.md └── guide │ └── README.md ├── extensions ├── .babelrc ├── .gitignore ├── README.md ├── core │ ├── interfaces │ │ ├── blob │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ └── readonly.vue │ │ ├── checkboxes │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── color │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ └── readonly.vue │ │ ├── date │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── datetime │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── dropdown-multiselect │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── dropdown │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── encrypted │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── file-size │ │ │ ├── format-size.js │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── file-upload │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ └── readonly.vue │ │ ├── group │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── json │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── many-to-one │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── markdown │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ └── readonly.vue │ │ ├── numeric │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── one-to-many │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── primary-key │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── radio-buttons │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── single-file │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── slider │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── slug │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ └── readonly.vue │ │ ├── sort │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── status │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── tags │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── text-input │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── textarea │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── time │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── toggle │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ └── readonly.vue │ │ ├── wysiwyg-full │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ ├── quill.theme.css │ │ │ └── readonly.vue │ │ └── wysiwyg │ │ │ ├── interface.vue │ │ │ ├── meta.json │ │ │ ├── package-lock.json │ │ │ ├── package.json │ │ │ └── readonly.vue │ └── listings │ │ ├── tabular │ │ ├── listing.vue │ │ ├── meta.json │ │ └── options.vue │ │ └── tile │ │ ├── listing.vue │ │ ├── meta.json │ │ └── options.vue ├── json-minifier.js ├── mixins │ ├── interface.js │ ├── listing.js │ └── page.js ├── package-lock.json └── package.json ├── logs └── .gitkeep ├── migrations └── db │ ├── schemas │ ├── 20180220023138_create_activity_table.php │ ├── 20180220023144_create_activity_seen_table.php │ ├── 20180220023152_create_collections_presets_table.php │ ├── 20180220023157_create_collections_table.php │ ├── 20180220023202_create_fields_table.php │ ├── 20180220023208_create_files_table.php │ ├── 20180220023213_create_folders_table.php │ ├── 20180220023217_create_roles_table.php │ ├── 20180220023226_create_permissions_table.php │ ├── 20180220023232_create_relations_table.php │ ├── 20180220023238_create_revisions_table.php │ ├── 20180220023243_create_settings_table.php │ ├── 20180220023248_create_users_table.php │ └── 20180426173310_create_user_roles.php │ └── seeds │ ├── FieldsSeeder.php │ ├── FileSeeder.php │ ├── RelationsSeeder.php │ └── RolesSeeder.php ├── package-lock.json ├── package.json ├── phpunit.php ├── phpunit.xml ├── public ├── .htaccess ├── extensions │ ├── .gitignore │ ├── core │ │ └── auth │ │ │ ├── facebook │ │ │ ├── Provider.php │ │ │ ├── auth.php │ │ │ └── icon.svg │ │ │ ├── github │ │ │ ├── Provider.php │ │ │ ├── auth.php │ │ │ └── icon.svg │ │ │ ├── google │ │ │ ├── Provider.php │ │ │ ├── auth.php │ │ │ └── icon.svg │ │ │ ├── okta │ │ │ ├── Provider.php │ │ │ ├── auth.php │ │ │ └── icon.svg │ │ │ └── twitter │ │ │ ├── Provider.php │ │ │ ├── auth.php │ │ │ └── icon.svg │ └── custom │ │ ├── auth │ │ └── .gitkeep │ │ ├── endpoints │ │ ├── .htaccess │ │ ├── _directory │ │ │ ├── controllers │ │ │ │ └── Home.php │ │ │ ├── endpoints.php │ │ │ └── functions.php │ │ └── _example.php │ │ ├── extensions │ │ ├── .gitignore │ │ └── .htaccess │ │ ├── hashers │ │ └── _CustomHasher.php │ │ ├── hooks │ │ ├── .gitignore │ │ ├── _products │ │ │ ├── BeforeInsertProducts.php │ │ │ └── hooks.php │ │ └── _webhook │ │ │ └── hooks.php │ │ ├── interfaces │ │ └── .gitignore │ │ ├── listviews │ │ └── .gitignore │ │ ├── mail │ │ └── .gitkeep │ │ └── migrations │ │ └── .gitkeep ├── index.php ├── storage │ └── uploads │ │ ├── .gitignore │ │ ├── .htaccess │ │ └── 00000000001.jpg └── thumbnail │ ├── .gitignore │ ├── .htaccess │ ├── img-not-found.png │ └── index.php ├── src ├── core │ └── Directus │ │ ├── Application │ │ ├── Application.php │ │ ├── Container.php │ │ ├── CoreServicesProvider.php │ │ ├── DefaultServicesProvider.php │ │ ├── ErrorHandlers │ │ │ ├── ErrorHandler.php │ │ │ ├── MethodNotAllowedHandler.php │ │ │ ├── NotFoundHandler.php │ │ │ └── NotInstalledNotFoundHandler.php │ │ ├── Http │ │ │ ├── Middleware │ │ │ │ ├── AbstractMiddleware.php │ │ │ │ ├── AbstractRateLimitMiddleware.php │ │ │ │ ├── AdminMiddleware.php │ │ │ │ ├── AuthenticatedMiddleware.php │ │ │ │ ├── AuthenticationMiddleware.php │ │ │ │ ├── CorsMiddleware.php │ │ │ │ ├── IpRateLimitMiddleware.php │ │ │ │ ├── Middleware.php │ │ │ │ ├── RateLimit │ │ │ │ │ └── UserIdentityResolver.php │ │ │ │ ├── ResponseCacheMiddleware.php │ │ │ │ ├── TableGatewayMiddleware.php │ │ │ │ └── UserRateLimitMiddleware.php │ │ │ ├── Request.php │ │ │ └── Response.php │ │ └── Route.php │ │ ├── Authentication │ │ ├── Exception │ │ │ ├── ExpiredRequestTokenException.php │ │ │ ├── ExpiredResetPasswordToken.php │ │ │ ├── ExpiredTokenException.php │ │ │ ├── InvalidInvitationCodeException.php │ │ │ ├── InvalidRequestTokenException.php │ │ │ ├── InvalidResetPasswordTokenException.php │ │ │ ├── InvalidTokenException.php │ │ │ ├── InvalidUserCredentialsException.php │ │ │ ├── UnknownUserAttributeException.php │ │ │ ├── UserAlreadyLoggedInException.php │ │ │ ├── UserInactiveException.php │ │ │ ├── UserNotAuthenticatedException.php │ │ │ ├── UserNotFoundException.php │ │ │ └── UserWithEmailNotFoundException.php │ │ ├── Provider.php │ │ ├── Sso │ │ │ ├── AbstractSocialProvider.php │ │ │ ├── OneSocialProvider.php │ │ │ ├── Social.php │ │ │ ├── SocialProviderInterface.php │ │ │ ├── SocialUser.php │ │ │ └── TwoSocialProvider.php │ │ └── User │ │ │ ├── Provider │ │ │ ├── UserProviderInterface.php │ │ │ └── UserTableGatewayProvider.php │ │ │ ├── User.php │ │ │ └── UserInterface.php │ │ ├── Cache │ │ ├── Cache.php │ │ └── Response.php │ │ ├── Collection │ │ ├── Arrayable.php │ │ ├── Collection.php │ │ ├── CollectionInterface.php │ │ └── composer.json │ │ ├── Config │ │ ├── Config.php │ │ ├── ConfigInterface.php │ │ ├── Exception │ │ │ ├── InvalidStatusException.php │ │ │ └── InvalidValueException.php │ │ ├── StatusItem.php │ │ ├── StatusMapping.php │ │ └── composer.json │ │ ├── Console │ │ ├── Cli.php │ │ ├── Common │ │ │ ├── Exception │ │ │ │ ├── PasswordChangeException.php │ │ │ │ ├── SettingUpdateException.php │ │ │ │ └── UserUpdateException.php │ │ │ ├── Setting.php │ │ │ └── User.php │ │ ├── Exception │ │ │ ├── CommandFailedException.php │ │ │ ├── UnsupportedCommandException.php │ │ │ └── WrongArgumentsException.php │ │ └── Modules │ │ │ ├── CacheModule.php │ │ │ ├── DatabaseModule.php │ │ │ ├── InstallModule.php │ │ │ ├── LogModule.php │ │ │ ├── ModuleBase.php │ │ │ ├── ModuleInterface.php │ │ │ └── UserModule.php │ │ ├── Container │ │ ├── Container.php │ │ ├── Exception │ │ │ └── ValueNotFoundException.php │ │ └── composer.json │ │ ├── Database │ │ ├── Connection.php │ │ ├── Ddl │ │ │ └── Column │ │ │ │ ├── Bit.php │ │ │ │ ├── Boolean.php │ │ │ │ ├── CollectionLength.php │ │ │ │ ├── Custom.php │ │ │ │ ├── Double.php │ │ │ │ ├── Enum.php │ │ │ │ ├── File.php │ │ │ │ ├── LongBlob.php │ │ │ │ ├── LongText.php │ │ │ │ ├── MediumBlob.php │ │ │ │ ├── MediumInteger.php │ │ │ │ ├── MediumText.php │ │ │ │ ├── Numeric.php │ │ │ │ ├── Real.php │ │ │ │ ├── Serial.php │ │ │ │ ├── Set.php │ │ │ │ ├── SmallInteger.php │ │ │ │ ├── TinyBlob.php │ │ │ │ ├── TinyInteger.php │ │ │ │ ├── TinyText.php │ │ │ │ └── Uuid.php │ │ ├── Exception │ │ │ ├── CollectionAlreadyExistsException.php │ │ │ ├── CollectionHasNotStatusInterfaceException.php │ │ │ ├── CollectionNotFoundException.php │ │ │ ├── CollectionNotManagedException.php │ │ │ ├── ConnectionFailedException.php │ │ │ ├── CustomUiValidationError.php │ │ │ ├── DbException.php │ │ │ ├── DuplicateItemException.php │ │ │ ├── FieldAlreadyExistsException.php │ │ │ ├── FieldAlreadyHasUniqueKeyException.php │ │ │ ├── FieldNotFoundException.php │ │ │ ├── FieldNotManagedException.php │ │ │ ├── ForbiddenSystemTableDirectAccessException.php │ │ │ ├── InvalidFieldException.php │ │ │ ├── InvalidQueryException.php │ │ │ ├── ItemNotFoundException.php │ │ │ ├── RelationshipMetadataException.php │ │ │ ├── RevisionInvalidDeltaException.php │ │ │ ├── RevisionNotFoundException.php │ │ │ ├── StatusMappingEmptyException.php │ │ │ ├── StatusMappingWrongValueTypeException.php │ │ │ ├── SuppliedArrayAsColumnValue.php │ │ │ └── UnknownDataTypeException.php │ │ ├── Filters │ │ │ ├── Filter.php │ │ │ └── In.php │ │ ├── Query │ │ │ ├── Builder.php │ │ │ └── Relations │ │ │ │ ├── ManyToManyRelation.php │ │ │ │ ├── ManyToOneRelation.php │ │ │ │ └── OneToManyRelation.php │ │ ├── README.md │ │ ├── Repositories │ │ │ ├── AbstractRepository.php │ │ │ ├── Repository.php │ │ │ ├── RepositoryFactory.php │ │ │ └── RepositoryInterface.php │ │ ├── ResultItem.php │ │ ├── ResultSet.php │ │ ├── RowGateway │ │ │ ├── BaseRowGateway.php │ │ │ ├── DirectusMediaRowGateway.php │ │ │ └── DirectusUsersRowGateway.php │ │ ├── Schema │ │ │ ├── DataTypes.php │ │ │ ├── Object │ │ │ │ ├── AbstractObject.php │ │ │ │ ├── Collection.php │ │ │ │ ├── Field.php │ │ │ │ └── FieldRelationship.php │ │ │ ├── SchemaFactory.php │ │ │ ├── SchemaManager.php │ │ │ └── Sources │ │ │ │ ├── AbstractSchema.php │ │ │ │ ├── MySQLSchema.php │ │ │ │ ├── SQLiteSchema.php │ │ │ │ └── SchemaInterface.php │ │ ├── SchemaService.php │ │ ├── TableGateway │ │ │ ├── BaseTableGateway.php │ │ │ ├── DirectusActivityTableGateway.php │ │ │ ├── DirectusCollectionPresetsTableGateway.php │ │ │ ├── DirectusCollectionsTableGateway.php │ │ │ ├── DirectusPermissionsTableGateway.php │ │ │ ├── DirectusRolesTableGateway.php │ │ │ ├── DirectusSettingsTableGateway.php │ │ │ ├── DirectusUsersTableGateway.php │ │ │ └── RelationalTableGateway.php │ │ ├── TableGatewayFactory.php │ │ └── composer.json │ │ ├── Embed │ │ ├── EmbedManager.php │ │ └── Provider │ │ │ ├── AbstractProvider.php │ │ │ ├── ProviderInterface.php │ │ │ ├── VimeoProvider.php │ │ │ └── YoutubeProvider.php │ │ ├── Exception │ │ ├── BadRequestException.php │ │ ├── BadRequestExceptionInterface.php │ │ ├── ConflictExceptionInterface.php │ │ ├── ErrorException.php │ │ ├── ErrorExceptionInterface.php │ │ ├── Exception.php │ │ ├── ForbiddenException.php │ │ ├── ForbiddenExceptionInterface.php │ │ ├── Http │ │ │ ├── Auth │ │ │ │ └── ForbiddenException.php │ │ │ ├── BadRequestException.php │ │ │ └── NotFoundException.php │ │ ├── InvalidPathException.php │ │ ├── InvalidPayloadException.php │ │ ├── MethodNotAllowedException.php │ │ ├── NotFoundException.php │ │ ├── NotFoundExceptionInterface.php │ │ ├── RuntimeException.php │ │ ├── UnauthorizedException.php │ │ ├── UnauthorizedExceptionInterface.php │ │ ├── UnprocessableEntityException.php │ │ └── UnprocessableEntityExceptionInterface.php │ │ ├── Filesystem │ │ ├── Exception │ │ │ ├── FailedUploadException.php │ │ │ ├── FilesystemException.php │ │ │ └── ForbiddenException.php │ │ ├── Files.php │ │ ├── Filesystem.php │ │ ├── FilesystemFactory.php │ │ ├── Thumbnail.php │ │ ├── Thumbnailer.php │ │ └── composer.json │ │ ├── Hash │ │ ├── Exception │ │ │ └── HasherNotFoundException.php │ │ ├── HashManager.php │ │ ├── Hasher │ │ │ ├── AbstractHashHasher.php │ │ │ ├── BCryptHasher.php │ │ │ ├── CoreHasher.php │ │ │ ├── HasherInterface.php │ │ │ ├── MD5Hasher.php │ │ │ ├── Sha1Hasher.php │ │ │ ├── Sha224Hasher.php │ │ │ ├── Sha256Hasher.php │ │ │ ├── Sha384Hasher.php │ │ │ └── Sha512Hasher.php │ │ └── composer.json │ │ ├── Hook │ │ ├── Emitter.php │ │ ├── HookInterface.php │ │ ├── Payload.php │ │ ├── README.md │ │ └── composer.json │ │ ├── Mail │ │ ├── Exception │ │ │ ├── InvalidTransportException.php │ │ │ ├── InvalidTransportObjectException.php │ │ │ └── TransportNotFoundException.php │ │ ├── Mailer.php │ │ ├── Message.php │ │ ├── TransportManager.php │ │ └── Transports │ │ │ ├── AbstractTransport.php │ │ │ ├── SendMailTransport.php │ │ │ ├── SimpleFileTransport.php │ │ │ └── SmtpTransport.php │ │ ├── Permissions │ │ ├── Acl.php │ │ ├── Exception │ │ │ ├── ForbiddenCollectionAlterException.php │ │ │ ├── ForbiddenCollectionCreateException.php │ │ │ ├── ForbiddenCollectionDeleteException.php │ │ │ ├── ForbiddenCollectionReadException.php │ │ │ ├── ForbiddenCollectionUpdateException.php │ │ │ ├── ForbiddenCommentCreateException.php │ │ │ ├── ForbiddenCommentDeleteException.php │ │ │ ├── ForbiddenCommentUpdateException.php │ │ │ ├── ForbiddenException.php │ │ │ ├── ForbiddenFieldReadException.php │ │ │ ├── ForbiddenFieldWriteException.php │ │ │ └── PermissionException.php │ │ ├── README.md │ │ └── composer.json │ │ ├── Services │ │ ├── AbstractExtensionsController.php │ │ ├── AbstractService.php │ │ ├── ActivityService.php │ │ ├── AuthService.php │ │ ├── CollectionPresetsService.php │ │ ├── FilesServices.php │ │ ├── InstanceService.php │ │ ├── InterfacesService.php │ │ ├── ItemsService.php │ │ ├── ListingsService.php │ │ ├── MailService.php │ │ ├── PagesService.php │ │ ├── PermissionsService.php │ │ ├── RelationsService.php │ │ ├── RevisionsService.php │ │ ├── RolesService.php │ │ ├── ScimService.php │ │ ├── ServerService.php │ │ ├── SettingsService.php │ │ ├── TablesService.php │ │ ├── UsersService.php │ │ └── UtilsService.php │ │ ├── Session │ │ ├── Session.php │ │ └── Storage │ │ │ ├── ArraySessionStorage.php │ │ │ ├── NativeSessionStorage.php │ │ │ └── SessionStorageInterface.php │ │ ├── Util │ │ ├── ArrayUtils.php │ │ ├── DateTimeUtils.php │ │ ├── Formatting.php │ │ ├── Git.php │ │ ├── Installation │ │ │ ├── InstallerUtils.php │ │ │ └── stubs │ │ │ │ └── config.stub │ │ ├── JWTUtils.php │ │ ├── README.md │ │ ├── SchemaUtils.php │ │ ├── StringUtils.php │ │ └── composer.json │ │ └── Validator │ │ ├── Constraints │ │ ├── Required.php │ │ └── RequiredValidator.php │ │ ├── Exception │ │ ├── InvalidRequestException.php │ │ └── UnknownConstraintException.php │ │ └── Validator.php ├── endpoints │ ├── Activity.php │ ├── Auth.php │ ├── CollectionPresets.php │ ├── Collections.php │ ├── Fields.php │ ├── Files.php │ ├── Home.php │ ├── Instances.php │ ├── Interfaces.php │ ├── Items.php │ ├── Listings.php │ ├── Mail.php │ ├── Pages.php │ ├── Permissions.php │ ├── Relations.php │ ├── Revisions.php │ ├── Roles.php │ ├── ScimTwo.php │ ├── Server.php │ ├── Settings.php │ ├── Types.php │ ├── Users.php │ └── Utils.php ├── helpers │ ├── all.php │ ├── app.php │ ├── arrays.php │ ├── bytes.php │ ├── constants.php │ ├── cors.php │ ├── extensions.php │ ├── file.php │ ├── items.php │ ├── mail.php │ ├── server.php │ ├── settings.php │ ├── sorting.php │ ├── url.php │ └── uuid.php ├── mail │ ├── base.twig │ ├── footer.twig │ ├── forgot-password.twig │ ├── new-install.twig │ ├── reset-password.twig │ └── user-invitation.twig ├── schema.sql ├── services │ └── .gitkeep └── web.php └── tests ├── api ├── AclTest.php ├── Application │ └── ApplicationTest.php ├── Authentication │ └── ProviderTest.php ├── Collection │ └── CollectionTest.php ├── Config │ └── ConfigTest.php ├── ContainerTest.php ├── Database │ ├── ConnectionTest.php │ ├── Ddl │ │ └── ColumnBooleanTest.php │ ├── Exception │ │ └── DuplicateEntryExceptionTest.php │ ├── Query │ │ └── BuilderTest.php │ ├── RowGateway │ │ └── BaseRowGatewayTest.php │ ├── SchemaTest.php │ ├── Schemas │ │ ├── MySQLSchemaTest.php │ │ └── Object │ │ │ ├── CollectionTest.php │ │ │ ├── FieldRelationshipTest.php │ │ │ └── FieldTest.php │ └── TableGateway │ │ └── AclAwareTableGatewayTest.php ├── Embed │ ├── EmbedManagerTest.php │ ├── VimeoProviderTest.php │ └── YoutubeProviderTest.php ├── Hook │ └── EmitterTest.php ├── Session │ ├── SessionStorageTest.php │ └── SessionTest.php ├── Twig │ └── TwigTest.php ├── Util │ ├── ArrayUtilsTest.php │ ├── DateTimeUtilsTest.php │ ├── FormattingTest.php │ ├── GitUtilsTest.php │ ├── Installation │ │ ├── InstallerUtilsTest.php │ │ ├── config │ │ │ └── .gitkeep │ │ └── mock │ │ │ ├── config.sample.php │ │ │ └── config.sample2.php │ ├── JWTTest.php │ ├── SchemaUtilTest.php │ └── StringUtilsTest.php ├── Validator │ └── ValidatorTest.php └── functionsTest.php ├── assets ├── image.jpeg └── travis-ci-apache ├── db.sql ├── io ├── ActivityCommentTest.php ├── ActivitySkipTest.php ├── ActivityTest.php ├── AuthenticationTest.php ├── CollectionPresetsTest.php ├── CollectionsTest.php ├── FieldsTest.php ├── FilesTest.php ├── GeneralTest.php ├── ItemsBatchTest.php ├── ItemsTest.php ├── PermissionsTest.php ├── RevisionsTest.php ├── RolesTest.php ├── SettingsTest.php ├── UsersTest.php └── UtilsTest.php └── utils ├── assert.php ├── database.php ├── helper.php ├── io_functions.php └── request.php /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 4 7 | end_of_line = lf 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.{js,json,yml,vue}] 12 | indent_size = 2 13 | 14 | [*.md] 15 | trim_trailing_whitespace = false 16 | 17 | # Tabs 18 | [*.{scss,sass,css,html,handlebars}] 19 | indent_style = tab 20 | indent_size = 4 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **For security issues, please email support@getdirectus.com directly.** 2 | 3 | To help us resolve your issue more quickly, please use the following template: 4 | 5 | For feature requests/voting please use the [Directus Request Tool](https://request.getdirectus.com/), or come chat with us through [getdirectus.com](http://getdirectus.com). Accepted feature requests will be added to GitHub Issues for assignment. 6 | 7 | ## Version Info 8 | - Directus API version (Or commit hash): 9 | - PHP version: 10 | - MySQL version: 11 | - Web server: (Ex. Apache, nginx or IIS?) 12 | - OS name and version: 13 | 14 | ## Expected Behavior 15 | 16 | 17 | ## Actual Behavior 18 | 19 | 20 | ## Steps to Reproduce 21 | 1. 22 | 2. 23 | 3. 24 | 25 | ## Schema Dump, Logs, or Screenshots 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. iOS] 25 | - Browser [e.g. chrome, safari] 26 | - Version [e.g. 22] 27 | 28 | **Smartphone (please complete the following information):** 29 | - Device: [e.g. iPhone6] 30 | - OS: [e.g. iOS8.1] 31 | - Browser [e.g. stock browser, safari] 32 | - Version [e.g. 22] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/Feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore configuration files 2 | config/* 3 | !config/migrations.php 4 | !config/api_sample.php 5 | 6 | public/core/ 7 | 8 | documents 9 | 10 | assets/css/*.map 11 | 12 | Icon 13 | .DS_Store 14 | dist/* 15 | 16 | # Don't track composer phar/vendors 17 | composer.phar 18 | composer.lock 19 | .lock 20 | /vendor 21 | 22 | # Ignore log files, but track the log README 23 | *.log 24 | 25 | !customs/endpoints/_example.php 26 | customs/extensions/* 27 | !customs/extensions/.gitignore 28 | !customs/extensions/_example/ 29 | customs/interfaces/* 30 | !customs/interfaces/.gitignore 31 | !customs/interfaces/_example 32 | /customs/listviews/* 33 | !customs/listviews/.gitignore 34 | !storage/uploads/.gitignore 35 | !storage/uploads/thumbs/.gitignore 36 | 37 | thumbnail/**/ 38 | 39 | # Ignore dev files 40 | .idea 41 | .sass-cache 42 | .gulp-scss-cache 43 | node_modules 44 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | filter: 2 | paths: ['api/*', 'installation/*', 'bin/*', 'index.php'] 3 | 4 | checks: 5 | php: true 6 | 7 | tools: 8 | external_code_coverage: 9 | timeout: 600 10 | runs: 2 11 | -------------------------------------------------------------------------------- /bin/build_directus.sh: -------------------------------------------------------------------------------- 1 | rm -rf .directus-build 2 | git clone https://github.com/directus/directus.git .directus-build 3 | 4 | pushd .directus-build 5 | npm install 6 | gulp build 7 | gulp deploy 8 | popd 9 | 10 | rm -rf .directus-build 11 | -------------------------------------------------------------------------------- /bin/build_subtree.sh: -------------------------------------------------------------------------------- 1 | git subsplit init git@github.com:directus/directus.git 2 | 3 | # Collection 4 | git subsplit publish --heads="version/6.4" --no-tags --debug api/core/Directus/Collection:git@github.com:directus/directus-collection.git 5 | 6 | # Config 7 | git subsplit publish --heads="version/6.4" --no-tags --debug api/core/Directus/Config:git@github.com:directus/directus-config.git 8 | 9 | # Permissions 10 | git subsplit publish --heads="version/6.4" --no-tags --debug api/core/Directus/Permissions:git@github.com:directus/directus-permissions.git 11 | 12 | # Database 13 | git subsplit publish --heads="version/6.4" --no-tags --debug api/core/Directus/Database:git@github.com:directus/directus-database.git 14 | 15 | # Filesystem 16 | git subsplit publish --heads="version/6.4" --no-tags --debug api/core/Directus/Filesystem:git@github.com:directus/directus-filesystem.git 17 | 18 | # Hash 19 | git subsplit publish --heads="version/6.4" --no-tags --debug api/core/Directus/Hash:git@github.com:directus/directus-hash.git 20 | 21 | # Hooks 22 | git subsplit publish --heads="version/6.4" --no-tags --debug api/core/Directus/Hook:git@github.com:directus/directus-hook.git 23 | 24 | # Utils 25 | git subsplit publish --heads="version/6.4" --no-tags --debug api/core/Directus/Util:git@github.com:directus/directus-php-utils.git 26 | 27 | rm -rf .subsplit 28 | -------------------------------------------------------------------------------- /bin/directus: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | run(); 10 | 11 | ?> 12 | -------------------------------------------------------------------------------- /bin/runtests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Command line runner for unit tests for composer projects 5 | # (c) Del 2015 http://www.babel.com.au/ 6 | # No Rights Reserved 7 | # 8 | 9 | # 10 | # Clean up after any previous test runs 11 | # 12 | mkdir -p documents 13 | rm -rf documents/coverage-html-new 14 | rm -f documents/coverage.xml 15 | 16 | # 17 | # Run phpunit 18 | # 19 | vendor/bin/phpunit --coverage-html documents/coverage-html-new --coverage-clover documents/coverage.xml 20 | 21 | if [ -d documents/coverage-html-new ]; then 22 | rm -rf documents/coverage-html 23 | mv documents/coverage-html-new documents/coverage-html 24 | fi 25 | 26 | -------------------------------------------------------------------------------- /config/migrations.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'migrations' => '%%PHINX_CONFIG_DIR%%/../migrations/db/schemas', 6 | 'seeds' => '%%PHINX_CONFIG_DIR%%/../migrations/db/seeds' 7 | ], 8 | 9 | 'version_order' => 'creation', 10 | 11 | 'environments' => [ 12 | 'default_migration_table' => 'directus_migrations', 13 | 'default_database' => 'development' 14 | ] 15 | ]; 16 | -------------------------------------------------------------------------------- /docs/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | base: '/api/', 3 | // NOTE: Most of this config must be the same as the one in the app repo to make sure 4 | // it's consistent when navigating between the two. 5 | title: 'Directus', 6 | description: 'A headless CMS that manages your content, not your workflow.', 7 | themeConfig: { 8 | // NOTE: For now, we use the github.com links as long as the docs aren't live yet 9 | nav: [ 10 | { text: 'Home', link: 'https://directus.github.io/app' }, 11 | { text: 'API Reference', link: '/' } 12 | ], 13 | repo: 'directus/api', 14 | docsDir: 'docs', 15 | editLinks: true, 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /extensions/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["env", { 4 | "modules": false, 5 | "targets": { 6 | "browsers": ["> 1%", "last 2 versions", "not ie <= 11"] 7 | } 8 | }], 9 | "stage-2" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /extensions/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .cache 3 | -------------------------------------------------------------------------------- /extensions/README.md: -------------------------------------------------------------------------------- 1 | # Directus Core Extensions 2 | 3 | These are the un-bundled / non-minified (core) extensions, like interfaces and listings. 4 | 5 | ## Installation 6 | 7 | These extensions are included in the production copy of the API by default. No further installation necessary. 8 | 9 | To work on these, you have to install the API locally according to [the local installation instructions](https://github.com/directus/api/wiki/Installing-the-API-locally). 10 | 11 | ## Usage 12 | 13 | To build the extensions production ready, run `npm run build`. This will bundle all the .vue files and move them into the public/extensions/core folder. 14 | 15 | By running `npm run dev`, a watcher is fired up which allows you to work on the extensions with hot module reloading enabled. 16 | -------------------------------------------------------------------------------- /extensions/core/interfaces/blob/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$t:blob", 3 | "version": "1.0.0", 4 | "datatypes": { 5 | "BLOB": null 6 | }, 7 | "options": { 8 | "nameField": { 9 | "name": "$t:name_field", 10 | "comment": "$t:name_field_comment", 11 | "interface": "text-input" 12 | }, 13 | "sizeField": { 14 | "name": "$t:size_field", 15 | "comment": "$t:size_field_comment", 16 | "interface": "text-input" 17 | }, 18 | "typeField": { 19 | "name": "$t:type_field", 20 | "comment": "$t:type_field_comment", 21 | "interface": "text-input" 22 | } 23 | }, 24 | "translation": { 25 | "en-US": { 26 | "blob": "Blob", 27 | "name_field": "Name Field", 28 | "name_field_comment": "Enter the name of the field that holds the file name", 29 | "size_field": "Size Field", 30 | "size_field_comment": "Enter the name of the field that holds the file size", 31 | "type_field": "Type Field", 32 | "type_field_comment": "Enter the name of the field that holds the file MIME type" 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /extensions/core/interfaces/blob/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@directus/blob", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "keywords": [], 7 | "author": "Rijk van Zanten ", 8 | "license": "GPL-3.0", 9 | "dependencies": { 10 | "filepond": "^1.2.10", 11 | "filepond-plugin-file-encode": "github:rijkvanzanten/filepond-plugin-file-encode", 12 | "filepond-plugin-image-preview": "^1.0.8" 13 | }, 14 | "devDependencies": { 15 | "@vue/component-compiler-utils": "^2.0.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /extensions/core/interfaces/blob/readonly.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | -------------------------------------------------------------------------------- /extensions/core/interfaces/checkboxes/readonly.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 35 | -------------------------------------------------------------------------------- /extensions/core/interfaces/color/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@directus/color", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "GPL-3.0", 12 | "dependencies": { 13 | "color": "^3.0.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /extensions/core/interfaces/date/interface.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 23 | 24 | 29 | -------------------------------------------------------------------------------- /extensions/core/interfaces/date/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$t:date", 3 | "version": "1.0.0", 4 | "datatypes": { 5 | "DATE": null 6 | }, 7 | "options": { 8 | "min": { 9 | "name": "$t:min", 10 | "comment": "$t:min_comment", 11 | "interface": "date" 12 | }, 13 | "max": { 14 | "name": "$t:max", 15 | "comment": "$t:max_comment", 16 | "interface": "date" 17 | }, 18 | "localized": { 19 | "name": "$t:localized", 20 | "comment": "$t:localized_comment", 21 | "interface": "toggle", 22 | "default": true 23 | }, 24 | "showRelative": { 25 | "name": "$t:relative", 26 | "comment": "$t:relative_comment", 27 | "interface": "toggle", 28 | "default": true 29 | } 30 | }, 31 | "translation": { 32 | "en-US": { 33 | "date": "Date", 34 | "min": "Minimum date", 35 | "min_comment": "Minimum date that can be chosen by the user", 36 | "max": "Maximum date", 37 | "max_comment": "Maximum date that can be chosen by the user", 38 | "localized": "Localized", 39 | "localized_comment": "Show the date localized in the output", 40 | "relative": "Show relative date", 41 | "relative_comment": "Show the date relatively (eg 2 days ago)" 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /extensions/core/interfaces/date/readonly.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 34 | -------------------------------------------------------------------------------- /extensions/core/interfaces/datetime/interface.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 37 | 38 | 43 | -------------------------------------------------------------------------------- /extensions/core/interfaces/datetime/readonly.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 41 | -------------------------------------------------------------------------------- /extensions/core/interfaces/dropdown-multiselect/readonly.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 42 | -------------------------------------------------------------------------------- /extensions/core/interfaces/dropdown/readonly.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 26 | -------------------------------------------------------------------------------- /extensions/core/interfaces/encrypted/readonly.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /extensions/core/interfaces/file-size/format-size.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Convert a value in bytes to a human readable format 3 | * @param {number} value The value in bytes to format 4 | * @return {string} The human readable filesize 5 | * @example 6 | * 7 | * const byteSize = 1500; 8 | * const fileSize = formatSize(byteSize); 9 | * // => '1.5 KB' 10 | * @see https://stackoverflow.com/a/14919494/4859211 11 | */ 12 | export default function formatSize(bytes = 0, decimal = true) { 13 | const threshold = decimal ? 1000 : 1024; 14 | 15 | if (Boolean(bytes) === false) { 16 | return "--"; 17 | } 18 | 19 | if (Math.abs(bytes) < threshold) { 20 | return `${bytes} B`; 21 | } 22 | 23 | const units = decimal 24 | ? ["kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] 25 | : ["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"]; 26 | 27 | let unitIndex = -1; 28 | let remainingBytes = bytes; 29 | 30 | do { 31 | remainingBytes /= threshold; 32 | unitIndex += 1; 33 | } while ( 34 | Math.abs(remainingBytes) >= threshold && 35 | unitIndex < units.length - 1 36 | ); 37 | 38 | return `${remainingBytes.toFixed(1)} ${units[unitIndex]}`; 39 | } 40 | -------------------------------------------------------------------------------- /extensions/core/interfaces/file-size/interface.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 19 | 20 | 25 | -------------------------------------------------------------------------------- /extensions/core/interfaces/file-size/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$t:file_size", 3 | "version": "1.0.0", 4 | "datatypes": { 5 | "INT": 11, 6 | "TINYINT": 1, 7 | "SMALLINT": 5, 8 | "MEDIUMINT": 7, 9 | "BIGINT": 18 10 | }, 11 | "options": { 12 | "placeholder": { 13 | "name": "$t:placeholder", 14 | "interface": "text-input", 15 | "comment": "$t:placeholder_comment", 16 | "length": 200 17 | }, 18 | "format": { 19 | "name": "$t:format", 20 | "comment": "$t:format_comment", 21 | "interface": "toggle", 22 | "default": true 23 | }, 24 | "decimal": { 25 | "name": "$t:decimal", 26 | "comment": "$t:decimal_comment", 27 | "interface": "toggle", 28 | "default": true 29 | } 30 | }, 31 | "translation": { 32 | "en-US": { 33 | "file_size": "File Size", 34 | "placeholder": "Placeholder", 35 | "placeholder_comment": "Enter placeholder text", 36 | "format": "Format value", 37 | "format_comment": "Show the value in a human readable way (e.g. 10MB)", 38 | "decimal": "Decimal", 39 | "decimal_comment": "Show the value in decimal counting (10MB vs 10MiB)" 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /extensions/core/interfaces/file-size/readonly.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 17 | -------------------------------------------------------------------------------- /extensions/core/interfaces/file-upload/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$t:upload", 3 | "private": true, 4 | "version": "1.0.0", 5 | "datatypes": { 6 | "BLOB": null 7 | }, 8 | "options": { 9 | "nameField": { 10 | "name": "$t:name_field", 11 | "comment": "$t:name_field_comment", 12 | "interface": "text-input" 13 | }, 14 | "sizeField": { 15 | "name": "$t:size_field", 16 | "comment": "$t:size_field_comment", 17 | "interface": "text-input" 18 | }, 19 | "typeField": { 20 | "name": "$t:type_field", 21 | "comment": "$t:type_field_comment", 22 | "interface": "text-input" 23 | } 24 | }, 25 | "translation": { 26 | "en-US": { 27 | "upload": "File Upload", 28 | "name_field": "Name Field", 29 | "name_field_comment": "Enter the name of the field that holds the file name", 30 | "size_field": "Size Field", 31 | "size_field_comment": "Enter the name of the field that holds the file size", 32 | "type_field": "Type Field", 33 | "type_field_comment": "Enter the name of the field that holds the file MIME type" 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /extensions/core/interfaces/file-upload/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@directus/file-upload", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "filepond": { 8 | "version": "1.4.0", 9 | "resolved": "https://registry.npmjs.org/filepond/-/filepond-1.4.0.tgz", 10 | "integrity": "sha512-M+pRzVangAyHNHh9tFVzRdl8xrICTH/F2gXUq+BOgelnuoCSQ8LeJQLi36t7c3oOSozu3JCR1/Q0oR7t30BoKw==" 11 | }, 12 | "filepond-plugin-file-encode": { 13 | "version": "1.0.4", 14 | "resolved": "https://registry.npmjs.org/filepond-plugin-file-encode/-/filepond-plugin-file-encode-1.0.4.tgz", 15 | "integrity": "sha512-kif22vSs3YzLuUyzEwTEr28SsPLFjXaMVh1Z6RzTK6d1s33JnmBUIDL9k6OrBtZn/6XlYRyclTkPQEM212S0HA==" 16 | }, 17 | "filepond-plugin-image-preview": { 18 | "version": "1.0.9", 19 | "resolved": "https://registry.npmjs.org/filepond-plugin-image-preview/-/filepond-plugin-image-preview-1.0.9.tgz", 20 | "integrity": "sha512-bIaT6Y5hbyCSITaSO96XdsTBUEo70ZIgzelpZbeqkJsi5BDXpxOhFtSzTMiY4c+w2wCiz/ZV0L2mmyH05uiktA==" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /extensions/core/interfaces/file-upload/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@directus/file-upload", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "keywords": [], 7 | "author": "Rijk van Zanten ", 8 | "license": "GPL-3.0", 9 | "dependencies": { 10 | "filepond": "^1.4.0", 11 | "filepond-plugin-file-encode": "^1.0.4", 12 | "filepond-plugin-image-preview": "^1.0.9" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /extensions/core/interfaces/file-upload/readonly.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 30 | 31 | 48 | -------------------------------------------------------------------------------- /extensions/core/interfaces/group/interface.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 16 | -------------------------------------------------------------------------------- /extensions/core/interfaces/group/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$t:group", 3 | "version": "1.0.0", 4 | "datatypes": { 5 | "GROUP": null 6 | }, 7 | "options": { 8 | "open": { 9 | "name": "$t:open", 10 | "comment": "$t:open_comment", 11 | "interface": "toggle", 12 | "default": true 13 | }, 14 | "title": { 15 | "name": "$t:title", 16 | "comment": "$t:title_comment", 17 | "interface": "text-input" 18 | } 19 | }, 20 | "translation": { 21 | "en-US": { 22 | "group": "Group", 23 | "open": "Default state", 24 | "open_comment": "Group is opened by default", 25 | "title": "Title", 26 | "title_comment": "Add a title to display" 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /extensions/core/interfaces/group/readonly.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /extensions/core/interfaces/json/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "JSON", 3 | "version": "1.0.0", 4 | "datatypes": { 5 | "TEXT": null, 6 | "TINYTEXT": null, 7 | "MEDIUMTEXT": null, 8 | "LONGTEXT": null, 9 | "VARCHAR": 255 10 | }, 11 | "options": { 12 | "placeholder": { 13 | "name": "$t:placeholder", 14 | "comment": "$t:placeholder_comment", 15 | "interface": "text-input", 16 | "length": 200 17 | }, 18 | "indent": { 19 | "name": "$t:indent", 20 | "comment": "$t:indent_comment", 21 | "interface": "text-input", 22 | "default": "\t" 23 | }, 24 | "valid": { 25 | "name": "$t:valid", 26 | "comment": "$t:valid_comment", 27 | "interface": "toggle", 28 | "default": true 29 | } 30 | }, 31 | "translation": { 32 | "en-US": { 33 | "json": "JSON", 34 | "placeholder": "Placeholder", 35 | "placeholder_comment": "Enter placeholder text", 36 | "indent": "Indent", 37 | "indent_comment": "What character(s) to use as indentation", 38 | "valid": "Only allow valid json", 39 | "valid_comment": "Only allow valid JSON to be saved to the database" 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /extensions/core/interfaces/json/readonly.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /extensions/core/interfaces/many-to-one/readonly.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 20 | -------------------------------------------------------------------------------- /extensions/core/interfaces/markdown/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$t:markdown", 3 | "version": "1.0.0", 4 | "datatypes": { 5 | "VARCHAR": 1024, 6 | "TINYTEXT": null, 7 | "TEXT": null, 8 | "MEDIUMTEXT": null, 9 | "LONGTEXT": null 10 | }, 11 | "options": {}, 12 | "translation": { 13 | "en-US": { 14 | "markdown": "Markdown" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /extensions/core/interfaces/markdown/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@directus/markdown", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "marked": { 8 | "version": "0.3.12", 9 | "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.12.tgz", 10 | "integrity": "sha512-k4NaW+vS7ytQn6MgJn3fYpQt20/mOgYM5Ft9BYMfQJDz2QT6yEeS9XJ8k2Nw8JTeWK/znPPW2n3UJGzyYEiMoA==" 11 | }, 12 | "vue": { 13 | "version": "2.5.16", 14 | "resolved": "https://registry.npmjs.org/vue/-/vue-2.5.16.tgz", 15 | "integrity": "sha512-/ffmsiVuPC8PsWcFkZngdpas19ABm5mh2wA7iDqcltyCTwlgZjHGeJYOXkBMo422iPwIcviOtrTCUpSfXmToLQ==" 16 | }, 17 | "vue-hot-reload-api": { 18 | "version": "2.3.0", 19 | "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.0.tgz", 20 | "integrity": "sha512-2j/t+wIbyVMP5NvctQoSUvLkYKoWAAk2QlQiilrM2a6/ulzFgdcLUJfTvs4XQ/3eZhHiBmmEojbjmM4AzZj8JA==" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /extensions/core/interfaces/markdown/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@directus/markdown", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "GPL-3.0", 12 | "dependencies": { 13 | "marked": "^0.3.12", 14 | "vue": "^2.5.16", 15 | "vue-hot-reload-api": "^2.3.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /extensions/core/interfaces/markdown/readonly.vue: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /extensions/core/interfaces/numeric/interface.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | 19 | 32 | -------------------------------------------------------------------------------- /extensions/core/interfaces/numeric/readonly.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 13 | -------------------------------------------------------------------------------- /extensions/core/interfaces/one-to-many/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$t:o2m", 3 | "version": "1.0.0", 4 | "datatypes": { 5 | "ALIAS": null 6 | }, 7 | "translation": { 8 | "en-US": { 9 | "o2m": "One to Many", 10 | "select_items": "Select the items" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /extensions/core/interfaces/one-to-many/readonly.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | -------------------------------------------------------------------------------- /extensions/core/interfaces/primary-key/interface.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 42 | 43 | 48 | -------------------------------------------------------------------------------- /extensions/core/interfaces/primary-key/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$t:primary_key", 3 | "version": "1.0.0", 4 | "datatypes": { 5 | "INT": 11, 6 | "TINYINT": 1, 7 | "SMALLINT": 5, 8 | "MEDIUMINT": 7, 9 | "BIGINT": 18, 10 | "VARCHAR": 100, 11 | "CHAR": 10 12 | }, 13 | "options": {}, 14 | "translation": { 15 | "en-US": { 16 | "primary_key": "Primary Key" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /extensions/core/interfaces/primary-key/readonly.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | -------------------------------------------------------------------------------- /extensions/core/interfaces/radio-buttons/interface.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 32 | -------------------------------------------------------------------------------- /extensions/core/interfaces/radio-buttons/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$t:radio", 3 | "version": "1.0.0", 4 | "datatypes": { 5 | "VARCHAR": 255, 6 | "CHAR": 10, 7 | "TINYTEXT": null, 8 | "TEXT": null, 9 | "MEDIUMTEXT": null, 10 | "LONGTEXT": null 11 | }, 12 | "fieldset": true, 13 | "options": { 14 | "choices": { 15 | "name": "$t:choices", 16 | "comment": "$t:choices_comment", 17 | "interface": "json", 18 | "type": "JSON", 19 | "default": { 20 | "value1": "$t:option 1", 21 | "value2": "$t:option 2" 22 | } 23 | }, 24 | "formatting": { 25 | "name": "$t:formatting", 26 | "comment": "$t:formatting_comment", 27 | "interface": "toggle", 28 | "type": "BOOLEAN", 29 | "default": true 30 | } 31 | }, 32 | "translation": { 33 | "en-US": { 34 | "radio": "Radio Buttons", 35 | "choices": "choices", 36 | "choices_comment": "Enter JSON key value pairs with the saved value and text displayed.", 37 | "option": "Option", 38 | "formatting": "Show display text", 39 | "formatting_comment": "Render the values as the display values" 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /extensions/core/interfaces/radio-buttons/readonly.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 21 | -------------------------------------------------------------------------------- /extensions/core/interfaces/single-file/interface.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | -------------------------------------------------------------------------------- /extensions/core/interfaces/single-file/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$t:single-file", 3 | "version": "1.0.0", 4 | "datatypes": { 5 | "INT": null 6 | }, 7 | "options": {}, 8 | "translation": { 9 | "en-US": { 10 | "single-file": "Single File" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /extensions/core/interfaces/single-file/readonly.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 29 | 30 | 47 | -------------------------------------------------------------------------------- /extensions/core/interfaces/slider/interface.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 21 | 22 | 35 | -------------------------------------------------------------------------------- /extensions/core/interfaces/slider/readonly.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | -------------------------------------------------------------------------------- /extensions/core/interfaces/slug/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@directus/slug", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "slugify": { 8 | "version": "1.2.9", 9 | "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.2.9.tgz", 10 | "integrity": "sha512-n0cdJ+kN3slJu8SbZXt/EHjljBqF6MxvMGSg/NPpBzoY7yyXoH38wp/ox20a1JaG1KgmdTN5Lf3aS9+xB2Y2aQ==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /extensions/core/interfaces/slug/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@directus/slug", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "Rijk van Zanten ", 11 | "license": "GPL-3.0", 12 | "dependencies": { 13 | "slugify": "^1.2.9" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /extensions/core/interfaces/slug/readonly.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | -------------------------------------------------------------------------------- /extensions/core/interfaces/sort/interface.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 27 | 28 | 33 | -------------------------------------------------------------------------------- /extensions/core/interfaces/sort/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$t:sort", 3 | "version": "1.0.0", 4 | "datatypes": { 5 | "INT": 11, 6 | "TINYINT": 1, 7 | "SMALLINT": 5, 8 | "MEDIUMINT": 7, 9 | "BIGINT": 18 10 | }, 11 | "options": {}, 12 | "translation": { 13 | "en-US": { 14 | "sort": "Sort" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /extensions/core/interfaces/sort/readonly.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | -------------------------------------------------------------------------------- /extensions/core/interfaces/status/interface.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 32 | 33 | 41 | -------------------------------------------------------------------------------- /extensions/core/interfaces/status/readonly.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 27 | 28 | 44 | -------------------------------------------------------------------------------- /extensions/core/interfaces/tags/readonly.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 37 | 38 | 53 | -------------------------------------------------------------------------------- /extensions/core/interfaces/text-input/readonly.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 23 | -------------------------------------------------------------------------------- /extensions/core/interfaces/textarea/interface.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 19 | 20 | 25 | -------------------------------------------------------------------------------- /extensions/core/interfaces/textarea/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$t:textarea", 3 | "version": "1.0.0", 4 | "datatypes": { 5 | "TEXT": null, 6 | "TINYTEXT": null, 7 | "MEDIUMTEXT": null, 8 | "LONGTEXT": null, 9 | "VARCHAR": 255 10 | }, 11 | "options": { 12 | "rows": { 13 | "name": "$t:rows", 14 | "comment": "$t:rows_comment", 15 | "interface": "numeric", 16 | "options": { 17 | "min": 5, 18 | "max": 100 19 | }, 20 | "default": 12 21 | }, 22 | "placeholder": { 23 | "name": "$t:placeholder", 24 | "comment": "$t:placeholder_comment", 25 | "interface": "text-input", 26 | "length": 200 27 | } 28 | }, 29 | "translation": { 30 | "en-US": { 31 | "textarea": "Textarea", 32 | "rows": "Rows", 33 | "rows_comment": "The number of text rows available for the input before scrolling", 34 | "placeholder": "Placeholder", 35 | "placeholder_comment": "Enter placeholder text" 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /extensions/core/interfaces/textarea/readonly.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 12 | -------------------------------------------------------------------------------- /extensions/core/interfaces/time/interface.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | 19 | 24 | -------------------------------------------------------------------------------- /extensions/core/interfaces/time/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$t:time", 3 | "version": "1.0.0", 4 | "datatypes": { 5 | "TIME": null 6 | }, 7 | "options": { 8 | "display24HourClock": { 9 | "name": "$t:24hour", 10 | "comment": "$t:24hour_comment", 11 | "interface": "toggle", 12 | "default": true 13 | } 14 | }, 15 | "translation": { 16 | "en-US": { 17 | "time": "Time", 18 | "include_seconds": "Include seconds", 19 | "include_seconds_comment": "Include seconds in the interface", 20 | "24hour": "Display 24 hour clock", 21 | "24hour_comment": "Show the time in 24-hour format (eg.: 15:30)" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /extensions/core/interfaces/time/readonly.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 45 | -------------------------------------------------------------------------------- /extensions/core/interfaces/toggle/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$t:toggle", 3 | "version": "1.0.0", 4 | "datatypes": { 5 | "BOOLEAN": null 6 | }, 7 | "options": { 8 | "label": { 9 | "name": "$t:label", 10 | "comment": "$t:label_comment", 11 | "interface": "text-input" 12 | }, 13 | "checkbox": { 14 | "name": "$t:checkbox", 15 | "comment": "$t:checkbox_comment", 16 | "interface": "toggle", 17 | "default": false 18 | } 19 | }, 20 | "translation": { 21 | "en-US": { 22 | "toggle": "Toggle", 23 | "label": "Label", 24 | "label_comment": "Label to show next to the toggle", 25 | "checkbox": "Show as Checkbox", 26 | "checkbox_comment": "Display a checkbox instead of the default switch" 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /extensions/core/interfaces/toggle/readonly.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 21 | 22 | 27 | -------------------------------------------------------------------------------- /extensions/core/interfaces/wysiwyg-full/interface.vue: -------------------------------------------------------------------------------- 1 | w 4 | 5 | 47 | -------------------------------------------------------------------------------- /extensions/core/interfaces/wysiwyg-full/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@directus/wysiwyg", 3 | "version": "1.0.0", 4 | "description": "", 5 | "keywords": [], 6 | "author": "", 7 | "license": "GPL-3.0", 8 | "dependencies": { 9 | "quill": "^1.3.6" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /extensions/core/interfaces/wysiwyg-full/readonly.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | -------------------------------------------------------------------------------- /extensions/core/interfaces/wysiwyg/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@directus/wysiwyg", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "medium-editor": { 8 | "version": "5.23.3", 9 | "resolved": "https://registry.npmjs.org/medium-editor/-/medium-editor-5.23.3.tgz", 10 | "integrity": "sha512-he9/TdjX8f8MGdXGfCs8AllrYnqXJJvjNkDKmPg3aPW/uoIrlRqtkFthrwvmd+u4QyzEiadhCCM0EwTiRdUCJw==" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /extensions/core/interfaces/wysiwyg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@directus/wysiwyg", 3 | "version": "1.0.0", 4 | "description": "", 5 | "keywords": [], 6 | "author": "", 7 | "license": "GPL-3.0", 8 | "dependencies": { 9 | "medium-editor": "^5.23.3" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /extensions/core/interfaces/wysiwyg/readonly.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | -------------------------------------------------------------------------------- /extensions/core/listings/tabular/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$t:tabular", 3 | "version": "1.0.0", 4 | "translation": { 5 | "en-US": { 6 | "tabular": "Table", 7 | "fields": "Fields" 8 | }, 9 | "nl-NL": { 10 | "tabular": "Tabel", 11 | "fields": "Kolommen" 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /extensions/core/listings/tile/meta.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "$t:tile", 3 | "version": "1.0.0", 4 | "translation": { 5 | "en-US": { 6 | "tile": "Tiles", 7 | "title": "Title", 8 | "subtitle": "Subtitle", 9 | "src": "Image source", 10 | "content": "Body content" 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /extensions/json-minifier.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This transformation is used in the copy command for the meta.json files 3 | * 4 | * It takes the contents of the json file, minifies it, and returns it. 5 | */ 6 | 7 | var through = require("through2"); 8 | var jsonminify = require("jsonminify"); 9 | 10 | module.exports = function(file) { 11 | return through(function(buf, enc, next) { 12 | this.push(jsonminify(buf.toString("utf8"))); 13 | next(); 14 | }); 15 | }; 16 | -------------------------------------------------------------------------------- /extensions/mixins/interface.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | props: { 3 | name: { 4 | type: String, 5 | required: true 6 | }, 7 | value: { 8 | type: null, 9 | default: null 10 | }, 11 | type: { 12 | type: String, 13 | required: true 14 | }, 15 | length: { 16 | type: [String, Number], 17 | default: null 18 | }, 19 | readonly: { 20 | type: Boolean, 21 | default: false 22 | }, 23 | required: { 24 | type: Boolean, 25 | default: false 26 | }, 27 | options: { 28 | type: Object, 29 | default: () => ({}) 30 | }, 31 | newItem: { 32 | type: Boolean, 33 | default: false 34 | }, 35 | relationship: { 36 | type: Object, 37 | default: null 38 | } 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /extensions/mixins/listing.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | props: { 3 | primaryKeyField: { 4 | type: String, 5 | required: true 6 | }, 7 | fields: { 8 | type: Object, 9 | required: true 10 | }, 11 | items: { 12 | type: Array, 13 | default: () => ([]) 14 | }, 15 | viewOptions: { 16 | type: Object, 17 | default: () => ({}) 18 | }, 19 | viewQuery: { 20 | type: Object, 21 | default: () => ({}) 22 | }, 23 | loading: { 24 | type: Boolean, 25 | default: false 26 | }, 27 | lazyLoading: { 28 | type: Boolean, 29 | default: false 30 | }, 31 | selection: { 32 | type: Array, 33 | default: () => [] 34 | }, 35 | link: { 36 | type: String, 37 | default: null 38 | } 39 | } 40 | }; 41 | -------------------------------------------------------------------------------- /extensions/mixins/page.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 3 | }; 4 | -------------------------------------------------------------------------------- /logs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsguru-git/api/1f6e97f6e33df69d280659cbeae2a1c630011593/logs/.gitkeep -------------------------------------------------------------------------------- /migrations/db/seeds/FileSeeder.php: -------------------------------------------------------------------------------- 1 | 1, 19 | 'filename' => '00000000001.jpg', 20 | 'title' => 'Mountain Range', 21 | 'description' => 'A gorgeous view of this wooded mountain range', 22 | 'location' => 'Earth', 23 | 'tags' => 'trees,rocks,nature,mountains,forest', 24 | 'width' => 1800, 25 | 'height' => 1200, 26 | 'filesize' => 602058, 27 | 'type' => 'image/jpeg', 28 | 'charset' => 'binary', 29 | 'upload_user' => 1, 30 | 'upload_date' => \Directus\Util\DateTimeUtils::nowInUTC()->toString(), 31 | 'storage_adapter' => 'local' 32 | ]; 33 | 34 | $files = $this->table('directus_files'); 35 | $files->insert($data)->save(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /migrations/db/seeds/RolesSeeder.php: -------------------------------------------------------------------------------- 1 | 1, 20 | 'name' => 'Administrator', 21 | 'description' => 'Admins have access to all managed data within the system by default' 22 | ], 23 | [ 24 | 'id' => 2, 25 | 'name' => 'Public', 26 | 'description' => 'This sets the data that is publicly available through the API without a token' 27 | ] 28 | ]; 29 | 30 | $groups = $this->table('directus_roles'); 31 | $groups->insert($data)->save(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@directus/api", 3 | "private": true, 4 | "version": "2.0.0-beta.1", 5 | "description": "This API is currently under development for the next major [Directus](https://github.com/directus/directus) version.", 6 | "main": "index.js", 7 | "repository": "directus/api", 8 | "keywords": [ 9 | "directus", 10 | "api" 11 | ], 12 | "scripts": { 13 | "docs:dev": "vuepress dev docs", 14 | "docs:build": "vuepress build docs" 15 | }, 16 | "author": "Welling Guzman ", 17 | "license": "GPL-3.0", 18 | "bugs": { 19 | "url": "https://github.com/directus/api/issues" 20 | }, 21 | "homepage": "https://github.com/directus/api#readme", 22 | "devDependencies": { 23 | "vuepress": "^0.7.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | ./tests 9 | 10 | 11 | 12 | 13 | ./src/core 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /public/.htaccess: -------------------------------------------------------------------------------- 1 | # Comment this line if you are getting: "Option SymLinksIfOwnerMatch not allowed here" error 2 | # in Apache 3 | Options +SymLinksIfOwnerMatch 4 | 5 | 6 | RewriteEngine On 7 | # Uncomment this if you are getting routing errors: 8 | # RewriteBase /api 9 | 10 | RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 11 | 12 | # Yield static media 13 | RewriteCond %{REQUEST_FILENAME} !-f 14 | 15 | # Map extension requests to their front controller 16 | # RewriteRule ^extensions/([^/]+) index.php?run_extension=$1&%{QUERY_STRING} [L] 17 | 18 | # Map all other requests to the main front controller, invoking the API router 19 | RewriteRule ^ index.php?%{QUERY_STRING} [L] 20 | 21 | 22 | 23 | # Set CORS header for static files 24 | Header set Access-Control-Allow-Origin "*" 25 | 26 | 27 | 28 | # Fix $HTTP_RAW_POST_DATA deprecated warning 29 | php_value always_populate_raw_post_data -1 30 | 31 | 32 | # Prevent PageSpeed module from rewriting the templates files 33 | # Avoiding it from breaking the template 34 | # 35 | # ModPagespeedDisallow "*/app/**/*.twig" 36 | # 37 | -------------------------------------------------------------------------------- /public/extensions/.gitignore: -------------------------------------------------------------------------------- 1 | core/interfaces 2 | core/listings 3 | core/pages 4 | -------------------------------------------------------------------------------- /public/extensions/core/auth/facebook/Provider.php: -------------------------------------------------------------------------------- 1 | provider = new Facebook([ 33 | 'clientId' => $this->config->get('client_id'), 34 | 'clientSecret' => $this->config->get('client_secret'), 35 | 'redirectUri' => $this->getRedirectUrl(), 36 | 'graphApiVersion' => $this->config->get('graph_api_version'), 37 | ]); 38 | 39 | return $this->provider; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /public/extensions/core/auth/facebook/auth.php: -------------------------------------------------------------------------------- 1 | \Directus\Authentication\Sso\Provider\facebook\Provider::class 5 | ]; 6 | -------------------------------------------------------------------------------- /public/extensions/core/auth/facebook/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/extensions/core/auth/github/auth.php: -------------------------------------------------------------------------------- 1 | \Directus\Authentication\Sso\Provider\github\Provider::class 5 | ]; 6 | -------------------------------------------------------------------------------- /public/extensions/core/auth/google/Provider.php: -------------------------------------------------------------------------------- 1 | provider = new Google([ 34 | 'clientId' => $this->config->get('client_id'), 35 | 'clientSecret' => $this->config->get('client_secret'), 36 | 'redirectUri' => $this->getRedirectUrl(), 37 | 'hostedDomain' => $this->config->get('hosted_domain') 38 | ]); 39 | 40 | return $this->provider; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /public/extensions/core/auth/google/auth.php: -------------------------------------------------------------------------------- 1 | \Directus\Authentication\Sso\Provider\google\Provider::class 5 | ]; 6 | -------------------------------------------------------------------------------- /public/extensions/core/auth/google/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/extensions/core/auth/okta/Provider.php: -------------------------------------------------------------------------------- 1 | provider = new Okta([ 31 | 'baseUrl' => $this->config->get('base_url'), 32 | 'clientId' => $this->config->get('client_id'), 33 | 'clientSecret' => $this->config->get('client_secret'), 34 | 'redirectUri' => $this->getRedirectUrl() 35 | ]); 36 | 37 | return $this->provider; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /public/extensions/core/auth/okta/auth.php: -------------------------------------------------------------------------------- 1 | \Directus\Authentication\Sso\Provider\okta\Provider::class 5 | ]; 6 | -------------------------------------------------------------------------------- /public/extensions/core/auth/okta/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/extensions/core/auth/twitter/Provider.php: -------------------------------------------------------------------------------- 1 | provider = new Twitter([ 23 | 'identifier' => $this->config->get('identifier'), 24 | 'secret' => $this->config->get('secret'), 25 | 'callback_uri' => $this->getRedirectUrl(), 26 | ]); 27 | 28 | return $this->provider; 29 | } 30 | 31 | /** 32 | * @inheritdoc 33 | */ 34 | public function getScopes() 35 | { 36 | return null; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /public/extensions/core/auth/twitter/auth.php: -------------------------------------------------------------------------------- 1 | \Directus\Authentication\Sso\Provider\twitter\Provider::class 5 | ]; 6 | -------------------------------------------------------------------------------- /public/extensions/core/auth/twitter/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/extensions/custom/auth/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsguru-git/api/1f6e97f6e33df69d280659cbeae2a1c630011593/public/extensions/custom/auth/.gitkeep -------------------------------------------------------------------------------- /public/extensions/custom/endpoints/.htaccess: -------------------------------------------------------------------------------- 1 | deny from all 2 | -------------------------------------------------------------------------------- /public/extensions/custom/endpoints/_directory/controllers/Home.php: -------------------------------------------------------------------------------- 1 | withJson(['data' => example_get_data()]); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /public/extensions/custom/endpoints/_directory/endpoints.php: -------------------------------------------------------------------------------- 1 | [ 7 | 'method' => 'GET', 8 | 'handler' => Home::class 9 | ], 10 | ]; 11 | -------------------------------------------------------------------------------- /public/extensions/custom/endpoints/_directory/functions.php: -------------------------------------------------------------------------------- 1 | $letter) { 17 | $letter = ord($letter) + $i + 1; 18 | $letters[$i] = chr($letter); 19 | } 20 | 21 | return implode('', $letters); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /public/extensions/custom/hooks/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsguru-git/api/1f6e97f6e33df69d280659cbeae2a1c630011593/public/extensions/custom/hooks/.gitignore -------------------------------------------------------------------------------- /public/extensions/custom/hooks/_products/BeforeInsertProducts.php: -------------------------------------------------------------------------------- 1 | set('sku', 'value'); 19 | 20 | // make sure to return the payload 21 | return $payload; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /public/extensions/custom/hooks/_products/hooks.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'collection.insert:before' => new \Directus\Custom\Hooks\Products\BeforeInsertProducts() 6 | ] 7 | ]; 8 | -------------------------------------------------------------------------------- /public/extensions/custom/hooks/_webhook/hooks.php: -------------------------------------------------------------------------------- 1 | [ 5 | // Send an alert when a post is created 6 | 'collection.insert.posts' => function (array $data) { 7 | $client = new \GuzzleHttp\Client([ 8 | 'base_uri' => 'http://example.com' 9 | ]); 10 | 11 | $data = [ 12 | 'type' => 'post', 13 | 'data' => $data 14 | ]; 15 | 16 | $response = $client->request('POST', 'alert', [ 17 | 'json' => $data 18 | ]); 19 | } 20 | ] 21 | ]; 22 | -------------------------------------------------------------------------------- /public/extensions/custom/interfaces/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | !_example 4 | !rating 5 | -------------------------------------------------------------------------------- /public/extensions/custom/listviews/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /public/extensions/custom/mail/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsguru-git/api/1f6e97f6e33df69d280659cbeae2a1c630011593/public/extensions/custom/mail/.gitkeep -------------------------------------------------------------------------------- /public/extensions/custom/migrations/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsguru-git/api/1f6e97f6e33df69d280659cbeae2a1c630011593/public/extensions/custom/migrations/.gitkeep -------------------------------------------------------------------------------- /public/index.php: -------------------------------------------------------------------------------- 1 | run(); 6 | -------------------------------------------------------------------------------- /public/storage/uploads/.gitignore: -------------------------------------------------------------------------------- 1 | /* 2 | !.gitignore 3 | !.htaccess 4 | -------------------------------------------------------------------------------- /public/storage/uploads/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | ExpiresActive On 3 | ExpiresDefault "access 1 year" 4 | 5 | 6 | 7 | ForceType text/plain 8 | 9 | 10 | # Respond with 404 if the file doesn't exists 11 | # Before the API mod_rewrite catches the request 12 | 13 | RewriteCond %{REQUEST_FILENAME} !-f 14 | RewriteRule .* - [L,R=404] 15 | 16 | -------------------------------------------------------------------------------- /public/storage/uploads/00000000001.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsguru-git/api/1f6e97f6e33df69d280659cbeae2a1c630011593/public/storage/uploads/00000000001.jpg -------------------------------------------------------------------------------- /public/thumbnail/.gitignore: -------------------------------------------------------------------------------- 1 | /thumbs 2 | -------------------------------------------------------------------------------- /public/thumbnail/.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine On 2 | RewriteRule (.*) index.php [L] 3 | -------------------------------------------------------------------------------- /public/thumbnail/img-not-found.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsguru-git/api/1f6e97f6e33df69d280659cbeae2a1c630011593/public/thumbnail/img-not-found.png -------------------------------------------------------------------------------- /src/core/Directus/Application/ErrorHandlers/MethodNotAllowedHandler.php: -------------------------------------------------------------------------------- 1 | withStatus(Response::HTTP_METHOD_NOT_ALLOWED) 21 | ->withJson(['error' => [ 22 | 'code' => MethodNotAllowedException::ERROR_CODE, 23 | 'message' => 'Method Not Allowed' 24 | ]]); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/core/Directus/Application/ErrorHandlers/NotFoundHandler.php: -------------------------------------------------------------------------------- 1 | withStatus(Response::HTTP_NOT_FOUND) 21 | ->withJson(['error' => [ 22 | 'code' => NotFoundException::ERROR_CODE, 23 | 'message' => 'Not Found' 24 | ]]); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/core/Directus/Application/ErrorHandlers/NotInstalledNotFoundHandler.php: -------------------------------------------------------------------------------- 1 | withStatus(Response::HTTP_NOT_FOUND) 21 | ->withJson(['error' => [ 22 | 'code' => NotFoundException::ERROR_CODE, 23 | 'message' => 'This instance of the Directus API has not been configured properly. Read More at: https://github.com/directus' 24 | ]]); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/core/Directus/Application/Http/Middleware/AbstractMiddleware.php: -------------------------------------------------------------------------------- 1 | container = $container; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/core/Directus/Application/Http/Middleware/AdminMiddleware.php: -------------------------------------------------------------------------------- 1 | container->get('acl'); 16 | 17 | if ($acl->isAdmin()) { 18 | return $next($request, $response); 19 | } 20 | 21 | throw new UserNotAuthenticatedException(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/core/Directus/Application/Http/Middleware/AuthenticatedMiddleware.php: -------------------------------------------------------------------------------- 1 | container->get('acl'); 16 | 17 | if ($acl->getUserId() && $acl->isPublic() !== true) { 18 | return $next($request, $response); 19 | } 20 | 21 | throw new UserNotAuthenticatedException(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/core/Directus/Application/Http/Middleware/IpRateLimitMiddleware.php: -------------------------------------------------------------------------------- 1 | acl = $acl; 16 | } 17 | 18 | /** 19 | * {@inheritdoc} 20 | */ 21 | public function getIdentity(RequestInterface $request) 22 | { 23 | return $this->acl->getUserId(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/core/Directus/Application/Http/Middleware/TableGatewayMiddleware.php: -------------------------------------------------------------------------------- 1 | container; 17 | 18 | // tablegateway dependency 19 | SchemaService::setAclInstance($container->get('acl')); 20 | SchemaService::setConnectionInstance($container->get('database')); 21 | SchemaService::setConfig($container->get('config')); 22 | BaseTableGateway::setHookEmitter($container->get('hook_emitter')); 23 | BaseTableGateway::setContainer($container); 24 | TableGatewayFactory::setContainer($container); 25 | 26 | return $next($request, $response); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/core/Directus/Application/Http/Middleware/UserRateLimitMiddleware.php: -------------------------------------------------------------------------------- 1 | container->get('acl')); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/core/Directus/Application/Http/Request.php: -------------------------------------------------------------------------------- 1 | getHeader('Origin') ?: $this->getServerParam('HTTP_ORIGIN'); 15 | } 16 | 17 | /** 18 | * Returns the Request Referer Url 19 | * 20 | * @return mixed 21 | */ 22 | public function getReferer() 23 | { 24 | return $this->getHeader('Referer') ?: $this->getServerParam('HTTP_REFERER'); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/core/Directus/Authentication/Exception/ExpiredRequestTokenException.php: -------------------------------------------------------------------------------- 1 | attributes['email'] = $email; 14 | $message = sprintf('User with email "%s" not found', $email); 15 | 16 | parent::__construct($message, static::ERROR_CODE); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/core/Directus/Authentication/Sso/SocialProviderInterface.php: -------------------------------------------------------------------------------- 1 | get('social_token'); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/core/Directus/Authentication/User/UserInterface.php: -------------------------------------------------------------------------------- 1 | tags = array_merge($this->tags, (array)$tags); 12 | 13 | return $this; 14 | } 15 | 16 | public function ttl($time) 17 | { 18 | $this->ttl = $time; 19 | } 20 | 21 | public function process($key, $bodyContent, $headers = []) 22 | { 23 | if ($key && !empty($this->tags)) { 24 | $value = ['body' => $bodyContent, 'headers' => $headers]; 25 | 26 | return $this->set($key, $value, $this->tags, $this->defaultTtl); 27 | } 28 | 29 | return false; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/core/Directus/Collection/Arrayable.php: -------------------------------------------------------------------------------- 1 | =5.4.0" 11 | }, 12 | "autoload": { 13 | "psr-4": { 14 | "Directus\\Collection\\": "" 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/core/Directus/Config/Config.php: -------------------------------------------------------------------------------- 1 | =5.4.0", 11 | "directus/collection": "^1.0.0", 12 | "directus/utils": "^1.0.0" 13 | }, 14 | "autoload": { 15 | "psr-4": { 16 | "Directus\\Config\\": "" 17 | } 18 | }, 19 | "extra": { 20 | "branch-alias": { 21 | "dev-version/6.4": "0.9.x-dev" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/core/Directus/Console/Common/Exception/PasswordChangeException.php: -------------------------------------------------------------------------------- 1 | code}]: {$this->message}\n"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/core/Directus/Console/Common/Exception/SettingUpdateException.php: -------------------------------------------------------------------------------- 1 | code}]: {$this->message}\n"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/core/Directus/Console/Common/Exception/UserUpdateException.php: -------------------------------------------------------------------------------- 1 | code}]: {$this->message}\n"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/core/Directus/Console/Exception/CommandFailedException.php: -------------------------------------------------------------------------------- 1 | code}]: {$this->message}\n"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/core/Directus/Console/Exception/UnsupportedCommandException.php: -------------------------------------------------------------------------------- 1 | code}]: {$this->message}\n"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/core/Directus/Console/Exception/WrongArgumentsException.php: -------------------------------------------------------------------------------- 1 | code}]: {$this->message}\n"; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/core/Directus/Container/Container.php: -------------------------------------------------------------------------------- 1 | offsetExists($offset); 16 | } 17 | 18 | /** 19 | * @inheritDoc 20 | */ 21 | public function get($offset) 22 | { 23 | if (!$this->offsetExists($offset)) { 24 | throw new ValueNotFoundException(sprintf('The key "%s" is not defined.', $offset)); 25 | } 26 | 27 | return $this->offsetGet($offset); 28 | } 29 | 30 | /** 31 | * @inheritDoc 32 | */ 33 | public function set($offset, $value) 34 | { 35 | $this->offsetSet($offset, $value); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/core/Directus/Container/Exception/ValueNotFoundException.php: -------------------------------------------------------------------------------- 1 | =5.4.0", 12 | "directus/collection": "^1.0", 13 | "pimple/pimple": "^3.0" 14 | }, 15 | "autoload": { 16 | "psr-4": { 17 | "Directus\\Container\\": "" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/core/Directus/Database/Ddl/Column/Bit.php: -------------------------------------------------------------------------------- 1 | setName($name); 26 | $this->setNullable($nullable); 27 | $this->setDefault($default); 28 | $this->setOptions($options); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/core/Directus/Database/Ddl/Column/Custom.php: -------------------------------------------------------------------------------- 1 | setType($type); 14 | } 15 | 16 | public function setType($type) 17 | { 18 | $this->type = $type; 19 | } 20 | 21 | /** 22 | * Sets the column length 23 | * 24 | * @param int|array $length 25 | * 26 | * @return $this 27 | */ 28 | public function setLength($length) 29 | { 30 | if (is_array($length)) { 31 | $length = implode(',', array_map(function ($value) { 32 | // add slashes in case the value has quotes 33 | return sprintf('"%s"', addslashes($value)); 34 | }, $length)); 35 | } else { 36 | $length = (int) $length; 37 | } 38 | 39 | $this->length = $length; 40 | 41 | return $this; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/core/Directus/Database/Ddl/Column/Double.php: -------------------------------------------------------------------------------- 1 | getLength(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/core/Directus/Database/Exception/CollectionAlreadyExistsException.php: -------------------------------------------------------------------------------- 1 | query = $query; 27 | } 28 | 29 | /** 30 | * @return string 31 | */ 32 | public function getQuery() 33 | { 34 | return $this->query; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/core/Directus/Database/Exception/ItemNotFoundException.php: -------------------------------------------------------------------------------- 1 | getConnection()); 18 | 19 | $this->parentBuilder = $builder; 20 | $this->column = $column; 21 | $this->relatedTable = $relatedTable; 22 | 23 | $this->columns([$this->column]); 24 | $this->from($this->relatedTable); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/core/Directus/Database/README.md: -------------------------------------------------------------------------------- 1 | # Directus Database 2 | 3 | The Directus Database component. 4 | 5 | ### _**A Work in Progress**_ 6 | -------------------------------------------------------------------------------- /src/core/Directus/Database/Repositories/Repository.php: -------------------------------------------------------------------------------- 1 | items = $data; 17 | 18 | return $this; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/core/Directus/Database/RowGateway/DirectusMediaRowGateway.php: -------------------------------------------------------------------------------- 1 | acl->getCmsOwnerColumnByTable($this->table); 14 | // $rowData[$cmsOwnerColumnName] = $currentUser['id']; 15 | } else { 16 | if (array_key_exists('date_uploaded', $rowData)) 17 | unset($rowData['date_uploaded']); 18 | } 19 | return $rowData; 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/core/Directus/Database/RowGateway/DirectusUsersRowGateway.php: -------------------------------------------------------------------------------- 1 | table, $this->sql->getAdapter(), $this->acl); 14 | $dbRecord = $TableGateway->find($rowData['id']); 15 | if (false === $dbRecord) { 16 | // @todo is it better to throw an exception here? 17 | $rowExistsInDatabase = false; 18 | } 19 | } 20 | 21 | // User is updating themselves. 22 | // Corresponds to a ping indicating their last activity. 23 | // Updated their "last_access" value. 24 | if ($this->acl) { 25 | if (isset($rowData['id']) && $rowData['id'] == $this->acl->getUserId()) { 26 | $rowData['last_access'] = DateTimeUtils::nowInUTC()->toString(); 27 | } 28 | } 29 | 30 | return $rowData; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/core/Directus/Database/TableGateway/DirectusCollectionsTableGateway.php: -------------------------------------------------------------------------------- 1 | =5.5.0", 11 | "zendframework/zend-db": "dev-directus", 12 | "directus/collection": "^1.0.0" 13 | }, 14 | "require-dev": { 15 | "phpunit/phpunit": "~3.7.0" 16 | }, 17 | "autoload": { 18 | "psr-4": { 19 | "Directus\\Database\\": "" 20 | } 21 | }, 22 | "extra": { 23 | "branch-alias": { 24 | "dev-version/6.4": "0.9.x-dev" 25 | } 26 | }, 27 | "repositories": [ 28 | { 29 | "type": "git", 30 | "url": "https://github.com/wellingguzman/zend-db" 31 | } 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /src/core/Directus/Exception/BadRequestException.php: -------------------------------------------------------------------------------- 1 | attributes; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/core/Directus/Exception/ForbiddenException.php: -------------------------------------------------------------------------------- 1 | uploadedError = $errorCode; 12 | parent::__construct(\Directus\get_uploaded_file_error($errorCode)); 13 | } 14 | 15 | public function getErrorCode() 16 | { 17 | return static::ERROR_CODE + $this->uploadedError; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/core/Directus/Filesystem/Exception/FilesystemException.php: -------------------------------------------------------------------------------- 1 | =5.4.0", 11 | "league/flysystem": "^1.0" 12 | }, 13 | "autoload": { 14 | "psr-4": { 15 | "Directus\\Filesystem\\": "" 16 | } 17 | }, 18 | "minimum-stability": "dev", 19 | "extra": { 20 | "branch-alias": { 21 | "dev-d64": "0.9.x-dev" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/core/Directus/Hash/Exception/HasherNotFoundException.php: -------------------------------------------------------------------------------- 1 | getName(), $string); 13 | } 14 | 15 | /** 16 | * @inheritdoc 17 | */ 18 | public function verify($string, $hash, array $options = []) 19 | { 20 | return hash($this->getName(), $string) === $hash; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/core/Directus/Hash/Hasher/BCryptHasher.php: -------------------------------------------------------------------------------- 1 | =5.5.0", 11 | "directus/utils": "^1.0.0" 12 | }, 13 | "autoload": { 14 | "psr-4": { 15 | "Directus\\Hash\\": "" 16 | } 17 | }, 18 | "extra": { 19 | "branch-alias": { 20 | "dev-version/6.4": "0.9.x-dev" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/core/Directus/Hook/HookInterface.php: -------------------------------------------------------------------------------- 1 | attributes = new Collection($attributes); 19 | } 20 | 21 | /** 22 | * Gets an attribute 23 | * 24 | * @param $key 25 | * 26 | * @return mixed 27 | */ 28 | public function attribute($key) 29 | { 30 | return $this->attributes[$key]; 31 | } 32 | 33 | /** 34 | * @return Collection 35 | */ 36 | public function attributes() 37 | { 38 | return $this->attributes; 39 | } 40 | 41 | /** 42 | * Gets all the data 43 | * 44 | * @return array 45 | */ 46 | public function getData() 47 | { 48 | return $this->items; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/core/Directus/Hook/README.md: -------------------------------------------------------------------------------- 1 | # Directus Hook 2 | -------------------------------------------------------------------------------- /src/core/Directus/Hook/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "directus/hook", 3 | "description": "Directus Hook Library", 4 | "keywords": [ 5 | "directus", 6 | "hook", 7 | "event" 8 | ], 9 | "license": "MIT", 10 | "require": { 11 | "php": ">=5.4.0" 12 | }, 13 | "autoload": { 14 | "psr-4": { 15 | "Directus\\Hook\\": "" 16 | } 17 | }, 18 | "extra": { 19 | "branch-alias": { 20 | "dev-d64": "0.9.x-dev" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/core/Directus/Mail/Exception/InvalidTransportException.php: -------------------------------------------------------------------------------- 1 | config = new Collection($config); 17 | 18 | $this->sendmail = \Swift_SendmailTransport::newInstance($this->config->get('sendmail')); 19 | } 20 | 21 | /** 22 | * @inheritdoc 23 | */ 24 | public function send(\Swift_Mime_Message $message, &$failedRecipients = null) 25 | { 26 | return $this->sendmail->send($message, &$failedRecipients); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/core/Directus/Mail/Transports/SimpleFileTransport.php: -------------------------------------------------------------------------------- 1 | config = new Collection($config); 18 | } 19 | 20 | /** 21 | * @inheritdoc 22 | */ 23 | public function send(Swift_Mime_Message $message, &$failedRecipients = null) 24 | { 25 | $path = rtrim($this->config->get('path', ''), '/') . '/' . time() . '.txt'; 26 | $message = [ 27 | implode(', ', array_keys($message->getTo())), 28 | $message->getSubject(), 29 | $message->getBody() 30 | ]; 31 | 32 | file_put_contents($path, implode("\n", $message)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/core/Directus/Mail/Transports/SmtpTransport.php: -------------------------------------------------------------------------------- 1 | config = new Collection($config); 17 | $transport = \Swift_SmtpTransport::newInstance( 18 | $this->config->get('host'), 19 | $this->config->get('port') 20 | ); 21 | 22 | if ($this->config->has('username')) { 23 | $transport->setUsername($this->config->get('username')); 24 | } 25 | 26 | if ($this->config->has('password')) { 27 | $transport->setPassword($this->config->get('password')); 28 | } 29 | 30 | if ($this->config->has('encryption')) { 31 | $transport->setEncryption($this->config->get('encryption')); 32 | } 33 | 34 | $this->smtp = $transport; 35 | } 36 | 37 | /** 38 | * @inheritdoc 39 | */ 40 | public function send(\Swift_Mime_Message $message, &$failedRecipients = null) 41 | { 42 | return $this->smtp->send($message, &$failedRecipients); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/core/Directus/Permissions/Exception/ForbiddenCollectionAlterException.php: -------------------------------------------------------------------------------- 1 | =5.4.0", 12 | "directus/exception": "~2.0.0" 13 | }, 14 | "autoload": { 15 | "psr-4": { 16 | "Directus\\Permissions\\": "" 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/core/Directus/Services/InterfacesService.php: -------------------------------------------------------------------------------- 1 | container->get('path_base'); 14 | $this->basePath = $basePath . '/public/extensions/core/interfaces'; 15 | } 16 | 17 | public function findAll(array $params = []) 18 | { 19 | return $this->all($this->basePath, $params); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/core/Directus/Services/ListingsService.php: -------------------------------------------------------------------------------- 1 | container->get('path_base'); 14 | $this->basePath = $basePath . '/public/extensions/core/listings'; 15 | } 16 | 17 | public function findAll(array $params = []) 18 | { 19 | return $this->all($this->basePath, $params); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/core/Directus/Services/PagesService.php: -------------------------------------------------------------------------------- 1 | container->get('path_base'); 14 | $this->basePath = $basePath . '/public/extensions/core/pages'; 15 | } 16 | 17 | public function findAll(array $params = []) 18 | { 19 | return $this->all($this->basePath, $params); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/core/Directus/Services/ServerService.php: -------------------------------------------------------------------------------- 1 | getAcl()->isAdmin()) { 14 | throw new UnauthorizedException('Only Admin can see this information'); 15 | } 16 | 17 | return [ 18 | 'data' => [ 19 | 'api' => [ 20 | 'version' => Application::DIRECTUS_VERSION 21 | ], 22 | 'server' => [ 23 | 'general' => [ 24 | 'php_version' => phpversion(), 25 | 'php_api' => php_sapi_name() 26 | ], 27 | 'max_upload_size' => \Directus\get_max_upload_size() 28 | ] 29 | ] 30 | ]; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/core/Directus/Session/Session.php: -------------------------------------------------------------------------------- 1 | storage = $storage; 24 | } 25 | 26 | /** 27 | * Get Session storage 28 | * 29 | * @return SessionStorageInterface 30 | */ 31 | public function getStorage() 32 | { 33 | return $this->storage; 34 | } 35 | 36 | /** 37 | * Proxy all undefined method to the storage 38 | * 39 | * @param string $method 40 | * @param array $arguments 41 | * 42 | * @return mixed 43 | */ 44 | public function __call($method, $arguments) 45 | { 46 | return call_user_func_array([$this->storage, $method], $arguments); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/core/Directus/Util/Git.php: -------------------------------------------------------------------------------- 1 | =5.4.0" 12 | }, 13 | "autoload": { 14 | "psr-4": { 15 | "Directus\\Util\\": "" 16 | } 17 | }, 18 | "extra": { 19 | "branch-alias": { 20 | "dev-d64": "0.9.x-dev" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/core/Directus/Validator/Constraints/Required.php: -------------------------------------------------------------------------------- 1 | context->buildViolation($constraint->message) 14 | ->setParameter('{{ value }}', $this->formatValue($value)) 15 | ->addViolation(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/core/Directus/Validator/Exception/InvalidRequestException.php: -------------------------------------------------------------------------------- 1 | container); 15 | $responseData = $service->findAllInfo(); 16 | 17 | return $this->responseWithData($request, $response, $responseData); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/endpoints/Instances.php: -------------------------------------------------------------------------------- 1 | post('', [$this, 'create']); 16 | } 17 | 18 | public function create(Request $request, Response $response) 19 | { 20 | $this->validateRequestPayload($request); 21 | $installService = new InstanceService($this->container); 22 | $installService->create($request->getParsedBody()); 23 | 24 | return $this->responseWithData($request, $response, []); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/endpoints/Interfaces.php: -------------------------------------------------------------------------------- 1 | get('', [$this, 'all']); 21 | } 22 | 23 | public function all(Request $request, Response $response) 24 | { 25 | $service = new InterfacesService($this->container); 26 | $responseData = $service->findAll(); 27 | 28 | return $this->responseWithData($request, $response, $responseData); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/endpoints/Listings.php: -------------------------------------------------------------------------------- 1 | get('', [$this, 'all']); 21 | } 22 | 23 | public function all(Request $request, Response $response) 24 | { 25 | $service = new ListingsService($this->container); 26 | $responseData = $service->findAll(); 27 | 28 | return $this->responseWithData($request, $response, $responseData); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/endpoints/Mail.php: -------------------------------------------------------------------------------- 1 | post('', [$this, 'send']); 16 | } 17 | 18 | public function send(Request $request, Response $response) 19 | { 20 | $this->validateRequestPayload($request); 21 | $mailService = new MailService($this->container); 22 | $mailService->send($request->getParsedBody()); 23 | 24 | $response = $response->withStatus(204); 25 | 26 | return $this->responseWithData($request, $response, []); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/endpoints/Pages.php: -------------------------------------------------------------------------------- 1 | get('', [$this, 'all']); 21 | } 22 | 23 | public function all(Request $request, Response $response) 24 | { 25 | $service = new PagesService($this->container); 26 | $responseData = $service->findAll(); 27 | 28 | return $this->responseWithData($request, $response, $responseData); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/endpoints/Server.php: -------------------------------------------------------------------------------- 1 | get('', [$this, 'all']); 19 | } 20 | 21 | /** 22 | * @param Request $request 23 | * @param Response $response 24 | * 25 | * @return Response 26 | */ 27 | public function all(Request $request, Response $response) 28 | { 29 | $responseData = [ 30 | 'data' => array_map(function ($type) { 31 | return ['name' => $type]; 32 | }, DataTypes::getAllTypes()) 33 | ]; 34 | 35 | return $this->responseWithData($request, $response, $responseData); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/helpers/arrays.php: -------------------------------------------------------------------------------- 1 | toString(); 16 | } 17 | } 18 | 19 | if (!function_exists('generate_uuid3')) { 20 | /** 21 | * Generates a UUID v3 string 22 | * 23 | * @param string $namespace 24 | * @param string $name 25 | * 26 | * @return string 27 | */ 28 | function generate_uui3($namespace, $name) 29 | { 30 | return Uuid::uuid3( 31 | $namespace, 32 | $name 33 | )->toString(); 34 | } 35 | } 36 | 37 | if (!function_exists('generate_uuid4')) { 38 | /** 39 | * Generates a UUID v4 string 40 | * 41 | * @return string 42 | */ 43 | function generate_uui4() 44 | { 45 | return Uuid::uuid4()->toString(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/mail/footer.twig: -------------------------------------------------------------------------------- 1 |

2 | This email was sent by Directus – {{settings.global.project.name }} 3 |

4 |

5 | Log in 6 | to manage your email preferences 7 |

8 | -------------------------------------------------------------------------------- /src/mail/forgot-password.twig: -------------------------------------------------------------------------------- 1 | {% extends "base.twig" %} 2 | {% block content %} 3 | 4 |

Hey there,

5 | 6 |

You requested to reset your password, here is your reset password link:

7 | 8 | {% set reset_url = settings.global.project.url|trim('/') ~ '/' ~ api.env ~ '/auth/reset_password/' ~ reset_token %} 9 |

{{ reset_url }}

10 | 11 |

Love,
Directus

12 | 13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /src/mail/new-install.twig: -------------------------------------------------------------------------------- 1 | {% extends "base.twig" %} 2 | {% block content %} 3 |

Your new Instance of Directus is ready to go! You can access it using the following credentials:

4 | 5 |

{{ project.url }}

6 | 7 |

Main Configuration

8 |

9 | Project Name: {{ project.name }}
10 | Admin Email: {{ user.email }}
11 | Admin Password: {{ user.password }}
12 | Installed Version: {{ project.version }}
13 | API Key: {{ user.token }} 14 |

15 | 16 |

Database Configuration

17 |

18 | Host Name: {{ database.host }}
19 | Username: {{ database.user }}
20 | Password: {{ database.password }}
21 | Database Name: {{ database.name }} 22 |

23 | 24 |

Love,
Directus

25 | {% endblock %} 26 | -------------------------------------------------------------------------------- /src/mail/reset-password.twig: -------------------------------------------------------------------------------- 1 | {% extends "base.twig" %} 2 | {% block content %} 3 | 4 |

Hey there,

5 | 6 |

Here is a temporary password to access Directus:

7 | 8 |

{{ new_password }}

9 | 10 |

Once you log in, you can change your password via the User Settings menu.

11 | 12 |

Love,
Directus

13 | 14 | {% endblock %} 15 | -------------------------------------------------------------------------------- /src/mail/user-invitation.twig: -------------------------------------------------------------------------------- 1 | {% extends "base.twig" %} 2 | {% block content %} 3 | 4 |

You have been invited to {{settings.global.project.name }}. Please click the link below to join:

5 | 6 | {% set invitation_url = settings.global.project.url|trim('/') ~ '/' ~ api.env ~ '/auth/invitation/' ~ token %} 7 |

{{ invitation_url }}

8 | 9 |

Love,
Directus

10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /src/services/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsguru-git/api/1f6e97f6e33df69d280659cbeae2a1c630011593/src/services/.gitkeep -------------------------------------------------------------------------------- /tests/api/Config/ConfigTest.php: -------------------------------------------------------------------------------- 1 | 1 13 | ]); 14 | 15 | $this->assertSame(1, $config->get('option')); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/api/ContainerTest.php: -------------------------------------------------------------------------------- 1 | 'john']); 12 | 13 | $this->assertSame('john', $container->get('name')); 14 | $this->assertTrue($container->has('name')); 15 | 16 | $container->set('age', 10); 17 | $this->assertSame(10, $container->get('age')); 18 | 19 | $container->set('country', function () { 20 | return 'us'; 21 | }); 22 | 23 | $this->assertSame('us', $container->get('country')); 24 | } 25 | 26 | /** 27 | * @expectedException \Directus\Container\Exception\ValueNotFoundException 28 | */ 29 | public function testNotFoundValue() 30 | { 31 | $container = new Container(); 32 | 33 | $name = $container->get('name'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/api/Database/Ddl/ColumnBooleanTest.php: -------------------------------------------------------------------------------- 1 | assertSame('foo', $column->getName()); 11 | $this->assertFalse($column->isNullable()); 12 | $this->assertSame(2, $column->getDefault()); 13 | } 14 | } -------------------------------------------------------------------------------- /tests/api/Database/Exception/DuplicateEntryExceptionTest.php: -------------------------------------------------------------------------------- 1 | (unique_email)'; 9 | 10 | $exception = new \Directus\Database\Exception\DuplicateItemException($message); 11 | 12 | $this->assertSame($expected, $exception->getMessage()); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tests/api/Database/Schemas/Object/FieldRelationshipTest.php: -------------------------------------------------------------------------------- 1 | 'category_id']); 8 | $fieldB = new \Directus\Database\Schema\Object\Field(['field' => 'products', 'type' => 'alias']); 9 | 10 | $data = [ 11 | 'collection_a' => 'projects', 12 | 'field_a' => 'category_id', 13 | 'junction_key_a' => null, 14 | 'junction_collection' => null, 15 | 'junction_mixed_collections' => null, 16 | 'junction_key_b' => null, 17 | 'collection_b' => 'categories', 18 | 'field_b' => 'products' 19 | ]; 20 | 21 | $relationshipA = new \Directus\Database\Schema\Object\FieldRelationship($fieldA, $data); 22 | $relationshipB = new \Directus\Database\Schema\Object\FieldRelationship($fieldB, $data); 23 | 24 | $this->assertTrue($relationshipA->isValid()); 25 | $this->assertTrue($relationshipA->isManyToOne()); 26 | 27 | $this->assertTrue($relationshipB->isValid()); 28 | $this->assertTrue($relationshipB->isOneToMany()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests/api/Database/TableGateway/AclAwareTableGatewayTest.php: -------------------------------------------------------------------------------- 1 | getAclMock(); 8 | // $adapterMock = get_adapter_mock(); 9 | // $table = new AclAwareTableGateway($aclMock, 'directus_users', $adapterMock); 10 | $table = $this->getMockBuilder('\Directus\Database\TableGateway\AclAwareTableGateway') 11 | ->disableOriginalConstructor() 12 | ->getMock(); 13 | 14 | $adapter = get_mock_adapter($this); 15 | $acl = new Directus\Permissions\Acl(); 16 | //$table = new \Directus\Database\TableGateway\AclAwareTableGateway($acl, 'users', $adapter); 17 | } 18 | 19 | protected function getAclMock() 20 | { 21 | $mock = $this->getMockBuilder('\Directus\Permissions\Acl') 22 | ->setConstructorArgs([]) 23 | ->getMock(); 24 | 25 | return $mock; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /tests/api/Session/SessionTest.php: -------------------------------------------------------------------------------- 1 | assertInstanceOf('\Directus\Session\Storage\ArraySessionStorage', $session->getStorage()); 11 | $this->assertSame($storage, $session->getStorage()); 12 | } 13 | 14 | public function testStorageCalls() 15 | { 16 | $storage = new \Directus\Session\Storage\ArraySessionStorage(); 17 | $session = new \Directus\Session\Session($storage); 18 | 19 | $this->assertNull($session->get('name')); 20 | 21 | $storage->set('name', 'joseph'); 22 | $this->assertSame('joseph', $session->get('name')); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests/api/Twig/TwigTest.php: -------------------------------------------------------------------------------- 1 | assertInternalType('array', $extension->getFilters()); 14 | $this->assertInternalType('array', $extension->getFunctions()); 15 | $this->assertInternalType('string', $extension->getName()); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/api/Util/FormattingTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('A Test File', Formatting::fileNameToFileTitle($path)); 11 | } 12 | 13 | public function testUnderscoreToCamelCase() 14 | { 15 | $this->assertEquals('HelloWorld', Formatting::underscoreToCamelCase('hello_world')); 16 | } 17 | 18 | public function testSanitizeTitle() 19 | { 20 | /* 21 | $title = 'this is a text example'; 22 | $this->assertEquals('this-is-a-text-example', Formatting::sanitize_title_with_dashes($title)); 23 | 24 | $title = 'this–is—a text example'; 25 | $this->assertEquals('this-is-a-text-example', Formatting::sanitize_title_with_dashes($title, '', 'save')); 26 | */ 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/api/Util/Installation/config/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsguru-git/api/1f6e97f6e33df69d280659cbeae2a1c630011593/tests/api/Util/Installation/config/.gitkeep -------------------------------------------------------------------------------- /tests/api/Util/JWTTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(JWTUtils::isJWT($token)); 15 | $this->assertFalse(JWTUtils::isJWT('token')); 16 | 17 | $token = implode('.', [base64_encode('null'), 'k', 'en']); 18 | $this->assertFalse(JWTUtils::isJWT($token)); 19 | $this->assertFalse(JWTUtils::isJWT(123)); 20 | 21 | $token = implode('.', [base64_encode('{"typ": "none"}'), 'k', 'en']); 22 | $this->assertFalse(JWTUtils::isJWT($token)); 23 | } 24 | 25 | public function testExpiration() 26 | { 27 | $data = ['exp' => 0]; 28 | $token = JWTUtils::encode($data, 123); 29 | $this->assertTrue(JWTUtils::hasExpired($token)); 30 | 31 | $data = ['exp' => time() * 2]; 32 | $token = JWTUtils::encode($data, 123); 33 | $this->assertFalse(JWTUtils::hasExpired($token)); 34 | 35 | $data = ['id' => 1]; 36 | $token = JWTUtils::encode($data, 123); 37 | $this->assertNull(JWTUtils::hasExpired($token)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests/assets/image.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsguru-git/api/1f6e97f6e33df69d280659cbeae2a1c630011593/tests/assets/image.jpeg -------------------------------------------------------------------------------- /tests/assets/travis-ci-apache: -------------------------------------------------------------------------------- 1 | 2 | DocumentRoot %TRAVIS_BUILD_DIR% 3 | 4 | 5 | Options FollowSymLinks MultiViews ExecCGI 6 | AllowOverride All 7 | Require all granted 8 | 9 | 10 | # Wire up Apache to use Travis CI's php-fpm. 11 | 12 | AddHandler php5-fcgi .php 13 | Action php5-fcgi /php5-fcgi 14 | Alias /php5-fcgi /usr/lib/cgi-bin/php5-fcgi 15 | FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi -host 127.0.0.1:9000 -pass-header Authorization 16 | 17 | 18 | Require all granted 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /tests/io/ActivitySkipTest.php: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsguru-git/api/1f6e97f6e33df69d280659cbeae2a1c630011593/tests/io/ActivitySkipTest.php -------------------------------------------------------------------------------- /tests/io/GeneralTest.php: -------------------------------------------------------------------------------- 1 | false 11 | ]); 12 | assert_response_contents($this, $response, 'pong', [ 13 | 'status' => 200 14 | ]); 15 | } 16 | 17 | public function testErrorExtraInformation() 18 | { 19 | // TODO: Switch between production and development to add more error information 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/utils/io_functions.php: -------------------------------------------------------------------------------- 1 |