diff --git a/.gitignore b/.gitignore index c59f3450e6..78ddb63a76 100644 --- a/.gitignore +++ b/.gitignore @@ -24,7 +24,7 @@ deploy.* # Ignore configuration files /config/* -!/config/api_sample.php +!/config/_example.php # PHPUnit /phpunit.xml diff --git a/README.md b/README.md index ca7a23c5c9..a889617275 100644 --- a/README.md +++ b/README.md @@ -1,173 +1,27 @@ -

- - Directus Logo - -

+

+ Directus Logo  Directus API

-

 

+> Looking for the full Directus suite? Checkout [the `directus/directus` repo](https://github.com/directus/directus)! -

- The All-New Directus 7
Future-Proof Headless CMS -

+## Description -

- Website • - Docs • - API Reference • - User Guide • - Demo • - Contribute • - Twitter -

+The Directus API is a PHP powered REST / GraphQL API that acts as a layer on top of a relational database. -

 

+## Installation / Usage -> _The Directus 7 App has been rebuilt from the ground-up in Vue.js and decoupled from our new "versionless" API. Both can easily be customized and extended to suit your project's needs._ +**NOTE**: Unless you're contributing to the project, you shouldn't build the API from source. Use [`directus/directus`](/directus/directus) instead. -

 

+1. Clone the repo +2. Install the depencencies using `composer`: `composer install` +3. Run the application using any local server, like MAMP, DDEV, Docker, or any other (preferably) Apache installation. -## 🐰 Introduction +## Core Team -Directus is an open-source suite of software that provides an instant API wrapper for SQL databases and an intuitive Admin App for non-technical users to manage that content. It's like a safe, friendly, and super-powered "database client" (eg: _PHP-my-Admin_ or _Sequel Pro_). Created in 2004, our premium platform powers hundreds-of-thousands of data-driven applications around the world. With our JSON API, webhooks, and complete control over where and how your content is used — what will you build with Directus? +* [Ben Haynes](https://github.com/benhaynes) _(Project Lead)_ +* [Rijk van Zanten](https://github.com/rijkvanzanten) _(Tech Lead)_ -* **[Directus API](https://github.com/directus/api)** — Wraps any new or existing SQL database with RESTful JSON endpoints for connecting content anywhere: websites, native apps, wearables, IoT devices, kiosks, or anything else! -* **[Directus Admin App](https://github.com/directus/app)** — A friendly admin interface built with Vue.js that works in conjunction with our API to allow non-technical users to manage database content and digital assets. -* **[Directus Suite](https://github.com/directus/directus)** A package that combines our API, Admin App, and all dependencies for quick and easy installation on most servers. +[Advisors, Sponsors, Partners, and Key Contributors](https://directus.io/organization.html#the-team) -

 

+## License -## ✨ Key Concepts - -### Database-First - -Directus follows a database-first approach, storing all of your data _unaltered_ in pure SQL databases with that can be completely customized. - -#### Access and Optimization - -Directus mirrors _your_ actual database so it will automatically stay in sync with any changes made directly to the database! With the full power of SQL unlocked, you can: - -1. Architect your actual database with meaningful table and column names -2. Infinitely optimize with indexing, datatypes, lengths, defaults, keys, encoding, etc -3. Update your database schema at any point and Directus will instantly reflect changes -4. Create, update, and delete content directly from the database - -#### Direct Access - -This database-first approach means that you have the option to completely bypass Directus if needed. Connecting your application _directly_ to the database means Directus is completely bypassed, removing even the slightest possiblility of a bottleneck, and giving you the unbridled power of complex SQL queries. - -#### Portability and Transparency - -Directus is _not_ a black-box system. There is nothing proprietary or opinionated about how Directus stores your content so you're never locked-in or locked out. Import existing SQL to get started quickly, and rest assured that you can also export your unaltered content as vanilla SQL at any point. You can also self-host the entire suite on your own servers if needed or use our hosted Cloud service. - -#### Complex Relationships - -Whether you need many-to-one, one-to-many, many-to-many, or something completely different... Directus has you covered. You can interrelate database fields, then fetch deeply nested data using our robust dot-notation parameters. - -### Simple, Modular and Extensible - -We've intentionally kept our codebase as simple and lightweight as possible. Every aspect of Directus has been modularized, with many options for extending, overriding, or even bypassing the core system. Choose your auth providers, storage adapters, content interfaces, data presentation, and more. We also have webhooks, event hooks, API response filters, custom endpoints, CSS/JS override files, and much more. - -Furthermore, because the codebase has been internally decoupled, Directus supports full multitenancy. You can use a single Directus App to manage multiple Directus API instances, and each API instance can manage multiple databases (projects or environments). - -### Internationalization and Accessibility - -Directus allows you to manage multilingual content in as many languages as your project needs. And no matter what language(s) your organization requires, our Admin App supports a growing number of locales. We've also paid close attention to our Admin App's interface to ensure it is as clear and accessible as possible for all users. - -

 

- -## 📖 Documentation - -* [What is Directus?](https://docs.directus.io/getting-started/introduction.html#what-is-directus) -* [Installing the Directus Suite](https://docs.directus.io/getting-started/installation.html) -* [Extending Directus](https://docs.directus.io/extensions/) -* Directus App - * [App Docs](https://docs.directus.io/advanced/app/standalone.html) - * [User Guide](https://docs.directus.io/guides/user-guide.html) - * [Install the App](https://docs.directus.io/advanced/app/standalone.html#installation) -* Directus API - * [API Docs](https://docs.directus.io/advanced/api/standalone.html) - * [API Reference](https://docs.directus.io/api/reference.html) - * [Install the API](https://docs.directus.io/advanced/api/standalone.html#installation) -* Tutorials - * [Tutorials](https://medium.com/directus) - * [Videos](https://www.youtube.com/playlist?list=PLD--x9rY3ZL31stRPkA4FdGC4idIM-8-d) - -

 

- -## 🤲 Help & Resources - -### Technical Support - -* **Community Support** — For help with open-source instances please post questions with the `directus` tag on [Stack Overflow](https://stackoverflow.com/questions/tagged/directus) or chat with members of our online community. -* **Premium Support** — Directus Cloud customers, Patreon Sponsors, and those paying for hourly support all have access to our Live support over our [Live Chat](https://directus.io) or [Email](mailto:support@directus.io). - -### Community - -* **[Slack](https://directus.chat)** — Come join over a thousand members discussing the future of Directus. Our helpful members are also quick to offer advice for simple questions you may have while getting started. -* **[Twitter](https://twitter.com/directus)** — Follow us on Twitter to be the first to hear about product updates, see sneak peeks of new features, and vote on polls regarding the future of our platform. - -### GitHub Tickets - -* **Reporting Bugs** — If you believe you've found a bug in the Directus Core codebase, please submit new tickets to either the [App](https://github.com/directus/app/issues/new?template=Bug_report.md) or the [API](https://github.com/directus/api/issues/new?template=Bug_report.md). **For all security related issues, please chat with us directly through [directus.io](https://directus.io/).** -* **Requesting Features** — Feature requests are managed as GitHub issues within the appropriate Directus repository. Before making a new submission, first [browse existing feature requests](https://github.com/directus/app/issues?q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc+label%3Aenhancement) using the `enhancement` label. If you find a similar request, simply vote for it using the :+1: or :-1: reactions and add any additional context in the comments. If _not_, [add a new feature request](https://github.com/directus/app/issues/new?template=Feature_request.md) by completing the provided template. - -### Online Demos - -We have two online demos available so you can easily check out Directus in action. These demos are public and is therefore susceptible to trolls who think it's funny to write offensive things or change the master user password. The entire database is reset each hour to mitigate this. The login credentials for both demos: `admin@example.com` and `password` - -* **[Stable](https://directus.app)** — This demo showcases the [latest release](https://github.com/directus/directus/releases/latest) of the Directus suite. -* **[Nightly](https://nightly.directus.app)** — This demo uses the [latest commits](https://github.com/directus/directus/commits/master) from the `master` branch. - -

 

- -## ❤️ Supporting Directus - -Directus is a GPLv3-licensed open source project with development made possible by support from our core team, contributors, and sponsors. It's not easy building premium open-source software; if you would like to help ensure Directus stays free, please consider becoming a sponsor. - -* [Become a backer or sponsor through Patreon](https://www.patreon.com/directus) -* [One-time donation through PayPal](https://www.paypal.me/supportdirectus) -* [Other ways to help](https://docs.directus.io/getting-started/supporting-directus.html) - - -### Contributing - -We love pull-requests! To work on Directus you'll need to install it locally from source by following the instructions below. Submit all pull-requests to the `develop` branch of our `api` and `app` repositories. - -* [Setup API Development Environment](https://docs.directus.io/advanced/source.html#api-source) -* [Setup App Development Environment](https://docs.directus.io/advanced/source.html#application-source) - -If you want to dive right into the code and skip the manual setup of your development environment you can also spin up fully functional browser based development environments with a single click: - -* [Start API Gitpod Workspace](https://gitpod.io/#https://github.com/directus/api) -* [Start APP Gitpod Workspace](https://gitpod.io/#https://github.com/directus/app) - -### Sponsors - -[RANGER Studio](http://rangerstudio.com), Bas Jansen - -### Core Team - - - - - - -
-
- Ben Haynes

- Project Lead -
-
- Rijk van Zanten

- App Lead -
- -**[Advisors, Sponsors, Partners, and Key Contributors](https://directus.io/organization.html#the-team)** - -

 

- ----- - -

- Directus is released under the GPLv3 license. RANGER Studio LLC owns all Directus trademarks and logos on behalf of our project's community. Copyright © 2006-2018, RANGER Studio LLC. -

+Directus is released under the [GPLv3](http://www.gnu.org/copyleft/gpl.html) license. [RANGER Studio LLC](https://rangerstudio.com) owns all Directus trademarks and logos on behalf of our project's community. Copyright © 2006-2019, [RANGER Studio LLC](https://rangerstudio.com). diff --git a/config/_example.php b/config/_example.php new file mode 100644 index 0000000000..4d78499ff5 --- /dev/null +++ b/config/_example.php @@ -0,0 +1,170 @@ + [ + 'type' => 'mysql', // Only mysql is supported + 'host' => 'localhost', + 'port' => 3306, + 'name' => 'directus', + 'username' => 'root', + 'password' => 'root', + 'engine' => 'InnoDB', + 'charset' => 'utf8mb4', + // 'socket' => '', // Path to socket. Remove the `host` key above when using sockets + // 'driver_options' => [ // Other MYSQL_PDO options. Can be used to connect to the database + // // over an encrypted connection. For more information, see + // // https://www.php.net/manual/en/ref.pdo-mysql.php#pdo-mysql.constants + // PDO::MYSQL_ATTR_SSL_CAPATH => '/etc/ssl/certs', + // ] + ], + + 'cors' => [ + 'enabled' => true, // Enable or disable all CORS headers + 'origin' => ['*'], // Access-Control-Allow-Origin + 'methods' => [ // Access-Control-Allow-Methods + 'GET', + 'POST', + 'PUT', + 'PATCH', + 'DELETE', + 'HEAD', + ], + 'headers' => [], // Access-Control-Allow-Headers + 'exposed_headers' => [], // Access-Control-Expose-Headers + 'max_age' => null, // Access-Control-Allow-Max-Age + 'credentials' => false, // Access-Control-Allow-Credentials + ], + + 'rate_limit' => [ + 'enabled' => false, // Enable or disable all rate limiting + 'limit' => 100, // Number of requests allowed... + 'interval' => 60, // ...during this interval (in seconds) + 'adapter' => 'redis', // Where to save the rate limit tmp data + 'host' => '127.0.0.1', + 'port' => 6379, + 'timeout' => 10 // Timeout from API to rate limit storage adapter + ], + + 'storage' => [ + 'adapter' => 'local', // What storage adapter to use for files + // Defaults to the local filesystem. Other natively supported + // options include: Amazon S3, Aliyun OSS + // You'll need to require the correct flysystem adapters through Composer + // See https://docs.directus.io/extensions/storage-adapters.html#using-aws-s3 + + 'root' => 'public/uploads/project-name/originals', // Where files are stored on disk + 'thumb_root' => 'public/uploads/project-name/generated', // Where thumbnails are stored on disk + 'root_url' => '/uploads/project-name/originals', // Where files are accessed over the web + + 'proxy_downloads' => false, // Use an internal proxy for downloading all files + + // S3 + //////////////////////////////////////// + // 'key' => 's3-key', + // 'secret' => 's3-secret', + // 'region' => 's3-region', + // 'version' => 's3-version', + // 'bucket' => 's3-bucket', + // 'options' => [ + // 'ACL' => 'public-read', + // 'Cache-Control' => 'max-age=604800' + // ], + // 'endpoint' => 's3-endpoint', + + // Aliyun OSS + //////////////////////////////////////// + // 'OSS_ACCESS_ID' => 'aliyun-oss-id', + // 'OSS_ACCESS_KEY' => 'aliyun-oss-key', + // 'OSS_ENDPOINT' => 'aliyun-oss-endpoint', + // 'OSS_BUCKET' => 'aliyun-oss-bucket', + ], + + 'mail' => [ + 'default' => [ + 'transport' => 'smtp', // How to send emails. Supports `smtp` and `sendmail` + 'from' => 'no-reply@example.com', // The sender of the email + + // SMTP + //////////////////////////////////////// + 'host' => 'smtp.example.com', + 'port' => 25, + 'username' => 'smtp-user', + 'password' => 'd1r3ctu5', + 'encryption' => 'tls' + ], + ], + + 'cache' => [ + 'enabled' => false, // Cache all API responses + 'response_ttl' => 3600, // Keep the cache for n seconds + 'pool' => [ + 'adapter' => 'apc', // What adapter to use to store the cache in + // Supports: apc, apcu, filesystem, memcached, + // memcache, redis + + // Filesystem + //////////////////////////////////////// + // 'path' => '../cache/', + + // memcached, memcache, redis + //////////////////////////////////////// + // 'host' => 'localhost', + // 'port' => 11211, + ], + ], + + 'auth' => [ + 'secret_key' => '1234', // Used in the oAuth flow + 'public_key' => '9876', + 'social_providers' => [ + // 'okta' => [ + // 'client_id' => '', + // 'client_secret' => '', + // 'base_url' => 'https://dev-000000.oktapreview.com/oauth2/default' + // ], + // 'github' => [ + // 'client_id' => '', + // 'client_secret' => '' + // ], + // 'facebook' => [ + // 'client_id' => '', + // 'client_secret' => '', + // 'graph_api_version' => 'v2.8', + // ], + // 'google' => [ + // 'client_id' => '', + // 'client_secret' => '', + // 'hosted_domain' => '*', + // 'use_oidc_mode' => true, + // ], + // 'twitter' => [ + // 'identifier' => '', + // 'secret' => '' + // ] + ] + ], + + 'hooks' => [ // https://docs.directus.io/extensions/hooks.html#creating-hooks + 'actions' => [], + 'filters' => [], + ], + + 'tableBlacklist' => [], // What tables to globally ignore within Directus + + 'env' => 'production', // production, development, or staging + // Production silences stack traces and error details in API output + + 'logger' => [ + 'path' => '../logs', // Where to save warning and error logs for the API + ], + + 'feedback' => [ + 'token' => '123', // Not currently used + 'login' => true // Not currently used + ], +]; diff --git a/config/api_sample.php b/config/api_sample.php deleted file mode 100644 index 760dfbc9a9..0000000000 --- a/config/api_sample.php +++ /dev/null @@ -1,174 +0,0 @@ - [ - 'env' => 'production', - 'timezone' => 'America/New_York' - ], - - 'settings' => [ - 'logger' => [ - 'path' => __DIR__ . '/../logs', - ], - ], - - 'database' => [ - 'type' => 'mysql', - 'host' => 'localhost', - 'port' => 3306, - 'name' => 'directus', - 'username' => 'root', - 'password' => 'root', - 'engine' => 'InnoDB', - 'charset' => 'utf8mb4', - // When using unix socket to connect to the database the host attribute should be removed - // 'socket' => '/var/lib/mysql/mysql.sock', - 'socket' => '', - // Connect over TLS by using the appropriate PDO_MySQL constants: - // https://www.php.net/manual/en/ref.pdo-mysql.php#pdo-mysql.constants - //'driver_options' => [ - // PDO::MYSQL_ATTR_SSL_CAPATH => '/etc/ssl/certs', - //] - ], - - 'cache' => [ - 'enabled' => false, - 'response_ttl' => 3600, // seconds - // 'pool' => [ - // 'adapter' => 'apc' - // ], - // 'pool' => [ - // 'adapter' => 'apcu' - // ], - // 'pool' => [ - // 'adapter' => 'filesystem', - // 'path' => '../cache/', // relative to the api directory - // ], - // 'pool' => [ - // 'adapter' => 'memcached', - // //'url' => 'localhost:11211;localhost:11212' - // 'host' => 'localhost', - // 'port' => 11211 - // ], - // 'pool' => [ - // 'adapter' => 'memcache', - // 'url' => 'localhost:11211;localhost:11212' - // //'host' => 'localhost', - // //'port' => 11211 - //], - // 'pool' => [ - // 'adapter' => 'redis', - // 'host' => 'localhost', - // 'port' => 6379 - // ], - ], - - 'storage' => [ - 'adapter' => 'local', - // The storage root is the directus root directory. - // All path are relative to the storage root when the path is not starting with a forward slash. - // By default the uploads directory is located at the directus public root - // An absolute path can be used as alternative. - 'root' => 'public/uploads/_/originals', - // This is the url where all the media will be pointing to - // here is where Directus will assume all assets will be accessed - // Ex: (yourdomain)/uploads/_/originals - 'root_url' => '/uploads/_/originals', - // Same as "root", but for the thumbnails - 'thumb_root' => 'public/uploads/_/thumbnails', - // 'key' => 's3-key', - // 'secret' => 's3-secret', - // 'region' => 's3-region', - // 'version' => 's3-version', - // 'bucket' => 's3-bucket', - // 'options' => ['ACL' => 'public-read', 'Cache-Control' => 'max-age=604800'] - // Set custom S3 endpoint - // 'endpoint' => 's3-endpoint', - // 'OSS_ACCESS_ID' => 'aliyun-oss-id', - // 'OSS_ACCESS_KEY' => 'aliyun-oss-key', - // 'OSS_ENDPOINT' => 'aliyun-oss-endpoint', - // 'OSS_BUCKET' => 'aliyun-oss-bucket' - // Use an internal proxy for downloading all files - //'proxy_downloads' => false, - ], - - 'mail' => [ - 'default' => [ - 'transport' => 'sendmail', - 'from' => 'admin@example.com' - ], - ], - - 'cors' => [ - 'enabled' => true, - 'origin' => ['*'], - 'methods' => [ - 'GET', - 'POST', - 'PUT', - 'PATCH', - 'DELETE', - 'HEAD', - ], - 'headers' => [], - 'exposed_headers' => [], - 'max_age' => null, // in seconds - 'credentials' => false, - ], - - 'rate_limit' => [ - 'enabled' => false, - 'limit' => 100, // number of request - 'interval' => 60, // seconds - 'adapter' => 'redis', - 'host' => '127.0.0.1', - 'port' => 6379, - 'timeout' => 10 - ], - - 'hooks' => [ - 'actions' => [], - 'filters' => [], - ], - - 'feedback' => [ - 'token' => 'a-kind-of-unique-token', - 'login' => true - ], - - // These tables will not be loaded in the directus schema - 'tableBlacklist' => [], - - 'auth' => [ - 'secret_key' => '', - 'public_key' => '', - 'social_providers' => [ - // 'okta' => [ - // 'client_id' => '', - // 'client_secret' => '', - // 'base_url' => 'https://dev-000000.oktapreview.com/oauth2/default' - // ], - // 'github' => [ - // 'client_id' => '', - // 'client_secret' => '' - // ], - // 'facebook' => [ - // 'client_id' => '', - // 'client_secret' => '', - // 'graph_api_version' => 'v2.8', - // ], - // 'google' => [ - // 'client_id' => '', - // 'client_secret' => '', - // 'hosted_domain' => '*', - // // Uses OpenIDConnect to fetch the email instead of using the Google+ API - // // Disabling the OIDC Mode, requires you to enable the Google+ API otherwise it will fail - // 'use_oidc_mode' => true, - // ], - // 'twitter' => [ - // 'identifier' => '', - // 'secret' => '' - // ] - ] - ], -]; diff --git a/migrations/db/schemas/20180220023138_create_activity_table.php b/migrations/db/schemas/20180220023138_create_activity_table.php deleted file mode 100644 index ae602ee604..0000000000 --- a/migrations/db/schemas/20180220023138_create_activity_table.php +++ /dev/null @@ -1,82 +0,0 @@ -table('directus_activity', ['signed' => false]); - - $table->addColumn('action', 'string', [ - 'limit' => 45, - 'null' => false - ]); - - $table->addColumn('action_by', 'integer', [ - 'signed' => false, - 'null' => false, - 'default' => 0 - ]); - - $table->addColumn('action_on', 'datetime', [ - 'default' => null - ]); - - $table->addColumn('ip', 'string', [ - 'limit' => 50, - 'default' => null - ]); - - $table->addColumn('user_agent', 'string', [ - 'limit' => 255 - ]); - - $table->addColumn('collection', 'string', [ - 'limit' => 64, - 'null' => false - ]); - - $table->addColumn('item', 'string',[ - 'limit' => 255 - ]); - - $table->addColumn('edited_on', 'datetime', [ - 'null' => true, - 'default' => null - ]); - - $table->addColumn('comment', 'text', [ - 'null' => true, - 'encoding' => 'utf8mb4' - ]); - - $table->addColumn('comment_deleted_on', 'datetime', [ - 'null' => true, - 'default' => null - ]); - - $table->create(); - } -} diff --git a/migrations/db/schemas/20180220023152_create_collections_presets_table.php b/migrations/db/schemas/20180220023152_create_collections_presets_table.php deleted file mode 100644 index f4f38af82e..0000000000 --- a/migrations/db/schemas/20180220023152_create_collections_presets_table.php +++ /dev/null @@ -1,83 +0,0 @@ -table('directus_collection_presets', ['signed' => false]); - - $table->addColumn('title', 'string', [ - 'limit' => 255, - 'null' => true, - 'default' => null, - 'encoding' => 'utf8mb4' - ]); - $table->addColumn('user', 'integer', [ - 'signed' => false, - 'null' => true - ]); - $table->addColumn('role', 'integer', [ - 'signed' => false, - 'null' => true - ]); - $table->addColumn('collection', 'string', [ - 'limit' => 64, - 'null' => false - ]); - $table->addColumn('search_query', 'string', [ - 'limit' => 100, - 'null' => true, - 'default' => null - ]); - $table->addColumn('filters', 'text', [ - 'null' => true, - 'default' => null - ]); - $table->addColumn('view_type', 'string', [ - 'limit' => 100, - 'null' => false, - 'default' => 'tabular' - ]); - $table->addColumn('view_query', 'text', [ - 'null' => true, - 'default' => null - ]); - $table->addColumn('view_options', 'text', [ - 'null' => true, - 'default' => null - ]); - $table->addColumn('translation', 'text', [ - 'null' => true, - 'default' => null - ]); - $table->addIndex(['user', 'collection', 'title'], [ - 'unique' => true, - 'name' => 'idx_user_collection_title' - ]); - - $table->create(); - } -} diff --git a/migrations/db/schemas/20180220023157_create_collections_table.php b/migrations/db/schemas/20180220023157_create_collections_table.php deleted file mode 100644 index e52b33a496..0000000000 --- a/migrations/db/schemas/20180220023157_create_collections_table.php +++ /dev/null @@ -1,71 +0,0 @@ -table('directus_collections', [ - 'id' => false, - 'primary_key' => 'collection' - ]); - - $table->addColumn('collection', 'string', [ - 'limit' => 64, - 'null' => false - ]); - $table->addColumn('managed', 'boolean', [ - 'signed' => false, - 'null' => false, - 'default' => true - ]); - $table->addColumn('hidden', 'boolean', [ - 'signed' => false, - 'null' => false, - 'default' => false - ]); - $table->addColumn('single', 'boolean', [ - 'signed' => false, - 'null' => false, - 'default' => false - ]); - $table->addColumn('icon', 'string', [ - 'limit' => 30, - 'null' => true, - 'default' => null - ]); - $table->addColumn('note', 'string', [ - 'limit' => 255, - 'null' => true, - 'default' => null - ]); - $table->addColumn('translation', 'text', [ - 'null' => true, - 'default' => null - ]); - - $table->create(); - } -} diff --git a/migrations/db/schemas/20180220023202_create_fields_table.php b/migrations/db/schemas/20180220023202_create_fields_table.php deleted file mode 100644 index 73d8891ed8..0000000000 --- a/migrations/db/schemas/20180220023202_create_fields_table.php +++ /dev/null @@ -1,115 +0,0 @@ -table('directus_fields', ['signed' => false]); - - $table->addColumn('collection', 'string', [ - 'limit' => 64, - 'null' => false - ]); - $table->addColumn('field', 'string', [ - 'limit' => 64, - 'null' => false - ]); - $table->addColumn('type', 'string', [ - 'limit' => 64, - 'null' => false - ]); - $table->addColumn('interface', 'string', [ - 'limit' => 64, - 'null' => true, - 'default' => null, - ]); - $table->addColumn('options', 'text', [ - 'null' => true, - 'default' => null - ]); - $table->addColumn('locked', 'boolean', [ - 'signed' => false, - 'null' => false, - 'default' => false - ]); - $table->addColumn('validation', 'string', [ - 'limit' => 255, - 'null' => true, - 'default' => null - ]); - $table->addColumn('required', 'boolean', [ - 'signed' => false, - 'null' => false, - 'default' => false - ]); - $table->addColumn('readonly', 'boolean', [ - 'signed' => false, - 'null' => false, - 'default' => false - ]); - $table->addColumn('hidden_detail', 'boolean', [ - 'signed' => false, - 'null' => false, - 'default' => 0 - ]); - $table->addColumn('hidden_browse', 'boolean', [ - 'signed' => false, - 'null' => false, - 'default' => 0 - ]); - $table->addColumn('sort', 'integer', [ - 'signed' => false, - 'null' => true, - 'default' => null - ]); - $table->addColumn('width', 'string', [ - 'limit' => 50, - 'null' => true, - 'default' => 'full' - ]); - $table->addColumn('group', 'integer', [ - 'signed' => false, - 'null' => true, - 'default' => null - ]); - $table->addColumn('note', 'string', [ - 'limit' => 1024, - 'null' => true, - 'default' => null - ]); - $table->addColumn('translation', 'text', [ - 'null' => true, - 'default' => null - ]); - - $table->addIndex(['collection', 'field'], [ - 'unique' => true, - 'name' => 'idx_collection_field' - ]); - - $table->create(); - } -} diff --git a/migrations/db/schemas/20180220023208_create_files_table.php b/migrations/db/schemas/20180220023208_create_files_table.php deleted file mode 100644 index 363ff572db..0000000000 --- a/migrations/db/schemas/20180220023208_create_files_table.php +++ /dev/null @@ -1,121 +0,0 @@ -table('directus_files', ['signed' => false]); - - $table->addColumn('storage', 'string', [ - 'limit' => 50, - 'null' => false, - 'default' => 'local' - ]); - $table->addColumn('filename', 'string', [ - 'limit' => 255, - 'null' => false, - 'default' => null - ]); - $table->addColumn('title', 'string', [ - 'limit' => 255, - 'null' => true, - 'default' => null - ]); - $table->addColumn('type', 'string', [ - 'limit' => 255, - 'null' => true, - 'default' => null // unknown type? - ]); - $table->addColumn('uploaded_by', 'integer', [ - 'signed' => false, - 'null' => false - ]); - // TODO: Make directus set this value to whatever default is on the server (UTC) - // In MySQL 5.5 and below doesn't support CURRENT TIMESTAMP on datetime as default - $table->addColumn('uploaded_on', 'datetime', [ - 'null' => false - ]); - $table->addColumn('charset', 'string', [ - 'limit' => 50, - 'null' => true, - 'default' => null - ]); - $table->addColumn('filesize', 'integer', [ - 'signed' => false, - 'default' => 0 - ]); - $table->addColumn('width', 'integer', [ - 'signed' => false, - 'null' => true, - 'default' => null - ]); - $table->addColumn('height', 'integer', [ - 'signed' => false, - 'null' => true, - 'default' => null - ]); - $table->addColumn('duration', 'integer', [ - 'signed' => true, - 'null' => true, - 'default' => null - ]); - $table->addColumn('embed', 'string', [ - 'limit' => 200, - 'null' => true, - 'default' => NULL - ]); - $table->addColumn('folder', 'integer', [ - 'signed' => false, - 'null' => true, - 'default' => null - ]); - $table->addColumn('description', 'text', [ - 'null' => true, - 'default' => null - ]); - $table->addColumn('location', 'string', [ - 'limit' => 200, - 'null' => true, - 'default' => null - ]); - $table->addColumn('tags', 'string', [ - 'limit' => 255, - 'null' => true, - 'default' => null - ]); - $table->addColumn('checksum', 'string', [ - 'limit' => 32, - 'null' => true, - 'default' => null - ]); - $table->addColumn('metadata', 'text', [ - 'null' => true, - 'default' => null - ]); - - $table->create(); - } -} diff --git a/migrations/db/schemas/20180220023213_create_folders_table.php b/migrations/db/schemas/20180220023213_create_folders_table.php deleted file mode 100644 index cb0596414d..0000000000 --- a/migrations/db/schemas/20180220023213_create_folders_table.php +++ /dev/null @@ -1,50 +0,0 @@ -table('directus_folders', ['signed' => false]); - - $table->addColumn('name', 'string', [ - 'limit' => 191, - 'null' => false, - 'encoding' => 'utf8mb4' - ]); - $table->addColumn('parent_folder', 'integer', [ - 'signed' => false, - 'null' => true, - 'default' => null - ]); - - $table->addIndex(['name', 'parent_folder'], [ - 'unique' => true, - 'name' => 'idx_name_parent_folder' - ]); - - $table->create(); - } -} diff --git a/migrations/db/schemas/20180220023217_create_roles_table.php b/migrations/db/schemas/20180220023217_create_roles_table.php deleted file mode 100644 index 2f07fe0110..0000000000 --- a/migrations/db/schemas/20180220023217_create_roles_table.php +++ /dev/null @@ -1,75 +0,0 @@ -table('directus_roles', ['signed' => false]); - - $table->addColumn('name', 'string', [ - 'limit' => 100, - 'null' => false - ]); - $table->addColumn('description', 'string', [ - 'limit' => 500, - 'null' => true, - 'default' => NULL - ]); - $table->addColumn('ip_whitelist', 'text', [ - 'null' => true, - 'default' => null - ]); - $table->addColumn('nav_blacklist', 'text', [ - 'null' => true, - 'default' => null - ]); - $table->addColumn('external_id', 'string', [ - 'limit' => 255, - 'null' => true, - 'default' => null - ]); - $table->addColumn('nav_override', 'text', [ - 'null' => true, - 'default' => null - ]); - $table->addColumn('enforce_2fa', 'boolean', [ - 'null' => true, - 'default' => false - ]); - - $table->addIndex('name', [ - 'unique' => true, - 'name' => 'idx_group_name' - ]); - - $table->addIndex('external_id', [ - 'unique' => true, - 'name' => 'idx_roles_external_id' - ]); - - $table->create(); - } -} diff --git a/migrations/db/schemas/20180220023226_create_permissions_table.php b/migrations/db/schemas/20180220023226_create_permissions_table.php deleted file mode 100644 index 47caedc97d..0000000000 --- a/migrations/db/schemas/20180220023226_create_permissions_table.php +++ /dev/null @@ -1,99 +0,0 @@ -table('directus_permissions', ['signed' => false]); - - $table->addColumn('collection', 'string', [ - 'limit' => 64, - 'null' => false, - ]); - $table->addColumn('role', 'integer', [ - 'signed' => false, - 'null' => false - ]); - $table->addColumn('status', 'string', [ - 'length' => 64, - 'default' => null, - 'null' => true - ]); - $table->addColumn('create', 'string', [ - 'signed' => false, - 'null' => true, - 'default' => 'none', - 'length' => 16, - ]); - $table->addColumn('read', 'string', [ - 'signed' => false, - 'null' => true, - 'default' => 'none', - 'length' => 16, - ]); - $table->addColumn('update', 'string', [ - 'signed' => false, - 'null' => true, - 'default' => 'none', - 'length' => 16, - ]); - $table->addColumn('delete', 'string', [ - 'signed' => false, - 'null' => true, - 'default' => 'none', - 'length' => 16, - ]); - $table->addColumn('comment', 'string', [ - 'limit' => 8, - 'null' => true, - 'default' => 'none' - ]); - $table->addColumn('explain', 'string', [ - 'limit' => 8, - 'null' => true, - 'default' => 'none' - ]); - $table->addColumn('read_field_blacklist', 'string', [ - 'limit' => 1000, - 'null' => true, - 'default' => null, - 'encoding' => 'utf8' - ]); - $table->addColumn('write_field_blacklist', 'string', [ - 'limit' => 1000, - 'null' => true, - 'default' => NULL, - 'encoding' => 'utf8', - ]); - $table->addColumn('status_blacklist', 'string', [ - 'length' => 1000, - 'default' => null, - 'null' => true - ]); - - $table->create(); - } -} diff --git a/migrations/db/schemas/20180220023232_create_relations_table.php b/migrations/db/schemas/20180220023232_create_relations_table.php deleted file mode 100644 index 7a2408abd8..0000000000 --- a/migrations/db/schemas/20180220023232_create_relations_table.php +++ /dev/null @@ -1,55 +0,0 @@ -table('directus_relations', ['signed' => false]); - - $table->addColumn('collection_many', 'string', [ - 'limit' => 64, - 'null' => false - ]); - $table->addColumn('field_many', 'string', [ - 'limit' => 45, - 'null' => false - ]); - $table->addColumn('collection_one', 'string', [ - 'limit' => 64, - 'null' => true - ]); - $table->addColumn('field_one', 'string', [ - 'limit' => 64, - 'null' => true - ]); - $table->addColumn('junction_field', 'string', [ - 'limit' => 64, - 'null' => true - ]); - - $table->create(); - } -} diff --git a/migrations/db/schemas/20180220023238_create_revisions_table.php b/migrations/db/schemas/20180220023238_create_revisions_table.php deleted file mode 100644 index fe7f729bb1..0000000000 --- a/migrations/db/schemas/20180220023238_create_revisions_table.php +++ /dev/null @@ -1,66 +0,0 @@ -table('directus_revisions', ['signed' => false]); - - $table->addColumn('activity', 'integer', [ - 'null' => false, - 'signed' => false - ]); - $table->addColumn('collection', 'string', [ - 'limit' => 64, - 'null' => false - ]); - $table->addColumn('item', 'string', [ - 'limit' => 255 - ]); - $table->addColumn('data', 'text', [ - 'limit' => 4294967295 - ]); - $table->addColumn('delta', 'text', [ - 'limit' => 4294967295, - 'null' => true - ]); - $table->addColumn('parent_collection', 'string', [ - 'limit' => 64, - 'null' => true - ]); - $table->addColumn('parent_item', 'string', [ - 'limit' => 255, - 'null' => true - ]); - $table->addColumn('parent_changed', 'boolean', [ - 'signed' => false, - 'default' => false, - 'null' => true - ]); - - $table->create(); - } -} diff --git a/migrations/db/schemas/20180220023243_create_settings_table.php b/migrations/db/schemas/20180220023243_create_settings_table.php deleted file mode 100644 index b2ca880b49..0000000000 --- a/migrations/db/schemas/20180220023243_create_settings_table.php +++ /dev/null @@ -1,47 +0,0 @@ -table('directus_settings', ['signed' => false]); - - $table->addColumn('key', 'string', [ - 'limit' => 64, - 'null' => false - ]); - $table->addColumn('value', 'text', [ - 'default' => null - ]); - - $table->addIndex(['key'], [ - 'unique' => true, - 'name' => 'idx_key' - ]); - - $table->create(); - } -} diff --git a/migrations/db/schemas/20180220023248_create_users_table.php b/migrations/db/schemas/20180220023248_create_users_table.php deleted file mode 100644 index 9aa70d77b4..0000000000 --- a/migrations/db/schemas/20180220023248_create_users_table.php +++ /dev/null @@ -1,143 +0,0 @@ -table('directus_users', ['signed' => false]); - - $table->addColumn('status', 'string', [ - 'limit' => 16, - 'default' => \Directus\Database\TableGateway\DirectusUsersTableGateway::STATUS_DRAFT - ]); - $table->addColumn('role', 'integer', [ - 'null' => true, - 'default' => null - ]); - $table->addColumn('first_name', 'string', [ - 'limit' => 50, - 'null' => true, - 'default' => null - ]); - $table->addColumn('last_name', 'string', [ - 'limit' => 50, - 'null' => true, - 'default' => null - ]); - $table->addColumn('email', 'string', [ - 'limit' => 128, - 'null' => false - ]); - $table->addColumn('password', 'string', [ - 'limit' => 255, - 'encoding' => 'utf8', - 'null' => true, - 'default' => null - ]); - $table->addColumn('token', 'string', [ - 'limit' => 255, - 'encoding' => 'utf8', - 'null' => true, - 'default' => null - ]); - $table->addColumn('timezone', 'string', [ - 'limit' => 32, - 'default' => date_default_timezone_get(), - ]); - $table->addColumn('locale', 'string', [ - 'limit' => 8, - 'null' => true, - 'default' => null - ]); - $table->addColumn('locale_options', 'text', [ - 'null' => true, - 'default' => null - ]); - $table->addColumn('avatar', 'integer', [ - 'signed' => false, - 'limit' => 11, - 'null' => true, - 'default' => null - ]); - $table->addColumn('company', 'string', [ - 'limit' => 191, - 'null' => true, - 'default' => null - ]); - $table->addColumn('title', 'string', [ - 'limit' => 191, - 'null' => true, - 'default' => null - ]); - $table->addColumn('email_notifications', 'integer', [ - 'limit' => 1, - 'default' => 1 - ]); - $table->addColumn('last_access_on', 'datetime', [ - 'null' => true, - 'default' => null - ]); - $table->addColumn('last_page', 'string', [ - 'limit' => 192, - 'null' => true, - 'default' => null - ]); - $table->addColumn('external_id', 'string', [ - 'limit' => 255, - 'null' => true, - 'default' => null - ]); - $table->addColumn('theme', 'string', [ - 'limit' => 100, - 'encoding' => 'utf8', - 'null' => true, - 'default' => 'auto' - ]); - $table->addColumn('2fa_secret', 'string', [ - 'limit' => 100, - 'encoding' => 'utf8', - 'null' => true, - 'default' => null - ]); - - $table->addIndex('email', [ - 'unique' => true, - 'name' => 'idx_users_email' - ]); - - $table->addIndex('token', [ - 'unique' => true, - 'name' => 'idx_users_token' - ]); - - $table->addIndex('external_id', [ - 'unique' => true, - 'name' => 'idx_users_external_id' - ]); - - $table->create(); - } -} diff --git a/migrations/db/schemas/20190912072543_create_user_sessions.php b/migrations/db/schemas/20190912072543_create_user_sessions.php deleted file mode 100644 index 72aec69d50..0000000000 --- a/migrations/db/schemas/20190912072543_create_user_sessions.php +++ /dev/null @@ -1,76 +0,0 @@ -table('directus_user_sessions', ['signed' => false]); - - $table->addColumn('user', 'integer', [ - 'signed' => false, - 'null' => true, - 'default' => null - ]); - - $table->addColumn('token_type', 'string', [ - 'null' => true, - 'default' => null - ]); - - $table->addColumn('token', 'string', [ - 'limit' => 520, - 'encoding' => 'utf8', - 'null' => true, - 'default' => null - ]); - - $table->addColumn('ip_address', 'string', [ - 'limit' => 255, - 'encoding' => 'utf8', - 'null' => true, - 'default' => null - ]); - - $table->addColumn('user_agent', 'text', [ - 'default' => null, - 'null' => true, - 'default' => null - ]); - - $table->addColumn('created_on', 'datetime', [ - 'null' => true, - 'default' => null - ]); - - $table->addColumn('token_expired_at', 'datetime', [ - 'null' => true, - 'default' => null - ]); - - $table->create(); - } -} diff --git a/migrations/db/schemas/20190917090849_create_web_hooks.php b/migrations/db/schemas/20190917090849_create_web_hooks.php deleted file mode 100644 index 1be2ad47a3..0000000000 --- a/migrations/db/schemas/20190917090849_create_web_hooks.php +++ /dev/null @@ -1,68 +0,0 @@ -table('directus_webhooks', ['signed' => false]); - - $table->addColumn('status', 'string', [ - 'limit' => 16, - 'default' => \Directus\Api\Routes\Webhook::STATUS_INACTIVE - ]); - - - $table->addColumn('http_action', 'string', [ - 'limit' => 255, - 'encoding' => 'utf8', - 'null' => true, - 'default' => null - ]); - - $table->addColumn('url', 'string', [ - 'limit' => 510, - 'encoding' => 'utf8', - 'null' => true, - 'default' => null - ]); - - $table->addColumn('collection', 'string', [ - 'limit' => 255, - 'null' => true, - 'default' => null - ]); - - $table->addColumn('directus_action', 'string', [ - 'limit' => 255, - 'encoding' => 'utf8', - 'null' => true, - 'default' => null - ]); - - $table->create(); - } -} diff --git a/migrations/db/seeds/CollectionPresetsSeeder.php b/migrations/db/seeds/CollectionPresetsSeeder.php deleted file mode 100644 index 65fae06913..0000000000 --- a/migrations/db/seeds/CollectionPresetsSeeder.php +++ /dev/null @@ -1,85 +0,0 @@ - 'directus_activity', - 'view_type' => 'timeline', - 'view_query' => json_encode([ - 'timeline' => [ - 'sort' => '-action_on' - ] - ]), - 'view_options' => json_encode([ - 'timeline' => [ - 'date' => 'action_on', - 'title' => '{{ action_by.first_name }} {{ action_by.last_name }} ({{ action }})', - 'content' => 'action_by', - 'color' => 'action' - ] - ]) - ], - [ - 'collection' => 'directus_files', - 'view_type' => 'cards', - 'view_options' => json_encode([ - 'cards' => [ - 'title' => 'title', - 'subtitle' => 'type', - 'content' => 'description', - 'src' => 'data' - ] - ]) - ], - [ - 'collection' => 'directus_users', - 'view_type' => 'cards', - 'view_options' => json_encode([ - 'cards' => [ - 'title' => 'first_name', - 'subtitle' => 'last_name', - 'content' => 'title', - 'src' => 'avatar', - 'icon' => 'person' - ] - ]) - ], - [ - 'collection' => 'directus_webhooks', - 'view_type' => 'tabular', - 'view_query' => json_encode([ - 'tabular' => [ - 'fields' => 'status,http_action,url,collection,directus_action' - ] - ]), - 'view_options' => json_encode([ - 'tabular' => [ - 'widths' => [ - 'status' => 32, - 'http_action' => 72, - 'url' => 200, - 'collection' => 200, - 'directus_action' => 200 - ] - ] - ]) - ] - ]; - - $files = $this->table('directus_collection_presets'); - $files->insert($data)->save(); - } -} diff --git a/migrations/db/seeds/FieldsSeeder.php b/migrations/db/seeds/FieldsSeeder.php deleted file mode 100644 index 59d6045bc4..0000000000 --- a/migrations/db/seeds/FieldsSeeder.php +++ /dev/null @@ -1,1915 +0,0 @@ - 'directus_activity', - 'field' => 'id', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'primary-key', - 'locked' => 1, - 'readonly' => 1, - 'required' => 1, - 'hidden_detail' => 1 - ], - [ - 'collection' => 'directus_activity', - 'field' => 'action', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'activity-icon', - 'options' => json_encode([ - 'iconRight' => 'change_history' - ]), - 'locked' => 1, - 'readonly' => 1, - 'sort' => 1, - 'width' => 'full' - ], - [ - 'collection' => 'directus_activity', - 'field' => 'collection', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'collections', - 'options' => json_encode([ - 'iconRight' => 'list_alt' - ]), - 'locked' => 1, - 'readonly' => 1, - 'sort' => 2, - 'width' => 'half' - ], - [ - 'collection' => 'directus_activity', - 'field' => 'item', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'options' => json_encode([ - 'iconRight' => 'link' - ]), - 'locked' => 1, - 'readonly' => 1, - 'sort' => 3, - 'width' => 'half' - ], - [ - 'collection' => 'directus_activity', - 'field' => 'action_by', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'user', - 'options' => json_encode([ - 'iconRight' => 'account_circle' - ]), - 'locked' => 1, - 'readonly' => 1, - 'sort' => 4, - 'width' => 'half' - ], - [ - 'collection' => 'directus_activity', - 'field' => 'action_on', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_DATETIME, - 'interface' => 'datetime', - 'options' => json_encode([ - 'showRelative' => true, - 'iconRight' => 'calendar_today' - ]), - 'locked' => 1, - 'readonly' => 1, - 'sort' => 5, - 'width' => 'half' - ], - [ - 'collection' => 'directus_activity', - 'field' => 'edited_on', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_DATETIME, - 'interface' => 'datetime', - 'options' => json_encode([ - 'showRelative' => true, - 'iconRight' => 'edit' - ]), - 'locked' => 1, - 'readonly' => 1, - 'sort' => 6, - 'width' => 'half' - ], - [ - 'collection' => 'directus_activity', - 'field' => 'comment_deleted_on', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_DATETIME, - 'interface' => 'datetime', - 'options' => json_encode([ - 'showRelative' => true, - 'iconRight' => 'delete_outline' - ]), - 'locked' => 1, - 'readonly' => 1, - 'sort' => 7, - 'width' => 'half' - ], - [ - 'collection' => 'directus_activity', - 'field' => 'ip', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'options' => json_encode([ - 'iconRight' => 'my_location' - ]), - 'locked' => 1, - 'readonly' => 1, - 'sort' => 8, - 'width' => 'half' - ], - [ - 'collection' => 'directus_activity', - 'field' => 'user_agent', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'options' => json_encode([ - 'iconRight' => 'devices_other' - ]), - 'locked' => 1, - 'readonly' => 1, - 'sort' => 9, - 'width' => 'half' - ], - [ - 'collection' => 'directus_activity', - 'field' => 'comment', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'textarea', - 'locked' => 1, - 'readonly' => 1, - 'sort' => 10, - 'width' => 'full' - ], - - - // Collection Presets - // ----------------------------------------------------------------- - [ - 'collection' => 'directus_collection_presets', - 'field' => 'id', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'primary-key', - 'locked' => 1 - ], - [ - 'collection' => 'directus_collection_presets', - 'field' => 'title', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1 - ], - [ - 'collection' => 'directus_collection_presets', - 'field' => 'user', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'user', - 'locked' => 1 - ], - [ - 'collection' => 'directus_collection_presets', - 'field' => 'role', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, - 'interface' => 'many-to-one', - 'locked' => 1 - ], - [ - 'collection' => 'directus_collection_presets', - 'field' => 'collection', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, - 'interface' => 'many-to-one', - 'locked' => 1 - ], - [ - 'collection' => 'directus_collection_presets', - 'field' => 'search_query', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1 - ], - [ - 'collection' => 'directus_collection_presets', - 'field' => 'filters', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, - 'interface' => 'json', - 'locked' => 1 - ], - [ - 'collection' => 'directus_collection_presets', - 'field' => 'view_options', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, - 'interface' => 'json', - 'locked' => 1 - ], - [ - 'collection' => 'directus_collection_presets', - 'field' => 'view_type', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1 - ], - [ - 'collection' => 'directus_collection_presets', - 'field' => 'view_query', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, - 'interface' => 'json', - 'locked' => 1 - ], - - // Collections - // ----------------------------------------------------------------- - [ - 'collection' => 'directus_collections', - 'field' => 'fields', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_O2M, - 'interface' => 'one-to-many', - 'locked' => 1, - 'hidden_detail' => 1, - 'hidden_browse' => 1, - 'sort' => 1 - ], - [ - 'collection' => 'directus_collections', - 'field' => 'collection', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'primary-key', - 'locked' => 1, - 'readonly' => 1, - 'required' => 1, - 'sort' => 2, - 'width' => 'half' - ], - [ - 'collection' => 'directus_collections', - 'field' => 'note', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1, - 'sort' => 3, - 'width' => 'half', - 'note' => 'An internal description.' - ], - [ - 'collection' => 'directus_collections', - 'field' => 'managed', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, - 'interface' => 'toggle', - 'locked' => 1, - 'sort' => 4, - 'width' => 'half', - 'hidden_detail' => 1, - 'note' => '[Learn More](https://docs.directus.io/guides/collections.html#managing-collections).' - ], - [ - 'collection' => 'directus_collections', - 'field' => 'hidden', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, - 'interface' => 'toggle', - 'locked' => 1, - 'sort' => 5, - 'width' => 'half', - 'note' => '[Learn More](https://docs.directus.io/guides/collections.html#hidden).' - ], - [ - 'collection' => 'directus_collections', - 'field' => 'single', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, - 'interface' => 'toggle', - 'locked' => 1, - 'sort' => 6, - 'width' => 'half', - 'note' => '[Learn More](https://docs.directus.io/guides/collections.html#single).' - ], - [ - 'collection' => 'directus_collections', - 'field' => 'translation', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, - 'interface' => 'json', - 'locked' => 1, - 'sort' => 7, - 'hidden_detail' => 1 - ], - [ - 'collection' => 'directus_collections', - 'field' => 'icon', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'icon', - 'locked' => 1, - 'sort' => 8, - 'note' => 'The icon shown in the App\'s navigation sidebar.' - ], - - - // Fields - // ----------------------------------------------------------------- - [ - 'collection' => 'directus_fields', - 'field' => 'id', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'primary-key', - 'locked' => 1, - 'required' => 1, - 'hidden_detail' => 1 - ], - [ - 'collection' => 'directus_fields', - 'field' => 'collection', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, - 'interface' => 'many-to-one', - 'locked' => 1 - ], - [ - 'collection' => 'directus_fields', - 'field' => 'field', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1 - ], - [ - 'collection' => 'directus_fields', - 'field' => 'type', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'primary-key', - 'locked' => 1 - ], - [ - 'collection' => 'directus_fields', - 'field' => 'interface', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'primary-key', - 'locked' => 1 - ], - [ - 'collection' => 'directus_fields', - 'field' => 'options', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, - 'interface' => 'json', - 'locked' => 1 - ], - [ - 'collection' => 'directus_fields', - 'field' => 'locked', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, - 'interface' => 'toggle', - 'locked' => 1 - ], - [ - 'collection' => 'directus_fields', - 'field' => 'translation', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, - 'interface' => 'json', - 'locked' => 1 - ], - [ - 'collection' => 'directus_fields', - 'field' => 'readonly', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, - 'interface' => 'toggle', - 'locked' => 1 - ], - [ - 'collection' => 'directus_fields', - 'field' => 'validation', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1, - ], - [ - 'collection' => 'directus_fields', - 'field' => 'required', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, - 'interface' => 'toggle', - 'locked' => 1 - ], - [ - 'collection' => 'directus_fields', - 'field' => 'sort', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_SORT, - 'interface' => 'sort', - 'locked' => 1 - ], - [ - 'collection' => 'directus_fields', - 'field' => 'note', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1 - ], - [ - 'collection' => 'directus_fields', - 'field' => 'hidden_detail', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, - 'interface' => 'toggle', - 'locked' => 1 - ], - [ - 'collection' => 'directus_fields', - 'field' => 'hidden_browse', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, - 'interface' => 'toggle', - 'locked' => 1 - ], - [ - 'collection' => 'directus_fields', - 'field' => 'width', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'numeric', - 'locked' => 1 - ], - [ - 'collection' => 'directus_fields', - 'field' => 'group', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, - 'interface' => 'many-to-one', - 'locked' => 1 - ], - - - // Files - // ----------------------------------------------------------------- - [ - 'collection' => 'directus_files', - 'field' => 'data', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_ALIAS, - 'interface' => 'file', - 'locked' => 1, - 'hidden_detail' => 1 - ], - [ - 'collection' => 'directus_files', - 'field' => 'id', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'primary-key', - 'locked' => 1, - 'required' => 1, - 'hidden_detail' => 1 - ], - [ - 'collection' => 'directus_files', - 'field' => 'preview', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_ALIAS, - 'interface' => 'file-preview', - 'locked' => 1, - 'sort' => 1, - 'width' => 'full' - ], - [ - 'collection' => 'directus_files', - 'field' => 'title', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'options' => json_encode([ - 'placeholder' => 'Enter a descriptive title...', - 'iconRight' => 'title' - ]), - 'locked' => 1, - 'sort' => 3, - 'width' => 'half' - ], - [ - 'collection' => 'directus_files', - 'field' => 'filename', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'options' => json_encode([ - 'placeholder' => 'Enter a unique file name...', - 'iconRight' => 'insert_drive_file' - ]), - 'locked' => 1, - 'readonly' => 1, - 'sort' => 4, - 'width' => 'half', - 'required' => 1 - ], - [ - 'collection' => 'directus_files', - 'field' => 'tags', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_ARRAY, - 'interface' => 'tags', - 'options' => json_encode([ - 'placeholder' => 'Enter a keyword then hit enter...' - ]), - 'sort' => 5, - 'width' => 'half' - ], - [ - 'collection' => 'directus_files', - 'field' => 'location', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'options' => json_encode([ - 'placeholder' => 'Enter a location...', - 'iconRight' => 'place' - ]), - 'sort' => 6, - 'width' => 'half' - ], - [ - 'collection' => 'directus_files', - 'field' => 'description', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'wysiwyg', - 'options' => json_encode([ - 'toolbar' => ['bold','italic','underline','link','code'] - ]), - 'sort' => 7, - 'width' => 'full' - ], - [ - 'collection' => 'directus_files', - 'field' => 'uploaded_on', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_DATETIME, - 'interface' => 'datetime', - 'options' => json_encode([ - 'iconRight' => 'today' - ]), - 'locked' => 1, - 'readonly' => 1, - 'sort' => 8, - 'width' => 'half', - 'required' => 1 - ], - [ - 'collection' => 'directus_files', - 'field' => 'uploaded_by', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_USER_CREATED, - 'interface' => 'user-created', - 'locked' => 1, - 'readonly' => 1, - 'sort' => 9, - 'width' => 'half', - 'required' => 1 - ], - [ - 'collection' => 'directus_files', - 'field' => 'width', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'numeric', - 'options' => json_encode([ - 'iconRight' => 'straighten' - ]), - 'locked' => 1, - 'readonly' => 1, - 'sort' => 10, - 'width' => 'half' - ], - [ - 'collection' => 'directus_files', - 'field' => 'height', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'numeric', - 'options' => json_encode([ - 'iconRight' => 'straighten' - ]), - 'locked' => 1, - 'readonly' => 1, - 'sort' => 11, - 'width' => 'half' - ], - [ - 'collection' => 'directus_files', - 'field' => 'duration', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'numeric', - 'options' => json_encode([ - 'iconRight' => 'timer' - ]), - 'locked' => 1, - 'readonly' => 1, - 'sort' => 12, - 'width' => 'half' - ], - [ - 'collection' => 'directus_files', - 'field' => 'filesize', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'file-size', - 'options' => json_encode([ - 'iconRight' => 'storage' - ]), - 'locked' => 1, - 'readonly' => 1, - 'sort' => 13, - 'width' => 'half' - ], - [ - 'collection' => 'directus_files', - 'field' => 'metadata', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, - 'interface' => 'json', - 'locked' => 1, - 'sort' => 14, - 'width' => 'full' - ], - [ - 'collection' => 'directus_files', - 'field' => 'type', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1, - 'readonly' => 1, - 'hidden_detail' => 1 - ], - [ - 'collection' => 'directus_files', - 'field' => 'charset', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1, - 'readonly' => 1, - 'hidden_detail' => 1, - 'hidden_browse' => 1 - ], - [ - 'collection' => 'directus_files', - 'field' => 'embed', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1, - 'readonly' => 1, - 'hidden_detail' => 1 - ], - [ - 'collection' => 'directus_files', - 'field' => 'folder', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, - 'interface' => 'many-to-one', - 'locked' => 1, - 'hidden_detail' => 1 - ], - [ - 'collection' => 'directus_files', - 'field' => 'storage', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1, - 'hidden_detail' => 1, - 'hidden_browse' => 1 - ], - [ - 'collection' => 'directus_files', - 'field' => 'checksum', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1, - 'readonly' => 1, - 'hidden_detail' => 1, - 'hidden_browse' => 1 - ], - - - // Folders - // ----------------------------------------------------------------- - [ - 'collection' => 'directus_folders', - 'field' => 'id', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'primary-key', - 'locked' => 1, - 'required' => 1, - 'hidden_detail' => 1 - ], - [ - 'collection' => 'directus_folders', - 'field' => 'name', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1 - ], - [ - 'collection' => 'directus_folders', - 'field' => 'parent_folder', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, - 'interface' => 'many-to-one', - 'locked' => 1 - ], - - - // Permissions - // ----------------------------------------------------------------- - [ - 'collection' => 'directus_permissions', - 'field' => 'id', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'primary-key', - 'locked' => 1, - 'required' => 1, - 'hidden_detail' => 1 - ], - [ - 'collection' => 'directus_permissions', - 'field' => 'collection', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, - 'interface' => 'many-to-one', - 'locked' => 1 - ], - [ - 'collection' => 'directus_permissions', - 'field' => 'role', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, - 'interface' => 'many-to-one', - 'locked' => 1 - ], - [ - 'collection' => 'directus_permissions', - 'field' => 'status', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1 - ], - [ - 'collection' => 'directus_permissions', - 'field' => 'create', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1 - ], - [ - 'collection' => 'directus_permissions', - 'field' => 'read', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1 - ], - [ - 'collection' => 'directus_permissions', - 'field' => 'update', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1 - ], - [ - 'collection' => 'directus_permissions', - 'field' => 'delete', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'primary-key', - 'locked' => 1 - ], - [ - 'collection' => 'directus_permissions', - 'field' => 'comment', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1 - ], - [ - 'collection' => 'directus_permissions', - 'field' => 'explain', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1 - ], - [ - 'collection' => 'directus_permissions', - 'field' => 'status_blacklist', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_ARRAY, - 'interface' => 'tags', - 'locked' => 1 - ], - [ - 'collection' => 'directus_permissions', - 'field' => 'read_field_blacklist', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_ARRAY, - 'interface' => 'tags', - 'locked' => 1 - ], - [ - 'collection' => 'directus_permissions', - 'field' => 'write_field_blacklist', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_ARRAY, - 'interface' => 'tags', - 'locked' => 1 - ], - - - // Relations - // ----------------------------------------------------------------- - [ - 'collection' => 'directus_relations', - 'field' => 'id', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'primary-key', - 'locked' => 1 - ], - [ - 'collection' => 'directus_relations', - 'field' => 'collection_many', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'collections', - 'locked' => 1 - ], - [ - 'collection' => 'directus_relations', - 'field' => 'field_many', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1 - ], - [ - 'collection' => 'directus_relations', - 'field' => 'collection_one', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'collections', - 'locked' => 1 - ], - [ - 'collection' => 'directus_relations', - 'field' => 'field_one', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1 - ], - [ - 'collection' => 'directus_relations', - 'field' => 'junction_field', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1 - ], - - - // Revisions - // ----------------------------------------------------------------- - [ - 'collection' => 'directus_revisions', - 'field' => 'id', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'primary-key', - 'locked' => 1 - ], - [ - 'collection' => 'directus_revisions', - 'field' => 'activity', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, - 'interface' => 'many-to-one', - 'locked' => 1 - ], - [ - 'collection' => 'directus_revisions', - 'field' => 'collection', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, - 'interface' => 'many-to-one', - 'locked' => 1 - ], - [ - 'collection' => 'directus_revisions', - 'field' => 'item', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1 - ], - [ - 'collection' => 'directus_revisions', - 'field' => 'data', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, - 'interface' => 'json', - 'locked' => 1 - ], - [ - 'collection' => 'directus_revisions', - 'field' => 'delta', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, - 'interface' => 'json', - 'locked' => 1 - ], - [ - 'collection' => 'directus_revisions', - 'field' => 'parent_item', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1 - ], - [ - 'collection' => 'directus_revisions', - 'field' => 'parent_collection', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'collections', - 'locked' => 1 - ], - [ - 'collection' => 'directus_revisions', - 'field' => 'parent_changed', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, - 'interface' => 'toggle', - 'locked' => 1 - ], - - - // Roles - // ----------------------------------------------------------------- - [ - 'collection' => 'directus_roles', - 'field' => 'id', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'primary-key', - 'locked' => 1, - 'required' => 1, - 'hidden_detail' => 1 - ], - [ - 'collection' => 'directus_roles', - 'field' => 'external_id', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1, - 'readonly' => 1, - 'hidden_detail' => 1, - 'hidden_browse' => 1 - ], - [ - 'collection' => 'directus_roles', - 'field' => 'name', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1, - 'sort' => 1, - 'width' => 'half', - 'required' => 1 - ], - [ - 'collection' => 'directus_roles', - 'field' => 'description', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1, - 'sort' => 2, - 'width' => 'half' - ], - [ - 'collection' => 'directus_roles', - 'field' => 'ip_whitelist', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'textarea', - 'locked' => 1 - ], - [ - 'collection' => 'directus_roles', - 'field' => 'nav_blacklist', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'textarea', - 'locked' => 1, - 'hidden_detail' => 1, - 'hidden_browse' => 1 - ], - [ - 'collection' => 'directus_roles', - 'field' => 'users', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_O2M, - 'interface' => 'one-to-many', - 'locked' => 1, - 'options' => json_encode([ - 'fields' => "first_name,last_name" - ]) - ], - [ - 'collection' => 'directus_roles', - 'field' => 'nav_override', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, - 'interface' => 'json', - 'locked' => 1, - 'options' => '[ - { - "title": "$t:collections", - "include": "collections" - }, - { - "title": "$t:bookmarks", - "include": "bookmarks" - }, - { - "title": "$t:extensions", - "include": "extensions" - }, - { - "title": "Custom Links", - "links": [ - { - "name": "RANGER Studio", - "path": "https://rangerstudio.com", - "icon": "star" - }, - { - "name": "Movies", - "path": "/collections/movies" - } - ] - } - ]' - ], - - // Settings - // ----------------------------------------------------------------- - [ - 'collection' => 'directus_settings', - 'field' => 'project_name', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'options' => json_encode([ - 'iconRight' => 'title' - ]), - 'locked' => 1, - 'required' => 1, - 'width' => 'half', - 'note' => 'Logo in the top-left of the App (40x40)', - 'sort' => 1 - ], - [ - 'collection' => 'directus_settings', - 'field' => 'project_url', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'options' => json_encode([ - 'iconRight' => 'link' - ]), - 'locked' => 1, - 'width' => 'half', - 'note' => 'External link for the App\'s top-left logo', - 'sort' => 2 - ], - [ - 'collection' => 'directus_settings', - 'field' => 'project_logo', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_FILE, - 'interface' => 'file', - 'locked' => 1, - 'width' => 'half', - 'note' => 'A 40x40 brand logo, ideally a white SVG/PNG', - 'sort' => 3 - ], - [ - 'collection' => 'directus_settings', - 'field' => 'project_color', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'color-palette', - 'locked' => 1, - 'width' => 'half', - 'note' => 'Color for login background and App\'s logo', - 'sort' => 4 - ], - [ - 'collection' => 'directus_settings', - 'field' => 'project_foreground', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_FILE, - 'interface' => 'file', - 'locked' => 1, - 'width' => 'half', - 'note' => 'Centered image (eg: logo) for the login page', - 'sort' => 5 - ], - [ - 'collection' => 'directus_settings', - 'field' => 'project_background', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_FILE, - 'interface' => 'file', - 'locked' => 1, - 'width' => 'half', - 'note' => 'Full-screen background for the login page', - 'sort' => 6 - ], - [ - 'collection' => 'directus_settings', - 'field' => 'default_locale', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'language', - 'locked' => 1, - 'width' => 'half', - 'note' => 'Default locale for Directus Users', - 'sort' => 7, - 'options' => json_encode([ - 'limit' => true - ]) - ], - [ - 'collection' => 'directus_settings', - 'field' => 'telemetry', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, - 'interface' => 'toggle', - 'locked' => 1, - 'width' => 'half', - 'note' => 'Learn More', - 'sort' => 8 - ], - [ - 'collection' => 'directus_settings', - 'field' => 'data_divider', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_ALIAS, - 'interface' => 'divider', - 'options' => json_encode([ - 'style' => 'large', - 'title' => 'Data', - 'hr' => true - ]), - 'locked' => 1, - 'width' => 'full', - 'hidden_browse' => 1, - 'sort' => 10 - ], - [ - 'collection' => 'directus_settings', - 'field' => 'default_limit', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'numeric', - 'options' => json_encode([ - 'iconRight' => 'keyboard_tab' - ]), - 'locked' => 1, - 'required' => 1, - 'width' => 'half', - 'note' => 'Default item count in API and App responses', - 'sort' => 11 - ], - [ - 'collection' => 'directus_settings', - 'field' => 'sort_null_last', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, - 'interface' => 'toggle', - 'locked' => 1, - 'note' => 'NULL values are sorted last', - 'width' => 'half', - 'sort' => 12 - ], - [ - 'collection' => 'directus_settings', - 'field' => 'security_divider', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_ALIAS, - 'interface' => 'divider', - 'options' => json_encode([ - 'style' => 'large', - 'title' => 'Security', - 'hr' => true - ]), - 'locked' => 1, - 'hidden_browse' => 1, - 'width' => 'full', - 'sort' => 20 - ], - [ - 'collection' => 'directus_settings', - 'field' => 'auto_sign_out', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'numeric', - 'options' => json_encode([ - 'iconRight' => 'timer' - ]), - 'locked' => 1, - 'required' => 1, - 'width' => 'half', - 'note' => 'Minutes before idle users are signed out', - 'sort' => 22 - ], - [ - 'collection' => 'directus_settings', - 'field' => 'login_attempts_allowed', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'numeric', - 'options' => json_encode([ - 'iconRight' => 'lock' - ]), - 'locked' => 1, - 'width' => 'half', - 'note' => 'Failed login attempts before suspending users', - 'sort' => 23 - ], - [ - 'collection' => 'directus_settings', - 'field' => 'files_divider', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_ALIAS, - 'interface' => 'divider', - 'options' => json_encode([ - 'style' => 'large', - 'title' => 'Files & Thumbnails', - 'hr' => true - ]), - 'locked' => 1, - 'hidden_browse' => 1, - 'width' => 'full', - 'sort' => 30 - ], - [ - 'collection' => 'directus_settings', - 'field' => 'file_naming', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'dropdown', - 'locked' => 1, - 'width' => 'half', - 'note' => 'File-system naming convention for uploads', - 'sort' => 31, - 'options' => json_encode([ - 'choices' => [ - 'uuid' => 'File Hash (Obfuscated)', - 'file_name' => 'File Name (Readable)' - ] - ]) - ], - [ - 'collection' => 'directus_settings', - 'field' => 'file_max_size', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'options' => json_encode([ - 'placeholder' => 'eg: 4MB', - 'iconRight' => 'storage' - ]), - 'locked' => 1, - 'width' => 'half', - 'sort' => 32 - ], - [ - 'collection' => 'directus_settings', - 'field' => 'file_mimetype_whitelist', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_ARRAY, - 'interface' => 'tags', - 'options' => json_encode([ - 'placeholder' => 'Enter a file mimetype then hit enter (eg: image/jpeg)' - ]), - 'locked' => 1, - 'width' => 'full', - 'sort' => 33 - ], - [ - 'collection' => 'directus_settings', - 'field' => 'thumbnail_dimensions', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_ARRAY, - 'interface' => 'tags', - 'options' => json_encode([ - 'placeholder' => 'Allowed dimensions for thumbnails (eg: 200x200)' - ]), - 'locked' => 1, - 'width' => 'full', - 'note' => 'Allowed dimensions for thumbnails.', - 'sort' => 34 - ], - [ - 'collection' => 'directus_settings', - 'field' => 'thumbnail_quality_tags', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, - 'interface' => 'json', - 'locked' => 1, - 'width' => 'half', - 'note' => 'Allowed qualities for thumbnails', - 'sort' => 35 - ], - [ - 'collection' => 'directus_settings', - 'field' => 'thumbnail_actions', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, - 'interface' => 'json', - 'locked' => 1, - 'width' => 'half', - 'note' => 'Defines how the thumbnail will be generated based on the requested dimensions', - 'sort' => 36 - ], - [ - 'collection' => 'directus_settings', - 'field' => 'thumbnail_not_found_location', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'options' => json_encode([ - 'iconRight' => 'broken_image' - ]), - 'locked' => 1, - 'width' => 'full', - 'note' => 'A fallback image used when thumbnail generation fails', - 'sort' => 37 - ], - [ - 'collection' => 'directus_settings', - 'field' => 'thumbnail_cache_ttl', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'numeric', - 'options' => json_encode([ - 'iconRight' => 'cached' - ]), - 'locked' => 1, - 'width' => 'half', - 'required' => 1, - 'note' => 'Seconds before browsers re-fetch thumbnails', - 'sort' => 38 - ], - [ - 'collection' => 'directus_settings', - 'field' => 'youtube_api', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'options' => json_encode([ - 'iconRight' => 'videocam' - ]), - 'locked' => 1, - 'width' => 'half', - 'note' => 'Allows fetching more YouTube Embed info', - 'sort' => 39 - ], - - // Users - // ----------------------------------------------------------------- - [ - 'collection' => 'directus_users', - 'field' => 'id', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'primary-key', - 'locked' => 1, - 'required' => 1, - 'hidden_detail' => 1, - 'sort' => 1 - ], - [ - 'collection' => 'directus_users', - 'field' => 'status', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STATUS, - 'interface' => 'status', - 'options' => json_encode([ - 'status_mapping' => [ - 'draft' => [ - 'name' => 'Draft', - 'text_color' => 'white', - 'background_color' => 'light-gray', - 'listing_subdued' => false, - 'listing_badge' => true, - 'soft_delete' => false, - ], - 'invited' => [ - 'name' => 'Invited', - 'text_color' => 'white', - 'background_color' => 'light-gray', - 'listing_subdued' => false, - 'listing_badge' => true, - 'soft_delete' => false, - ], - 'active' => [ - 'name' => 'Active', - 'text_color' => 'white', - 'background_color' => 'success', - 'listing_subdued' => false, - 'listing_badge' => false, - 'soft_delete' => false, - ], - 'suspended' => [ - 'name' => 'Suspended', - 'text_color' => 'white', - 'background_color' => 'light-gray', - 'listing_subdued' => false, - 'listing_badge' => true, - 'soft_delete' => false, - ], - 'deleted' => [ - 'name' => 'Deleted', - 'text_color' => 'white', - 'background_color' => 'danger', - 'listing_subdued' => false, - 'listing_badge' => true, - 'soft_delete' => true, - ] - ] - ]), - 'locked' => 1, - 'sort' => 2, - 'required' => 1 - ], - [ - 'collection' => 'directus_users', - 'field' => 'first_name', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'options' => json_encode([ - 'iconRight' => 'account_circle' - ]), - 'locked' => 1, - 'required' => 1, - 'sort' => 3, - 'width' => 'half' - ], - [ - 'collection' => 'directus_users', - 'field' => 'last_name', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'options' => json_encode([ - 'iconRight' => 'account_circle' - ]), - 'locked' => 1, - 'required' => 1, - 'sort' => 4, - 'width' => 'half' - ], - [ - 'collection' => 'directus_users', - 'field' => 'email', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'options' => json_encode([ - 'iconRight' => 'alternate_email' - ]), - 'locked' => 1, - 'validation' => '$email', - 'required' => 1, - 'sort' => 5, - 'width' => 'half' - ], - [ - 'collection' => 'directus_users', - 'field' => 'email_notifications', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, - 'interface' => 'toggle', - 'locked' => 1, - 'sort' => 6, - 'width' => 'half' - ], - [ - 'collection' => 'directus_users', - 'field' => 'password', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_HASH, - 'interface' => 'password', - 'locked' => 1, - 'required' => 1, - 'sort' => 7, - 'width' => 'half' - ], - [ - 'collection' => 'directus_users', - 'field' => 'role', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, - 'interface' => 'user-roles', - 'locked' => 1, - 'sort' => 8, - 'width' => 'half', - 'required' => 1 - ], - [ - 'collection' => 'directus_users', - 'field' => 'company', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'options' => json_encode([ - 'iconRight' => 'location_city' - ]), - 'sort' => 9, - 'width' => 'half' - ], - [ - 'collection' => 'directus_users', - 'field' => 'title', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'options' => json_encode([ - 'iconRight' => 'text_fields' - ]), - 'sort' => 10, - 'width' => 'half' - ], - [ - 'collection' => 'directus_users', - 'field' => 'timezone', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'dropdown', - 'options' => json_encode([ - 'choices' => [ - 'Pacific/Midway' => '(UTC-11:00) Midway Island', - 'Pacific/Samoa' => '(UTC-11:00) Samoa', - 'Pacific/Honolulu' => '(UTC-10:00) Hawaii', - 'US/Alaska' => '(UTC-09:00) Alaska', - 'America/Los_Angeles' => '(UTC-08:00) Pacific Time (US & Canada)', - 'America/Tijuana' => '(UTC-08:00) Tijuana', - 'US/Arizona' => '(UTC-07:00) Arizona', - 'America/Chihuahua' => '(UTC-07:00) Chihuahua', - 'America/Mexico/La_Paz' => '(UTC-07:00) La Paz', - 'America/Mazatlan' => '(UTC-07:00) Mazatlan', - 'US/Mountain' => '(UTC-07:00) Mountain Time (US & Canada)', - 'America/Managua' => '(UTC-06:00) Central America', - 'US/Central' => '(UTC-06:00) Central Time (US & Canada)', - 'America/Guadalajara' => '(UTC-06:00) Guadalajara', - 'America/Mexico_City' => '(UTC-06:00) Mexico City', - 'America/Monterrey' => '(UTC-06:00) Monterrey', - 'Canada/Saskatchewan' => '(UTC-06:00) Saskatchewan', - 'America/Bogota' => '(UTC-05:00) Bogota', - 'US/Eastern' => '(UTC-05:00) Eastern Time (US & Canada)', - 'US/East-Indiana' => '(UTC-05:00) Indiana (East)', - 'America/Lima' => '(UTC-05:00) Lima', - 'America/Quito' => '(UTC-05:00) Quito', - 'Canada/Atlantic' => '(UTC-04:00) Atlantic Time (Canada)', - 'America/New_York' => '(UTC-04:00) New York', - 'America/Caracas' => '(UTC-04:30) Caracas', - 'America/La_Paz' => '(UTC-04:00) La Paz', - 'America/Santiago' => '(UTC-04:00) Santiago', - 'America/Santo_Domingo' => '(UTC-04:00) Santo Domingo', - 'Canada/Newfoundland' => '(UTC-03:30) Newfoundland', - 'America/Sao_Paulo' => '(UTC-03:00) Brasilia', - 'America/Argentina/Buenos_Aires' => '(UTC-03:00) Buenos Aires', - 'America/Argentina/GeorgeTown' => '(UTC-03:00) Georgetown', - 'America/Godthab' => '(UTC-03:00) Greenland', - 'America/Noronha' => '(UTC-02:00) Mid-Atlantic', - 'Atlantic/Azores' => '(UTC-01:00) Azores', - 'Atlantic/Cape_Verde' => '(UTC-01:00) Cape Verde Is.', - 'Africa/Casablanca' => '(UTC+00:00) Casablanca', - 'Europe/Edinburgh' => '(UTC+00:00) Edinburgh', - 'Etc/Greenwich' => '(UTC+00:00) Greenwich Mean Time : Dublin', - 'Europe/Lisbon' => '(UTC+00:00) Lisbon', - 'Europe/London' => '(UTC+00:00) London', - 'Africa/Monrovia' => '(UTC+00:00) Monrovia', - 'UTC' => '(UTC+00:00) UTC', - 'Europe/Amsterdam' => '(UTC+01:00) Amsterdam', - 'Europe/Belgrade' => '(UTC+01:00) Belgrade', - 'Europe/Berlin' => '(UTC+01:00) Berlin', - 'Europe/Bern' => '(UTC+01:00) Bern', - 'Europe/Bratislava' => '(UTC+01:00) Bratislava', - 'Europe/Brussels' => '(UTC+01:00) Brussels', - 'Europe/Budapest' => '(UTC+01:00) Budapest', - 'Europe/Copenhagen' => '(UTC+01:00) Copenhagen', - 'Europe/Ljubljana' => '(UTC+01:00) Ljubljana', - 'Europe/Madrid' => '(UTC+01:00) Madrid', - 'Europe/Paris' => '(UTC+01:00) Paris', - 'Europe/Prague' => '(UTC+01:00) Prague', - 'Europe/Rome' => '(UTC+01:00) Rome', - 'Europe/Sarajevo' => '(UTC+01:00) Sarajevo', - 'Europe/Skopje' => '(UTC+01:00) Skopje', - 'Europe/Stockholm' => '(UTC+01:00) Stockholm', - 'Europe/Vienna' => '(UTC+01:00) Vienna', - 'Europe/Warsaw' => '(UTC+01:00) Warsaw', - 'Africa/Lagos' => '(UTC+01:00) West Central Africa', - 'Europe/Zagreb' => '(UTC+01:00) Zagreb', - 'Europe/Athens' => '(UTC+02:00) Athens', - 'Europe/Bucharest' => '(UTC+02:00) Bucharest', - 'Africa/Cairo' => '(UTC+02:00) Cairo', - 'Africa/Harare' => '(UTC+02:00) Harare', - 'Europe/Helsinki' => '(UTC+02:00) Helsinki', - 'Europe/Istanbul' => '(UTC+02:00) Istanbul', - 'Asia/Jerusalem' => '(UTC+02:00) Jerusalem', - 'Europe/Kyiv' => '(UTC+02:00) Kyiv', - 'Africa/Johannesburg' => '(UTC+02:00) Pretoria', - 'Europe/Riga' => '(UTC+02:00) Riga', - 'Europe/Sofia' => '(UTC+02:00) Sofia', - 'Europe/Tallinn' => '(UTC+02:00) Tallinn', - 'Europe/Vilnius' => '(UTC+02:00) Vilnius', - 'Asia/Baghdad' => '(UTC+03:00) Baghdad', - 'Asia/Kuwait' => '(UTC+03:00) Kuwait', - 'Europe/Minsk' => '(UTC+03:00) Minsk', - 'Africa/Nairobi' => '(UTC+03:00) Nairobi', - 'Asia/Riyadh' => '(UTC+03:00) Riyadh', - 'Europe/Volgograd' => '(UTC+03:00) Volgograd', - 'Asia/Tehran' => '(UTC+03:30) Tehran', - 'Asia/Abu_Dhabi' => '(UTC+04:00) Abu Dhabi', - 'Asia/Baku' => '(UTC+04:00) Baku', - 'Europe/Moscow' => '(UTC+04:00) Moscow', - 'Asia/Muscat' => '(UTC+04:00) Muscat', - 'Europe/St_Petersburg' => '(UTC+04:00) St. Petersburg', - 'Asia/Tbilisi' => '(UTC+04:00) Tbilisi', - 'Asia/Yerevan' => '(UTC+04:00) Yerevan', - 'Asia/Kabul' => '(UTC+04:30) Kabul', - 'Asia/Islamabad' => '(UTC+05:00) Islamabad', - 'Asia/Karachi' => '(UTC+05:00) Karachi', - 'Asia/Tashkent' => '(UTC+05:00) Tashkent', - 'Asia/Calcutta' => '(UTC+05:30) Chennai', - 'Asia/Kolkata' => '(UTC+05:30) Kolkata', - 'Asia/Mumbai' => '(UTC+05:30) Mumbai', - 'Asia/New_Delhi' => '(UTC+05:30) New Delhi', - 'Asia/Sri_Jayawardenepura' => '(UTC+05:30) Sri Jayawardenepura', - 'Asia/Katmandu' => '(UTC+05:45) Kathmandu', - 'Asia/Almaty' => '(UTC+06:00) Almaty', - 'Asia/Astana' => '(UTC+06:00) Astana', - 'Asia/Dhaka' => '(UTC+06:00) Dhaka', - 'Asia/Yekaterinburg' => '(UTC+06:00) Ekaterinburg', - 'Asia/Rangoon' => '(UTC+06:30) Rangoon', - 'Asia/Bangkok' => '(UTC+07:00) Bangkok', - 'Asia/Hanoi' => '(UTC+07:00) Hanoi', - 'Asia/Jakarta' => '(UTC+07:00) Jakarta', - 'Asia/Novosibirsk' => '(UTC+07:00) Novosibirsk', - 'Asia/Beijing' => '(UTC+08:00) Beijing', - 'Asia/Chongqing' => '(UTC+08:00) Chongqing', - 'Asia/Hong_Kong' => '(UTC+08:00) Hong Kong', - 'Asia/Krasnoyarsk' => '(UTC+08:00) Krasnoyarsk', - 'Asia/Kuala_Lumpur' => '(UTC+08:00) Kuala Lumpur', - 'Australia/Perth' => '(UTC+08:00) Perth', - 'Asia/Singapore' => '(UTC+08:00) Singapore', - 'Asia/Taipei' => '(UTC+08:00) Taipei', - 'Asia/Ulan_Bator' => '(UTC+08:00) Ulaan Bataar', - 'Asia/Urumqi' => '(UTC+08:00) Urumqi', - 'Asia/Irkutsk' => '(UTC+09:00) Irkutsk', - 'Asia/Osaka' => '(UTC+09:00) Osaka', - 'Asia/Sapporo' => '(UTC+09:00) Sapporo', - 'Asia/Seoul' => '(UTC+09:00) Seoul', - 'Asia/Tokyo' => '(UTC+09:00) Tokyo', - 'Australia/Adelaide' => '(UTC+09:30) Adelaide', - 'Australia/Darwin' => '(UTC+09:30) Darwin', - 'Australia/Brisbane' => '(UTC+10:00) Brisbane', - 'Australia/Canberra' => '(UTC+10:00) Canberra', - 'Pacific/Guam' => '(UTC+10:00) Guam', - 'Australia/Hobart' => '(UTC+10:00) Hobart', - 'Australia/Melbourne' => '(UTC+10:00) Melbourne', - 'Pacific/Port_Moresby' => '(UTC+10:00) Port Moresby', - 'Australia/Sydney' => '(UTC+10:00) Sydney', - 'Asia/Yakutsk' => '(UTC+10:00) Yakutsk', - 'Asia/Vladivostok' => '(UTC+11:00) Vladivostok', - 'Pacific/Auckland' => '(UTC+12:00) Auckland', - 'Pacific/Fiji' => '(UTC+12:00) Fiji', - 'Pacific/Kwajalein' => '(UTC+12:00) International Date Line West', - 'Asia/Kamchatka' => '(UTC+12:00) Kamchatka', - 'Asia/Magadan' => '(UTC+12:00) Magadan', - 'Pacific/Marshall_Is' => '(UTC+12:00) Marshall Is.', - 'Asia/New_Caledonia' => '(UTC+12:00) New Caledonia', - 'Asia/Solomon_Is' => '(UTC+12:00) Solomon Is.', - 'Pacific/Wellington' => '(UTC+12:00) Wellington', - 'Pacific/Tongatapu' => '(UTC+13:00) Nuku\'alofa' - ], - 'placeholder' => 'Choose a timezone...' - ]), - 'locked' => 1, - 'sort' => 11, - 'width' => 'half', - 'required' => 1 - ], - [ - 'collection' => 'directus_users', - 'field' => 'locale', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'language', - 'options' => json_encode([ - 'limit' => true - ]), - 'locked' => 1, - 'sort' => 12, - 'width' => 'half', - 'required' => 0 - ], - [ - 'collection' => 'directus_users', - 'field' => 'avatar', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_FILE, - 'interface' => 'file', - 'locked' => 1, - 'sort' => 13 - ], - [ - 'collection' => 'directus_users', - 'field' => 'theme', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'radio-buttons', - 'options' => json_encode([ - 'format' => true, - 'choices' => [ - 'auto' => 'Auto', - 'light' => 'Light', - 'dark' => 'Dark' - ] - ]), - 'locked' => 1, - 'readonly' => 0, - 'sort' => 14 - ], - [ - 'collection' => 'directus_users', - 'field' => '2fa_secret', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => '2fa-secret', - 'locked' => 1, - 'readonly' => 1, - 'sort' => 15 - ], - [ - 'collection' => 'directus_users', - 'field' => 'locale_options', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, - 'interface' => 'json', - 'locked' => 1, - 'hidden_browse' => 1, - 'hidden_detail' => 1, - 'sort' => 16 - ], - [ - 'collection' => 'directus_users', - 'field' => 'token', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1, - 'hidden_detail' => 1, - 'hidden_browse' => 1, - 'sort' => 17 - ], - [ - 'collection' => 'directus_users', - 'field' => 'last_access_on', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_DATETIME, - 'interface' => 'datetime', - 'locked' => 1, - 'readonly' => 1, - 'hidden_detail' => 1, - 'sort' => 18 - ], - [ - 'collection' => 'directus_users', - 'field' => 'last_page', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1, - 'readonly' => 1, - 'hidden_detail' => 1, - 'hidden_browse' => 1, - 'sort' => 19 - ], - [ - 'collection' => 'directus_users', - 'field' => 'external_id', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'locked' => 1, - 'readonly' => 1, - 'hidden_detail' => 20 - ], - - // User Session - // ----------------------------------------------------------------- - [ - 'collection' => 'directus_user_sessions', - 'field' => 'id', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'primary-key', - 'locked' => 1, - 'required' => 1, - 'hidden_detail' => 1 - ], - [ - 'collection' => 'directus_user_sessions', - 'field' => 'user', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_USER, - 'required' => 1, - 'interface' => 'user' - ], - [ - 'collection' => 'directus_user_sessions', - 'field' => 'token_type', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input' - ], - [ - 'collection' => 'directus_user_sessions', - 'field' => 'token', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input' - ], - [ - 'collection' => 'directus_user_sessions', - 'field' => 'ip_address', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input' - ], - [ - 'collection' => 'directus_user_sessions', - 'field' => 'user_agent', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input' - ], - [ - 'collection' => 'directus_user_sessions', - 'field' => 'created_on', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_DATETIME, - 'interface' => 'datetime' - ], - [ - 'collection' => 'directus_user_sessions', - 'field' => 'token_expired_at', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_DATETIME, - 'interface' => 'datetime' - ], - - // Webhooks - // ----------------------------------------------------------------- - [ - 'collection' => 'directus_webhooks', - 'field' => 'id', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, - 'interface' => 'primary-key', - 'locked' => 1, - 'required' => 1, - 'hidden_detail' => 1 - ], - [ - 'collection' => 'directus_webhooks', - 'field' => 'status', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STATUS, - 'interface' => 'status', - 'options' => json_encode([ - 'status_mapping' => [ - 'active' => [ - 'name' => 'Active', - 'value' => 'active', - 'text_color' => 'white', - 'background_color' => 'green', - 'browse_subdued' => false, - 'browse_badge' => true, - 'soft_delete' => false, - 'published' => true, - ], - 'inactive' => [ - 'name' => 'Inactive', - 'value' => 'inactive', - 'text_color' => 'white', - 'background_color' => 'blue-grey', - 'browse_subdued' => true, - 'browse_badge' => true, - 'soft_delete' => false, - 'published' => false, - ] - ] - ]), - 'locked' => 1, - 'width' => 'full', - 'sort' => 1 - ], - [ - 'collection' => 'directus_webhooks', - 'field' => 'http_action', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'dropdown', - 'required' => 1, - 'options' => json_encode([ - 'choices' => [ - 'get' => 'GET', - 'post' => 'POST' - ] - ]), - 'locked' => 1, - 'width' => 'half-space', - 'sort' => 2 - ], - [ - 'collection' => 'directus_webhooks', - 'field' => 'url', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'options' => json_encode([ - 'placeholder' => 'https://example.com', - 'iconRight' => 'link' - ]), - 'required' => 1, - 'locked' => 1, - 'width' => 'full', - 'note' => '', - 'sort' => 3 - ], - [ - 'collection' => 'directus_webhooks', - 'field' => 'collection', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'collections', - 'required' => 1, - 'locked' => 1, - 'width' => 'half', - 'note' => '', - 'sort' => 4 - ], - [ - 'collection' => 'directus_webhooks', - 'field' => 'directus_action', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'dropdown', - 'required' => 1, - 'options' => json_encode([ - 'choices' => [ - 'item.create:after' => 'Create', - 'item.update:after' => 'Update', - 'item.delete:after' => 'Delete', - ] - ]), - 'locked' => 1, - 'width' => 'half', - 'note' => '', - 'sort' => 5 - ], - [ - 'collection' => 'directus_webhooks', - 'field' => 'info', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_ALIAS, - 'interface' => 'divider', - 'options' => json_encode([ - 'style' => 'medium', - 'title' => 'How Webhooks Work', - 'hr' => true, - 'margin' => false, - 'description' => 'When the selected action occurs for the selected collection, Directus will send an HTTP request to the above URL.' - ]), - 'locked' => 1, - 'width' => 'full', - 'hidden_browse' => 1, - 'sort' => 6 - ], - ]; - - $files = $this->table('directus_fields'); - $files->insert($data)->save(); - } -} diff --git a/migrations/db/seeds/RelationsSeeder.php b/migrations/db/seeds/RelationsSeeder.php deleted file mode 100644 index 41ae9188fd..0000000000 --- a/migrations/db/seeds/RelationsSeeder.php +++ /dev/null @@ -1,80 +0,0 @@ - 'directus_activity', - 'field_many' => 'action_by', - 'collection_one' => 'directus_users' - ], - [ - 'collection_many' => 'directus_collections_presets', - 'field_many' => 'user', - 'collection_one' => 'directus_users' - ], - [ - 'collection_many' => 'directus_collections_presets', - 'field_many' => 'group', - 'collection_one' => 'directus_groups' - ], - [ - 'collection_many' => 'directus_fields', - 'field_many' => 'collection', - 'collection_one' => 'directus_collections', - 'field_one' => 'fields' - ], - [ - 'collection_many' => 'directus_files', - 'field_many' => 'uploaded_by', - 'collection_one' => 'directus_users' - ], - [ - 'collection_many' => 'directus_files', - 'field_many' => 'folder', - 'collection_one' => 'directus_folders' - ], - [ - 'collection_many' => 'directus_folders', - 'field_many' => 'parent_folder', - 'collection_one' => 'directus_folders' - ], - [ - 'collection_many' => 'directus_permissions', - 'field_many' => 'group', - 'collection_one' => 'directus_groups' - ], - [ - 'collection_many' => 'directus_revisions', - 'field_many' => 'activity', - 'collection_one' => 'directus_activity' - ], - [ - 'collection_many' => 'directus_users', - 'field_many' => 'role', - 'collection_one' => 'directus_roles', - 'field_one' => 'users' - ], - [ - 'collection_many' => 'directus_users', - 'field_many' => 'avatar', - 'collection_one' => 'directus_files' - ] - ]; - - $files = $this->table('directus_relations'); - $files->insert($data)->save(); - } -} diff --git a/migrations/db/seeds/RolesSeeder.php b/migrations/db/seeds/RolesSeeder.php deleted file mode 100644 index b245c76522..0000000000 --- a/migrations/db/seeds/RolesSeeder.php +++ /dev/null @@ -1,33 +0,0 @@ - 1, - 'name' => 'Administrator', - 'description' => 'Admins have access to all managed data within the system by default' - ], - [ - 'id' => 2, - 'name' => 'Public', - 'description' => 'Controls what API data is publicly available without authenticating' - ] - ]; - - $groups = $this->table('directus_roles'); - $groups->insert($data)->save(); - } -} diff --git a/migrations/db/seeds/SettingsSeeder.php b/migrations/db/seeds/SettingsSeeder.php deleted file mode 100644 index 86b1920296..0000000000 --- a/migrations/db/seeds/SettingsSeeder.php +++ /dev/null @@ -1,111 +0,0 @@ - 'project_url', - 'value' => '' - ], - [ - 'key' => 'project_logo', - 'value' => '' - ], - [ - 'key' => 'project_color', - 'value' => 'blue-grey-900', - ], - [ - 'key' => 'project_foreground', - 'value' => '', - ], - [ - 'key' => 'project_background', - 'value' => '', - ], - [ - 'key' => 'default_locale', - 'value' => 'en-US', - ], - [ - 'key' => 'telemetry', - 'value' => '1', - ], - [ - 'key' => 'default_limit', - 'value' => '200' - ], - [ - 'key' => 'sort_null_last', - 'value' => '1' - ], - [ - 'key' => 'password_policy', - 'value' => '' - ], - [ - 'key' => 'auto_sign_out', - 'value' => '10080' - ], - [ - 'key' => 'login_attempts_allowed', - 'value' => '10' - ], - [ - 'key' => 'trusted_proxies', - 'value' => '' - ], - [ - 'key' => 'file_naming', - 'value' => 'uuid' - ], - [ - 'key' => 'file_max_size', - 'value' => '100MB' - ], - [ - 'key' => 'file_mimetype_whitelist', - 'value' => '' - ], - [ - 'key' => 'thumbnail_dimensions', - 'value' => '200x200' - ], - [ - 'key' => 'thumbnail_quality_tags', - 'value' => '{"poor": 25, "good": 50, "better": 75, "best": 100}' - ], - [ - 'key' => 'thumbnail_actions', - 'value' => '{"contain":{"options":{"resizeCanvas":false,"position":"center","resizeRelative":false,"canvasBackground":"ccc"}},"crop":{"options":{"position":"center"}}}' - ], - [ - 'key' => 'thumbnail_not_found_location', - 'value' => '' - ], - [ - 'key' => 'thumbnail_cache_ttl', - 'value' => '86400' - ], - [ - 'key' => 'youtube_api_key', - 'value' => '' - ] - ]; - - $groups = $this->table('directus_settings'); - $groups->insert($data)->save(); - } -} diff --git a/migrations/install/20180220023123_create_fields_table.php b/migrations/install/20180220023123_create_fields_table.php new file mode 100644 index 0000000000..536227d22d --- /dev/null +++ b/migrations/install/20180220023123_create_fields_table.php @@ -0,0 +1,252 @@ +table('directus_fields', ['signed' => false]); + + $table->addColumn('collection', 'string', [ + 'limit' => 64, + 'null' => false + ]); + $table->addColumn('field', 'string', [ + 'limit' => 64, + 'null' => false + ]); + $table->addColumn('type', 'string', [ + 'limit' => 64, + 'null' => false + ]); + $table->addColumn('interface', 'string', [ + 'limit' => 64, + 'null' => true, + 'default' => null, + ]); + $table->addColumn('options', 'text', [ + 'null' => true, + 'default' => null + ]); + $table->addColumn('locked', 'boolean', [ + 'signed' => false, + 'null' => false, + 'default' => false + ]); + $table->addColumn('validation', 'string', [ + 'limit' => 255, + 'null' => true, + 'default' => null + ]); + $table->addColumn('required', 'boolean', [ + 'signed' => false, + 'null' => false, + 'default' => false + ]); + $table->addColumn('readonly', 'boolean', [ + 'signed' => false, + 'null' => false, + 'default' => false + ]); + $table->addColumn('hidden_detail', 'boolean', [ + 'signed' => false, + 'null' => false, + 'default' => 0 + ]); + $table->addColumn('hidden_browse', 'boolean', [ + 'signed' => false, + 'null' => false, + 'default' => 0 + ]); + $table->addColumn('sort', 'integer', [ + 'signed' => false, + 'null' => true, + 'default' => null + ]); + $table->addColumn('width', 'string', [ + 'limit' => 50, + 'null' => true, + 'default' => 'full' + ]); + $table->addColumn('group', 'integer', [ + 'signed' => false, + 'null' => true, + 'default' => null + ]); + $table->addColumn('note', 'string', [ + 'limit' => 1024, + 'null' => true, + 'default' => null + ]); + $table->addColumn('translation', 'text', [ + 'null' => true, + 'default' => null + ]); + + $table->addIndex(['collection', 'field'], [ + 'unique' => true, + 'name' => 'idx_collection_field' + ]); + + $table->create(); + + $data = [ + [ + 'collection' => 'directus_fields', + 'field' => 'id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'primary-key', + 'locked' => 1, + 'required' => 1, + 'hidden_detail' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'collection', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, + 'interface' => 'many-to-one', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'field', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'type', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'primary-key', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'interface', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'primary-key', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'options', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'json', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'locked', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'translation', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'repeater', + 'locked' => 1, + 'options' => '{ + "fields": [ + { + "field": "locale", + "type": "string", + "interface": "language", + "options": { + "limit": true + }, + "width": "half" + }, + { + "field": "translation", + "type": "string", + "interface": "text-input", + "width": "half" + } + ] + }' + ], + [ + 'collection' => 'directus_fields', + 'field' => 'readonly', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'validation', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + ], + [ + 'collection' => 'directus_fields', + 'field' => 'required', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'sort', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_SORT, + 'interface' => 'sort', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'note', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'hidden_detail', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'hidden_browse', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'width', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'numeric', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'group', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, + 'interface' => 'many-to-one', + 'locked' => 1 + ], + ]; + + foreach($data as $value){ + if(!$this->checkFieldExist($value['collection'], $value['field'])){ + $fileds = $this->table('directus_fields'); + $fileds->insert($value)->save(); + } + } + } + + public function checkFieldExist($collection,$field){ + $checkSql = sprintf('SELECT 1 FROM `directus_fields` WHERE `collection` = "%s" AND `field` = "%s";', $collection, $field); + return $this->query($checkSql)->fetch(); + } +} diff --git a/migrations/install/20180220023138_create_activity_table.php b/migrations/install/20180220023138_create_activity_table.php new file mode 100644 index 0000000000..d1ed6aa8c9 --- /dev/null +++ b/migrations/install/20180220023138_create_activity_table.php @@ -0,0 +1,220 @@ +table('directus_activity', ['signed' => false]); + + $table->addColumn('action', 'string', [ + 'limit' => 45, + 'null' => false + ]); + + $table->addColumn('action_by', 'integer', [ + 'signed' => false, + 'null' => false, + 'default' => 0 + ]); + + $table->addColumn('action_on', 'datetime', [ + 'default' => null + ]); + + $table->addColumn('ip', 'string', [ + 'limit' => 50, + 'default' => null + ]); + + $table->addColumn('user_agent', 'string', [ + 'limit' => 255 + ]); + + $table->addColumn('collection', 'string', [ + 'limit' => 64, + 'null' => false + ]); + + $table->addColumn('item', 'string',[ + 'limit' => 255 + ]); + + $table->addColumn('edited_on', 'datetime', [ + 'null' => true, + 'default' => null + ]); + + $table->addColumn('comment', 'text', [ + 'null' => true, + 'encoding' => 'utf8mb4' + ]); + + $table->addColumn('comment_deleted_on', 'datetime', [ + 'null' => true, + 'default' => null + ]); + + $table->create(); + + $data = [ + [ + 'collection' => 'directus_activity', + 'field' => 'id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'primary-key', + 'locked' => 1, + 'readonly' => 1, + 'required' => 1, + 'hidden_detail' => 1 + ], + [ + 'collection' => 'directus_activity', + 'field' => 'action', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'change_history' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 1, + 'width' => 'full' + ], + [ + 'collection' => 'directus_activity', + 'field' => 'collection', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'collections', + 'options' => json_encode([ + 'iconRight' => 'list_alt', + 'include_system' => true + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 2, + 'width' => 'half' + ], + [ + 'collection' => 'directus_activity', + 'field' => 'item', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'link' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 3, + 'width' => 'half' + ], + [ + 'collection' => 'directus_activity', + 'field' => 'action_by', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'user', + 'options' => json_encode([ + 'iconRight' => 'account_circle' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 4, + 'width' => 'half' + ], + [ + 'collection' => 'directus_activity', + 'field' => 'action_on', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_DATETIME, + 'interface' => 'datetime', + 'options' => json_encode([ + 'showRelative' => true, + 'iconRight' => 'calendar_today' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 5, + 'width' => 'half' + ], + [ + 'collection' => 'directus_activity', + 'field' => 'edited_on', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_DATETIME, + 'interface' => 'datetime', + 'options' => json_encode([ + 'showRelative' => true, + 'iconRight' => 'edit' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 6, + 'width' => 'half' + ], + [ + 'collection' => 'directus_activity', + 'field' => 'comment_deleted_on', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_DATETIME, + 'interface' => 'datetime', + 'options' => json_encode([ + 'showRelative' => true, + 'iconRight' => 'delete_outline' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 7, + 'width' => 'half' + ], + [ + 'collection' => 'directus_activity', + 'field' => 'ip', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'my_location' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 8, + 'width' => 'half' + ], + [ + 'collection' => 'directus_activity', + 'field' => 'user_agent', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'devices_other' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 9, + 'width' => 'half' + ], + [ + 'collection' => 'directus_activity', + 'field' => 'comment', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'textarea', + 'locked' => 1, + 'readonly' => 1, + 'sort' => 10, + 'width' => 'full' + ], + ]; + + foreach($data as $value){ + if(!$this->checkFieldExist($value['collection'], $value['field'])){ + $fileds = $this->table('directus_fields'); + $fileds->insert($value)->save(); + } + } + } + + public function checkFieldExist($collection,$field){ + $checkSql = sprintf('SELECT 1 FROM `directus_fields` WHERE `collection` = "%s" AND `field` = "%s";', $collection, $field); + return $this->query($checkSql)->fetch(); + } +} diff --git a/migrations/install/20180220023152_create_collections_presets_table.php b/migrations/install/20180220023152_create_collections_presets_table.php new file mode 100644 index 0000000000..19860a4b00 --- /dev/null +++ b/migrations/install/20180220023152_create_collections_presets_table.php @@ -0,0 +1,218 @@ +table('directus_collection_presets', ['signed' => false]); + + $table->addColumn('title', 'string', [ + 'limit' => 255, + 'null' => true, + 'default' => null, + 'encoding' => 'utf8mb4' + ]); + $table->addColumn('user', 'integer', [ + 'signed' => false, + 'null' => true + ]); + $table->addColumn('role', 'integer', [ + 'signed' => false, + 'null' => true + ]); + $table->addColumn('collection', 'string', [ + 'limit' => 64, + 'null' => false + ]); + $table->addColumn('search_query', 'string', [ + 'limit' => 100, + 'null' => true, + 'default' => null + ]); + $table->addColumn('filters', 'text', [ + 'null' => true, + 'default' => null + ]); + $table->addColumn('view_type', 'string', [ + 'limit' => 100, + 'null' => false, + 'default' => 'tabular' + ]); + $table->addColumn('view_query', 'text', [ + 'null' => true, + 'default' => null + ]); + $table->addColumn('view_options', 'text', [ + 'null' => true, + 'default' => null + ]); + $table->addColumn('translation', 'text', [ + 'null' => true, + 'default' => null + ]); + $table->addIndex(['user', 'collection', 'title'], [ + 'unique' => true, + 'name' => 'idx_user_collection_title' + ]); + + $table->create(); + + $data = [ + [ + 'collection' => 'directus_collection_presets', + 'field' => 'id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'primary-key', + 'locked' => 1 + ], + [ + 'collection' => 'directus_collection_presets', + 'field' => 'title', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_collection_presets', + 'field' => 'user', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'user', + 'locked' => 1 + ], + [ + 'collection' => 'directus_collection_presets', + 'field' => 'role', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, + 'interface' => 'many-to-one', + 'locked' => 1 + ], + [ + 'collection' => 'directus_collection_presets', + 'field' => 'collection', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, + 'interface' => 'many-to-one', + 'locked' => 1 + ], + [ + 'collection' => 'directus_collection_presets', + 'field' => 'search_query', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_collection_presets', + 'field' => 'filters', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'json', + 'locked' => 1 + ], + [ + 'collection' => 'directus_collection_presets', + 'field' => 'view_options', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'json', + 'locked' => 1 + ], + [ + 'collection' => 'directus_collection_presets', + 'field' => 'view_type', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_collection_presets', + 'field' => 'view_query', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'json', + 'locked' => 1 + ], + ]; + + foreach($data as $value){ + if(!$this->checkFieldExist($value['collection'], $value['field'])){ + $fileds = $this->table('directus_fields'); + $fileds->insert($value)->save(); + } + } + + $result = [ + [ + 'collection' => 'directus_activity', + 'view_type' => 'timeline', + 'view_query' => json_encode([ + 'timeline' => [ + 'sort' => '-action_on' + ] + ]), + 'view_options' => json_encode([ + 'timeline' => [ + 'date' => 'action_on', + 'title' => '{{ action }} by {{ action_by.first_name }} {{ action_by.last_name }} (#{{ item }})', + 'content' => 'collection', + 'color' => 'action' + ] + ]) + ], + [ + 'collection' => 'directus_files', + 'view_type' => 'cards', + 'view_options' => json_encode([ + 'cards' => [ + 'title' => 'title', + 'subtitle' => 'type', + 'content' => 'description', + 'src' => 'data' + ] + ]) + ], + [ + 'collection' => 'directus_users', + 'view_type' => 'cards', + 'view_options' => json_encode([ + 'cards' => [ + 'title' => 'first_name', + 'subtitle' => 'last_name', + 'content' => 'title', + 'src' => 'avatar', + 'icon' => 'person' + ] + ]) + ], + [ + 'collection' => 'directus_webhooks', + 'view_type' => 'tabular', + 'view_query' => json_encode([ + 'tabular' => [ + 'fields' => 'status,http_action,url,collection,directus_action' + ] + ]), + 'view_options' => json_encode([ + 'tabular' => [ + 'widths' => [ + 'status' => 32, + 'http_action' => 72, + 'url' => 200, + 'collection' => 200, + 'directus_action' => 200 + ] + ] + ]) + ] + ]; + + $files = $this->table('directus_collection_presets'); + $files->insert($result)->save(); + } + + public function checkFieldExist($collection,$field){ + $checkSql = sprintf('SELECT 1 FROM `directus_fields` WHERE `collection` = "%s" AND `field` = "%s";', $collection, $field); + return $this->query($checkSql)->fetch(); + } +} diff --git a/migrations/install/20180220023157_create_collections_table.php b/migrations/install/20180220023157_create_collections_table.php new file mode 100644 index 0000000000..c9fdee5698 --- /dev/null +++ b/migrations/install/20180220023157_create_collections_table.php @@ -0,0 +1,167 @@ +table('directus_collections', [ + 'id' => false, + 'primary_key' => 'collection' + ]); + + $table->addColumn('collection', 'string', [ + 'limit' => 64, + 'null' => false + ]); + $table->addColumn('managed', 'boolean', [ + 'signed' => false, + 'null' => false, + 'default' => true + ]); + $table->addColumn('hidden', 'boolean', [ + 'signed' => false, + 'null' => false, + 'default' => false + ]); + $table->addColumn('single', 'boolean', [ + 'signed' => false, + 'null' => false, + 'default' => false + ]); + $table->addColumn('icon', 'string', [ + 'limit' => 30, + 'null' => true, + 'default' => null + ]); + $table->addColumn('note', 'string', [ + 'limit' => 255, + 'null' => true, + 'default' => null + ]); + $table->addColumn('translation', 'text', [ + 'null' => true, + 'default' => null + ]); + + $table->create(); + $data = [ + [ + 'collection' => 'directus_collections', + 'field' => 'fields', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_O2M, + 'interface' => 'one-to-many', + 'locked' => 1, + 'hidden_detail' => 1, + 'hidden_browse' => 1, + 'sort' => 1 + ], + [ + 'collection' => 'directus_collections', + 'field' => 'collection', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'primary-key', + 'locked' => 1, + 'readonly' => 1, + 'required' => 1, + 'sort' => 2, + 'width' => 'half' + ], + [ + 'collection' => 'directus_collections', + 'field' => 'note', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'sort' => 3, + 'width' => 'half', + 'note' => 'An internal description.' + ], + [ + 'collection' => 'directus_collections', + 'field' => 'managed', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1, + 'sort' => 4, + 'width' => 'half', + 'hidden_detail' => 1, + 'note' => '[Learn More](https://docs.directus.io/guides/collections.html#managing-collections).' + ], + [ + 'collection' => 'directus_collections', + 'field' => 'hidden', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1, + 'sort' => 5, + 'width' => 'half', + 'note' => '[Learn More](https://docs.directus.io/guides/collections.html#hidden).' + ], + [ + 'collection' => 'directus_collections', + 'field' => 'single', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1, + 'sort' => 6, + 'width' => 'half', + 'note' => '[Learn More](https://docs.directus.io/guides/collections.html#single).' + ], + [ + 'collection' => 'directus_collections', + 'field' => 'translation', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'repeater', + 'locked' => 1, + 'sort' => 7, + 'hidden_detail' => 0, + 'options' => '{ + "fields": [ + { + "field": "locale", + "type": "string", + "interface": "language", + "options": { + "limit": true + }, + "width": "half" + }, + { + "field": "translation", + "type": "string", + "interface": "text-input", + "width": "half" + } + ] + }' + ], + [ + 'collection' => 'directus_collections', + 'field' => 'icon', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'icon', + 'locked' => 1, + 'sort' => 8, + 'note' => 'The icon shown in the App\'s navigation sidebar.' + ], + + ]; + + foreach($data as $value){ + if(!$this->checkFieldExist($value['collection'], $value['field'])){ + $fileds = $this->table('directus_fields'); + $fileds->insert($value)->save(); + } + } + } + + public function checkFieldExist($collection,$field){ + $checkSql = sprintf('SELECT 1 FROM `directus_fields` WHERE `collection` = "%s" AND `field` = "%s";', $collection, $field); + return $this->query($checkSql)->fetch(); + } +} diff --git a/migrations/install/20180220023208_create_files_table.php b/migrations/install/20180220023208_create_files_table.php new file mode 100644 index 0000000000..a536c227f2 --- /dev/null +++ b/migrations/install/20180220023208_create_files_table.php @@ -0,0 +1,399 @@ +table('directus_files', ['signed' => false]); + + $table->addColumn('storage', 'string', [ + 'limit' => 50, + 'null' => false, + 'default' => 'local' + ]); + $table->addColumn('private_hash', 'string', [ + 'limit' => 16, + 'null' => true, + 'default' => null + ]); + $table->addColumn('filename_disk', 'string', [ + 'limit' => 255, + 'null' => false + ]); + $table->addColumn('filename_download', 'string', [ + 'limit' => 255, + 'null' => false + ]); + $table->addColumn('title', 'string', [ + 'limit' => 255, + 'null' => true, + 'default' => null + ]); + $table->addColumn('type', 'string', [ + 'limit' => 255, + 'null' => true, + 'default' => null // unknown type? + ]); + $table->addColumn('uploaded_by', 'integer', [ + 'signed' => false, + 'null' => false + ]); + // TODO: Make directus set this value to whatever default is on the server (UTC) + // In MySQL 5.5 and below doesn't support CURRENT TIMESTAMP on datetime as default + $table->addColumn('uploaded_on', 'datetime', [ + 'null' => false + ]); + $table->addColumn('charset', 'string', [ + 'limit' => 50, + 'null' => true, + 'default' => null + ]); + $table->addColumn('filesize', 'integer', [ + 'signed' => false, + 'default' => 0 + ]); + $table->addColumn('width', 'integer', [ + 'signed' => false, + 'null' => true, + 'default' => null + ]); + $table->addColumn('height', 'integer', [ + 'signed' => false, + 'null' => true, + 'default' => null + ]); + $table->addColumn('duration', 'integer', [ + 'signed' => true, + 'null' => true, + 'default' => null + ]); + $table->addColumn('embed', 'string', [ + 'limit' => 200, + 'null' => true, + 'default' => NULL + ]); + $table->addColumn('folder', 'integer', [ + 'signed' => false, + 'null' => true, + 'default' => null + ]); + $table->addColumn('description', 'text', [ + 'null' => true, + 'default' => null + ]); + $table->addColumn('location', 'string', [ + 'limit' => 200, + 'null' => true, + 'default' => null + ]); + $table->addColumn('tags', 'string', [ + 'limit' => 255, + 'null' => true, + 'default' => null + ]); + $table->addColumn('checksum', 'string', [ + 'limit' => 32, + 'null' => true, + 'default' => null + ]); + $table->addColumn('metadata', 'text', [ + 'null' => true, + 'default' => null + ]); + + $table->create(); + + $data = [ + [ + 'collection' => 'directus_files', + 'field' => 'preview', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ALIAS, + 'interface' => 'file-preview', + 'locked' => 1, + 'sort' => 1, + 'width' => 'full' + ], + [ + 'collection' => 'directus_files', + 'field' => 'title', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'placeholder' => 'Enter a descriptive title...', + 'iconRight' => 'title' + ]), + 'locked' => 1, + 'sort' => 2, + 'width' => 'full' + ], + [ + 'collection' => 'directus_files', + 'field' => 'tags', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ARRAY, + 'interface' => 'tags', + 'options' => json_encode([ + 'placeholder' => 'Enter a keyword then hit enter...' + ]), + 'sort' => 3, + 'width' => 'half', + 'locked' => 1 + ], + [ + 'collection' => 'directus_files', + 'field' => 'location', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'placeholder' => 'Enter a location...', + 'iconRight' => 'place' + ]), + 'sort' => 4, + 'width' => 'half', + 'locked' => 1 + ], + [ + 'collection' => 'directus_files', + 'field' => 'description', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'wysiwyg', + 'options' => json_encode([ + 'toolbar' => ['bold','italic','underline','link','code'] + ]), + 'sort' => 5, + 'width' => 'full', + 'locked' => 1 + ], + [ + 'collection' => 'directus_files', + 'field' => 'filename_download', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'options' => json_encode([ + 'monospace' => true, + 'iconRight' => 'get_app' + ]), + 'sort' => 6, + 'width' => 'full' + ], + [ + 'collection' => 'directus_files', + 'field' => 'filename_disk', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'placeholder' => 'Enter a unique file name...', + 'iconRight' => 'insert_drive_file' + ]), + 'locked' => 1, + 'sort' => 7, + 'width' => 'full' + ], + [ + 'collection' => 'directus_files', + 'field' => 'private_hash', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface'=> 'slug', + 'width' => 'half', + 'locked' => 1, + 'sort' => 8, + 'options' => json_encode([ + 'iconRight' => 'lock' + ]) + ], + [ + 'collection' => 'directus_files', + 'field' => 'checksum', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'readonly' => 1, + 'sort' => 9, + 'width' => 'half', + 'options' => json_encode([ + 'iconRight' => 'check', + 'monospace' => true + ]) + ], + [ + 'collection' => 'directus_files', + 'field' => 'uploaded_on', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_DATETIME, + 'interface' => 'datetime', + 'options' => json_encode([ + 'iconRight' => 'today' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 10, + 'width' => 'half', + 'required' => 1 + ], + [ + 'collection' => 'directus_files', + 'field' => 'uploaded_by', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_USER_CREATED, + 'interface' => 'user-created', + 'locked' => 1, + 'readonly' => 1, + 'sort' => 11, + 'width' => 'half', + 'required' => 1 + ], + [ + 'collection' => 'directus_files', + 'field' => 'width', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'numeric', + 'options' => json_encode([ + 'iconRight' => 'straighten' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 12, + 'width' => 'half' + ], + [ + 'collection' => 'directus_files', + 'field' => 'height', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'numeric', + 'options' => json_encode([ + 'iconRight' => 'straighten' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 13, + 'width' => 'half' + ], + [ + 'collection' => 'directus_files', + 'field' => 'duration', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'numeric', + 'options' => json_encode([ + 'iconRight' => 'timer' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 14, + 'width' => 'half' + ], + [ + 'collection' => 'directus_files', + 'field' => 'filesize', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'file-size', + 'options' => json_encode([ + 'iconRight' => 'storage' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 15, + 'width' => 'half' + ], + [ + 'collection' => 'directus_files', + 'field' => 'metadata', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'key-value', + 'locked' => 1, + 'sort' => 15, + 'width' => 'full', + 'options' => json_encode([ + 'keyInterface' => 'text-input', + 'keyDataType' => 'string', + 'keyOptions' => [ + 'monospace' => true, + 'placeholder' => 'Key' + ], + 'valueInterface' => 'text-input', + 'valueDataType' => 'string', + 'valueOptions' => [ + 'monospace' => true, + 'placeholder' => 'Value' + ] + ]) + ], + [ + 'collection' => 'directus_files', + 'field' => 'data', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ALIAS, + 'interface' => 'file', + 'locked' => 1, + 'hidden_detail' => 1 + ], + [ + 'collection' => 'directus_files', + 'field' => 'id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'primary-key', + 'locked' => 1, + 'required' => 1, + 'hidden_detail' => 1 + ], + [ + 'collection' => 'directus_files', + 'field' => 'type', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'readonly' => 1, + 'hidden_detail' => 1 + ], + [ + 'collection' => 'directus_files', + 'field' => 'charset', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'readonly' => 1, + 'hidden_detail' => 1, + 'hidden_browse' => 1 + ], + [ + 'collection' => 'directus_files', + 'field' => 'embed', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'readonly' => 1, + 'hidden_detail' => 1 + ], + [ + 'collection' => 'directus_files', + 'field' => 'folder', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, + 'interface' => 'many-to-one', + 'locked' => 1, + 'hidden_detail' => 1 + ], + [ + 'collection' => 'directus_files', + 'field' => 'storage', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'hidden_detail' => 1, + 'hidden_browse' => 1 + ] + ]; + + foreach($data as $value){ + if(!$this->checkFieldExist($value['collection'], $value['field'])){ + $fileds = $this->table('directus_fields'); + $fileds->insert($value)->save(); + } + } + } + + public function checkFieldExist($collection,$field){ + $checkSql = sprintf('SELECT 1 FROM `directus_fields` WHERE `collection` = "%s" AND `field` = "%s";', $collection, $field); + return $this->query($checkSql)->fetch(); + } +} diff --git a/migrations/install/20180220023213_create_folders_table.php b/migrations/install/20180220023213_create_folders_table.php new file mode 100644 index 0000000000..4c88e3baab --- /dev/null +++ b/migrations/install/20180220023213_create_folders_table.php @@ -0,0 +1,69 @@ +table('directus_folders', ['signed' => false]); + + $table->addColumn('name', 'string', [ + 'limit' => 191, + 'null' => false, + 'encoding' => 'utf8mb4' + ]); + $table->addColumn('parent_folder', 'integer', [ + 'signed' => false, + 'null' => true, + 'default' => null + ]); + + $table->addIndex(['name', 'parent_folder'], [ + 'unique' => true, + 'name' => 'idx_name_parent_folder' + ]); + + $table->create(); + $data = [ + [ + 'collection' => 'directus_folders', + 'field' => 'id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'primary-key', + 'locked' => 1, + 'required' => 1, + 'hidden_detail' => 1 + ], + [ + 'collection' => 'directus_folders', + 'field' => 'name', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_folders', + 'field' => 'parent_folder', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, + 'interface' => 'many-to-one', + 'locked' => 1 + ], + ]; + + foreach($data as $value){ + if(!$this->checkFieldExist($value['collection'], $value['field'])){ + $fileds = $this->table('directus_fields'); + $fileds->insert($value)->save(); + } + } + } + + public function checkFieldExist($collection,$field){ + $checkSql = sprintf('SELECT 1 FROM `directus_fields` WHERE `collection` = "%s" AND `field` = "%s";', $collection, $field); + return $this->query($checkSql)->fetch(); + } +} diff --git a/migrations/install/20180220023217_create_roles_table.php b/migrations/install/20180220023217_create_roles_table.php new file mode 100644 index 0000000000..bdd6041ab7 --- /dev/null +++ b/migrations/install/20180220023217_create_roles_table.php @@ -0,0 +1,170 @@ +table('directus_roles', ['signed' => false]); + + $table->addColumn('name', 'string', [ + 'limit' => 100, + 'null' => false + ]); + $table->addColumn('description', 'string', [ + 'limit' => 500, + 'null' => true, + 'default' => NULL + ]); + $table->addColumn('ip_whitelist', 'text', [ + 'null' => true, + 'default' => null + ]); + $table->addColumn('external_id', 'string', [ + 'limit' => 255, + 'null' => true, + 'default' => null + ]); + $table->addColumn('module_listing', 'text', [ + 'null' => true, + 'default' => null + ]); + $table->addColumn('collection_listing', 'text', [ + 'null' => true, + 'default' => null + ]); + $table->addColumn('enforce_2fa', 'boolean', [ + 'null' => true, + 'default' => false + ]); + + $table->addIndex('name', [ + 'unique' => true, + 'name' => 'idx_group_name' + ]); + + $table->addIndex('external_id', [ + 'unique' => true, + 'name' => 'idx_roles_external_id' + ]); + + $table->create(); + $data = [ + [ + 'collection' => 'directus_roles', + 'field' => 'id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'primary-key', + 'locked' => 1, + 'required' => 1, + 'hidden_detail' => 1 + ], + [ + 'collection' => 'directus_roles', + 'field' => 'external_id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'readonly' => 1, + 'hidden_detail' => 1, + 'hidden_browse' => 1 + ], + [ + 'collection' => 'directus_roles', + 'field' => 'name', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'sort' => 1, + 'width' => 'half', + 'required' => 1 + ], + [ + 'collection' => 'directus_roles', + 'field' => 'description', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'sort' => 2, + 'width' => 'half' + ], + [ + 'collection' => 'directus_roles', + 'field' => 'ip_whitelist', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ARRAY, + 'interface' => 'tags', + 'options' => json_encode([ + '' => 'Add an IP address...' + ]), + 'locked' => 1 + ], + [ + 'collection' => 'directus_roles', + 'field' => 'enforce_2fa', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1 + ], + [ + 'collection' => 'directus_roles', + 'field' => 'users', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_O2M, + 'interface' => 'one-to-many', + 'locked' => 1, + 'options' => json_encode([ + 'fields' => "first_name,last_name" + ]) + ], + [ + 'collection' => 'directus_roles', + 'field' => 'module_listing', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'repeater', + 'locked' => 1, + 'options' => '{"template":"{{ name }}","createItemText":"Add Module","fields":[{"field":"name","interface":"text-input","type":"string","width":"half"},{"field":"link","interface":"text-input","type":"string","width":"half"},{"field":"icon","interface":"icon","type":"string","width":"full"}]}' + ], + [ + 'collection' => 'directus_roles', + 'field' => 'collection_listing', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'repeater', + 'locked' => 1, + 'options' => '{"template":"{{ group_name }}","createItemText":"Add Group","fields":[{"field":"group_name","width":"full","interface":"text-input","type":"string"},{"field":"collections","interface":"repeater","type":"JSON","options":{"createItemText":"Add Collection","fields":[{"field":"collection","type":"string","interface":"collections","width":"full"}]}}]}' + ], + ]; + + + + foreach($data as $value){ + if(!$this->checkFieldExist($value['collection'], $value['field'])){ + $fileds = $this->table('directus_fields'); + $fileds->insert($value)->save(); + } + } + + $rolesData = [ + [ + 'id' => 1, + 'name' => 'Administrator', + 'description' => 'Admins have access to all managed data within the system by default' + ], + [ + 'id' => 2, + 'name' => 'Public', + 'description' => 'Controls what API data is publicly available without authenticating' + ] + ]; + + $groups = $this->table('directus_roles'); + $groups->insert($rolesData)->save(); + } + + public function checkFieldExist($collection,$field){ + $checkSql = sprintf('SELECT 1 FROM `directus_fields` WHERE `collection` = "%s" AND `field` = "%s";', $collection, $field); + return $this->query($checkSql)->fetch(); + } +} diff --git a/migrations/install/20180220023226_create_permissions_table.php b/migrations/install/20180220023226_create_permissions_table.php new file mode 100644 index 0000000000..86c5b01dcf --- /dev/null +++ b/migrations/install/20180220023226_create_permissions_table.php @@ -0,0 +1,189 @@ +table('directus_permissions', ['signed' => false]); + + $table->addColumn('collection', 'string', [ + 'limit' => 64, + 'null' => false, + ]); + $table->addColumn('role', 'integer', [ + 'signed' => false, + 'null' => false + ]); + $table->addColumn('status', 'string', [ + 'length' => 64, + 'default' => null, + 'null' => true + ]); + $table->addColumn('create', 'string', [ + 'signed' => false, + 'null' => true, + 'default' => 'none', + 'length' => 16, + ]); + $table->addColumn('read', 'string', [ + 'signed' => false, + 'null' => true, + 'default' => 'none', + 'length' => 16, + ]); + $table->addColumn('update', 'string', [ + 'signed' => false, + 'null' => true, + 'default' => 'none', + 'length' => 16, + ]); + $table->addColumn('delete', 'string', [ + 'signed' => false, + 'null' => true, + 'default' => 'none', + 'length' => 16, + ]); + $table->addColumn('comment', 'string', [ + 'limit' => 8, + 'null' => true, + 'default' => 'none' + ]); + $table->addColumn('explain', 'string', [ + 'limit' => 8, + 'null' => true, + 'default' => 'none' + ]); + $table->addColumn('read_field_blacklist', 'string', [ + 'limit' => 1000, + 'null' => true, + 'default' => null, + 'encoding' => 'utf8' + ]); + $table->addColumn('write_field_blacklist', 'string', [ + 'limit' => 1000, + 'null' => true, + 'default' => NULL, + 'encoding' => 'utf8', + ]); + $table->addColumn('status_blacklist', 'string', [ + 'length' => 1000, + 'default' => null, + 'null' => true + ]); + + $table->create(); + $data = [ + [ + 'collection' => 'directus_permissions', + 'field' => 'id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'primary-key', + 'locked' => 1, + 'required' => 1, + 'hidden_detail' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'collection', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, + 'interface' => 'many-to-one', + 'locked' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'role', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, + 'interface' => 'many-to-one', + 'locked' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'status', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'create', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'read', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'update', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'delete', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'primary-key', + 'locked' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'comment', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'explain', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'status_blacklist', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ARRAY, + 'interface' => 'tags', + 'locked' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'read_field_blacklist', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ARRAY, + 'interface' => 'tags', + 'locked' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'write_field_blacklist', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ARRAY, + 'interface' => 'tags', + 'locked' => 1 + ], + + ]; + + foreach($data as $value){ + if(!$this->checkFieldExist($value['collection'], $value['field'])){ + $fileds = $this->table('directus_fields'); + $fileds->insert($value)->save(); + } + } + } + + public function checkFieldExist($collection,$field){ + $checkSql = sprintf('SELECT 1 FROM `directus_fields` WHERE `collection` = "%s" AND `field` = "%s";', $collection, $field); + return $this->query($checkSql)->fetch(); + } +} diff --git a/migrations/install/20180220023232_create_relations_table.php b/migrations/install/20180220023232_create_relations_table.php new file mode 100644 index 0000000000..d8c6cb534a --- /dev/null +++ b/migrations/install/20180220023232_create_relations_table.php @@ -0,0 +1,158 @@ +table('directus_relations', ['signed' => false]); + + $table->addColumn('collection_many', 'string', [ + 'limit' => 64, + 'null' => false + ]); + $table->addColumn('field_many', 'string', [ + 'limit' => 45, + 'null' => false + ]); + $table->addColumn('collection_one', 'string', [ + 'limit' => 64, + 'null' => true + ]); + $table->addColumn('field_one', 'string', [ + 'limit' => 64, + 'null' => true + ]); + $table->addColumn('junction_field', 'string', [ + 'limit' => 64, + 'null' => true + ]); + + $table->create(); + + $data = [ + [ + 'collection' => 'directus_relations', + 'field' => 'id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'primary-key', + 'locked' => 1 + ], + [ + 'collection' => 'directus_relations', + 'field' => 'collection_many', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'collections', + 'locked' => 1 + ], + [ + 'collection' => 'directus_relations', + 'field' => 'field_many', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_relations', + 'field' => 'collection_one', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'collections', + 'locked' => 1 + ], + [ + 'collection' => 'directus_relations', + 'field' => 'field_one', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_relations', + 'field' => 'junction_field', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + ]; + + foreach($data as $value){ + if(!$this->checkFieldExist($value['collection'], $value['field'])){ + $fileds = $this->table('directus_fields'); + $fileds->insert($value)->save(); + } + } + + //Insert into relations table + $relationsData = [ + [ + 'collection_many' => 'directus_activity', + 'field_many' => 'action_by', + 'collection_one' => 'directus_users' + ], + [ + 'collection_many' => 'directus_collections_presets', + 'field_many' => 'user', + 'collection_one' => 'directus_users' + ], + [ + 'collection_many' => 'directus_collections_presets', + 'field_many' => 'group', + 'collection_one' => 'directus_groups' + ], + [ + 'collection_many' => 'directus_fields', + 'field_many' => 'collection', + 'collection_one' => 'directus_collections', + 'field_one' => 'fields' + ], + [ + 'collection_many' => 'directus_files', + 'field_many' => 'uploaded_by', + 'collection_one' => 'directus_users' + ], + [ + 'collection_many' => 'directus_files', + 'field_many' => 'folder', + 'collection_one' => 'directus_folders' + ], + [ + 'collection_many' => 'directus_folders', + 'field_many' => 'parent_folder', + 'collection_one' => 'directus_folders' + ], + [ + 'collection_many' => 'directus_permissions', + 'field_many' => 'group', + 'collection_one' => 'directus_groups' + ], + [ + 'collection_many' => 'directus_revisions', + 'field_many' => 'activity', + 'collection_one' => 'directus_activity' + ], + [ + 'collection_many' => 'directus_users', + 'field_many' => 'role', + 'collection_one' => 'directus_roles', + 'field_one' => 'users' + ], + [ + 'collection_many' => 'directus_users', + 'field_many' => 'avatar', + 'collection_one' => 'directus_files' + ] + ]; + + $files = $this->table('directus_relations'); + $files->insert($relationsData)->save(); + } + + public function checkFieldExist($collection,$field){ + $checkSql = sprintf('SELECT 1 FROM `directus_fields` WHERE `collection` = "%s" AND `field` = "%s";', $collection, $field); + return $this->query($checkSql)->fetch(); + } +} diff --git a/migrations/install/20180220023238_create_revisions_table.php b/migrations/install/20180220023238_create_revisions_table.php new file mode 100644 index 0000000000..34e51fd73d --- /dev/null +++ b/migrations/install/20180220023238_create_revisions_table.php @@ -0,0 +1,127 @@ +table('directus_revisions', ['signed' => false]); + + $table->addColumn('activity', 'integer', [ + 'null' => false, + 'signed' => false + ]); + $table->addColumn('collection', 'string', [ + 'limit' => 64, + 'null' => false + ]); + $table->addColumn('item', 'string', [ + 'limit' => 255 + ]); + $table->addColumn('data', 'text', [ + 'limit' => 4294967295 + ]); + $table->addColumn('delta', 'text', [ + 'limit' => 4294967295, + 'null' => true + ]); + $table->addColumn('parent_collection', 'string', [ + 'limit' => 64, + 'null' => true + ]); + $table->addColumn('parent_item', 'string', [ + 'limit' => 255, + 'null' => true + ]); + $table->addColumn('parent_changed', 'boolean', [ + 'signed' => false, + 'default' => false, + 'null' => true + ]); + + $table->create(); + + + $data = [ + [ + 'collection' => 'directus_revisions', + 'field' => 'id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'primary-key', + 'locked' => 1 + ], + [ + 'collection' => 'directus_revisions', + 'field' => 'activity', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, + 'interface' => 'many-to-one', + 'locked' => 1 + ], + [ + 'collection' => 'directus_revisions', + 'field' => 'collection', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, + 'interface' => 'many-to-one', + 'locked' => 1 + ], + [ + 'collection' => 'directus_revisions', + 'field' => 'item', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_revisions', + 'field' => 'data', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'json', + 'locked' => 1 + ], + [ + 'collection' => 'directus_revisions', + 'field' => 'delta', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'json', + 'locked' => 1 + ], + [ + 'collection' => 'directus_revisions', + 'field' => 'parent_item', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_revisions', + 'field' => 'parent_collection', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'collections', + 'locked' => 1 + ], + [ + 'collection' => 'directus_revisions', + 'field' => 'parent_changed', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1 + ], + ]; + + foreach($data as $value){ + if(!$this->checkFieldExist($value['collection'], $value['field'])){ + $fileds = $this->table('directus_fields'); + $fileds->insert($value)->save(); + } + } + } + + public function checkFieldExist($collection,$field){ + $checkSql = sprintf('SELECT 1 FROM `directus_fields` WHERE `collection` = "%s" AND `field` = "%s";', $collection, $field); + return $this->query($checkSql)->fetch(); + } +} diff --git a/migrations/install/20180220023243_create_settings_table.php b/migrations/install/20180220023243_create_settings_table.php new file mode 100644 index 0000000000..b11d1c900e --- /dev/null +++ b/migrations/install/20180220023243_create_settings_table.php @@ -0,0 +1,523 @@ +table('directus_settings', ['signed' => false]); + + $table->addColumn('key', 'string', [ + 'limit' => 64, + 'null' => false + ]); + + $table->addColumn('value', 'text', [ + 'default' => null, + 'null' => true + ]); + + $table->addIndex(['key'], [ + 'unique' => true, + 'name' => 'idx_key' + ]); + + $table->create(); + + $data = [ + [ + 'collection' => 'directus_settings', + 'field' => 'project_name', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'title' + ]), + 'locked' => 1, + 'required' => 1, + 'width' => 'half', + 'note' => 'Logo in the top-left of the App (40x40)', + 'sort' => 1 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'project_url', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'link' + ]), + 'locked' => 1, + 'width' => 'half', + 'note' => 'External link for the App\'s top-left logo', + 'sort' => 2 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'project_logo', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_FILE, + 'interface' => 'file', + 'locked' => 1, + 'width' => 'half', + 'note' => 'A 40x40 brand logo, ideally a white SVG/PNG', + 'sort' => 3 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'project_color', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'color', + 'locked' => 1, + 'width' => 'half', + 'note' => 'Color for login background and App\'s logo', + 'sort' => 4 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'project_foreground', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_FILE, + 'interface' => 'file', + 'locked' => 1, + 'width' => 'half', + 'note' => 'Centered image (eg: logo) for the login page', + 'sort' => 5 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'project_background', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_FILE, + 'interface' => 'file', + 'locked' => 1, + 'width' => 'half', + 'note' => 'Full-screen background for the login page', + 'sort' => 6 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'project_public_note', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'markdown', + 'locked' => 1, + 'width' => 'full', + 'note' => 'This value will be shown on the public pages of the app', + 'sort' => 7 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'default_locale', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'language', + 'locked' => 1, + 'width' => 'half', + 'note' => 'Default locale for Directus Users', + 'sort' => 8, + 'options' => json_encode([ + 'limit' => true + ]) + ], + [ + 'collection' => 'directus_settings', + 'field' => 'telemetry', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1, + 'width' => 'half', + 'note' => 'Learn More', + 'sort' => 9 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'data_divider', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ALIAS, + 'interface' => 'divider', + 'options' => json_encode([ + 'style' => 'large', + 'title' => 'Data', + 'hr' => true + ]), + 'locked' => 1, + 'width' => 'full', + 'hidden_browse' => 1, + 'sort' => 11 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'default_limit', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'numeric', + 'options' => json_encode([ + 'iconRight' => 'keyboard_tab' + ]), + 'locked' => 1, + 'required' => 1, + 'width' => 'half', + 'note' => 'Default item count in API and App responses', + 'sort' => 12 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'sort_null_last', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1, + 'note' => 'NULL values are sorted last', + 'width' => 'half', + 'sort' => 13 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'security_divider', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ALIAS, + 'interface' => 'divider', + 'options' => json_encode([ + 'style' => 'large', + 'title' => 'Security', + 'hr' => true + ]), + 'locked' => 1, + 'hidden_browse' => 1, + 'width' => 'full', + 'sort' => 20 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'auto_sign_out', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'numeric', + 'options' => json_encode([ + 'iconRight' => 'timer' + ]), + 'locked' => 1, + 'required' => 1, + 'width' => 'half', + 'note' => 'Minutes before idle users are signed out', + 'sort' => 22 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'login_attempts_allowed', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'numeric', + 'options' => json_encode([ + 'iconRight' => 'lock' + ]), + 'locked' => 1, + 'width' => 'half', + 'note' => 'Failed login attempts before suspending users', + 'sort' => 23 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'password_policy', + 'type' => 'string', + 'note' => 'Weak: Minimum length 8; Strong: 1 small-case letter, 1 capital letter, 1 digit, 1 special character and the length should be minimum 8', + 'interface' => 'dropdown', + 'options' => json_encode([ + 'choices' => [ + '' => 'None', + '/^.{8,}$/' => 'Weak', + '/(?=^.{8,}$)(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*()_+}{\';\'?>.<,])(?!.*\s).*$/' => 'Strong' + ] + ]), + 'sort' => 24, + 'locked' => 1, + 'width' => 'half' + ], + [ + 'collection' => 'directus_settings', + 'field' => 'files_divider', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ALIAS, + 'interface' => 'divider', + 'options' => json_encode([ + 'style' => 'large', + 'title' => 'Files & Thumbnails', + 'hr' => true + ]), + 'locked' => 1, + 'hidden_browse' => 1, + 'width' => 'full', + 'sort' => 30 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'file_naming', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'dropdown', + 'locked' => 1, + 'width' => 'half', + 'note' => 'File-system naming convention for uploads', + 'sort' => 31, + 'options' => json_encode([ + 'choices' => [ + 'uuid' => 'UUID (Obfuscated)', + 'file_name' => 'File Name (Readable)' + ] + ]) + ], + [ + 'collection' => 'directus_settings', + 'field' => 'file_max_size', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'placeholder' => 'eg: 4MB', + 'iconRight' => 'storage' + ]), + 'locked' => 1, + 'width' => 'half', + 'sort' => 32 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'file_mimetype_whitelist', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ARRAY, + 'interface' => 'tags', + 'options' => json_encode([ + 'placeholder' => 'Enter a file mimetype then hit enter (eg: image/jpeg)' + ]), + 'locked' => 1, + 'width' => 'full', + 'sort' => 33 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'asset_whitelist', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'repeater', + 'width' => 'full', + 'note' => 'Defines how the thumbnail will be generated based on the requested params.', + 'sort' => 34, + 'options' => json_encode([ + 'template' => '{{key}}', + 'fields' => [ + [ + 'field' => 'key', + 'interface' => 'slug', + 'width' => 'half', + 'type' => 'string', + 'required' => true + ], + [ + 'field' => 'fit', + 'interface' => 'dropdown', + 'width' => 'half', + 'type' => 'string', + 'options' => [ + 'choices' => [ + 'crop' => 'Crop (forces exact size)', + 'contain' => 'Contain (preserve aspect ratio)' + ] + ], + 'required' => true + ], + [ + 'field' => 'width', + 'interface' => 'numeric', + 'width' => 'half', + 'type' => 'integer', + 'required' => true + ], + [ + 'field' => 'height', + 'interface' => 'numeric', + 'width' => 'half', + 'type' => 'integer', + 'required' => true + ], + [ + 'field' => 'quality', + 'interface' => 'slider', + 'width' => 'full', + 'type' => 'integer', + 'default' => 80, + 'options' => [ + 'min' => 0, + 'max' => 100, + 'step' => 1 + ], + 'required' => true + ] + ] + ]) + ], + [ + 'collection' => 'directus_settings', + 'field' => 'asset_whitelist_system', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'json', + 'readonly' => 1, + 'width' => 'half', + 'hidden_browse' => 1, + 'hidden_detail' => 1, + 'sort' => 35 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'youtube_api_key', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'videocam' + ]), + 'locked' => 1, + 'width' => 'full', + 'note' => 'Allows fetching more YouTube Embed info', + 'sort' => 36 + ], + ]; + + foreach($data as $value){ + if(!$this->checkFieldExist($value['collection'], $value['field'])){ + $fileds = $this->table('directus_fields'); + $fileds->insert($value)->save(); + } + } + + // Insert into settings table + $data = [ + [ + 'key' => 'project_url', + 'value' => '' + ], + [ + 'key' => 'project_logo', + 'value' => '' + ], + [ + 'key' => 'project_color', + 'value' => '#263238', + ], + [ + 'key' => 'project_foreground', + 'value' => '', + ], + [ + 'key' => 'project_background', + 'value' => '', + ], + [ + 'key' => 'project_public_note', + 'value' => '', + ], + [ + 'key' => 'default_locale', + 'value' => 'en-US', + ], + [ + 'key' => 'telemetry', + 'value' => '1', + ], + [ + 'key' => 'default_limit', + 'value' => '200' + ], + [ + 'key' => 'sort_null_last', + 'value' => '1' + ], + [ + 'key' => 'password_policy', + 'value' => '' + ], + [ + 'key' => 'auto_sign_out', + 'value' => '10080' + ], + [ + 'key' => 'login_attempts_allowed', + 'value' => '10' + ], + [ + 'key' => 'trusted_proxies', + 'value' => '' + ], + [ + 'key' => 'file_max_size', + 'value' => '100MB' + ], + [ + 'key' => 'file_mimetype_whitelist', + 'value' => '' + ], + [ + 'key' => 'file_naming', + 'value' => 'uuid' + ], + [ + 'key' => 'youtube_api_key', + 'value' => '' + ], + [ + 'key' => 'asset_whitelist', + 'value' => json_encode([ + [ + "key" => "thumbnail", + "width" => 200, + "height" => 200, + "fit" => "contain", + "quality" => 80 + ] + ]) + ], + [ + 'key' => 'asset_whitelist_system', + 'value' => json_encode([ + [ + 'key' => 'directus-small-crop', + 'width' => 64, + 'height' => 64, + 'fit' => 'crop', + 'quality' => 80 + ], + [ + 'key' => 'directus-small-contain', + 'width' => 64, + 'height' => 64, + 'fit' => 'contain', + 'quality' => 80 + ], + [ + 'key' => 'directus-medium-crop', + 'width' => 300, + 'height' => 300, + 'fit' => 'crop', + 'quality' => 80 + ], + [ + 'key' => 'directus-medium-contain', + 'width' => 300, + 'height' => 300, + 'fit' => 'contain', + 'quality' => 80 + ], + [ + 'key' => 'directus-large-crop', + 'width' => 800, + 'height' => 600, + 'fit' => 'crop', + 'quality' => 80 + ], + [ + 'key' => 'directus-large-contain', + 'width' => 800, + 'height' => 600, + 'fit' => 'contain', + 'quality' => 80 + ] + ]) + ] + ]; + + $groups = $this->table('directus_settings'); + $groups->insert($data)->save(); + } + + public function checkFieldExist($collection,$field){ + $checkSql = sprintf('SELECT 1 FROM `directus_fields` WHERE `collection` = "%s" AND `field` = "%s";', $collection, $field); + return $this->query($checkSql)->fetch(); + } +} diff --git a/migrations/install/20180220023248_create_users_table.php b/migrations/install/20180220023248_create_users_table.php new file mode 100644 index 0000000000..bb5b7fdd54 --- /dev/null +++ b/migrations/install/20180220023248_create_users_table.php @@ -0,0 +1,552 @@ +table('directus_users', ['signed' => false]); + + $table->addColumn('status', 'string', [ + 'limit' => 16, + 'default' => \Directus\Database\TableGateway\DirectusUsersTableGateway::STATUS_DRAFT + ]); + $table->addColumn('role', 'integer', [ + 'null' => true, + 'default' => null + ]); + $table->addColumn('first_name', 'string', [ + 'limit' => 50, + 'null' => true, + 'default' => null + ]); + $table->addColumn('last_name', 'string', [ + 'limit' => 50, + 'null' => true, + 'default' => null + ]); + $table->addColumn('email', 'string', [ + 'limit' => 128, + 'null' => false + ]); + $table->addColumn('password', 'string', [ + 'limit' => 255, + 'encoding' => 'utf8', + 'null' => true, + 'default' => null + ]); + $table->addColumn('token', 'string', [ + 'limit' => 255, + 'encoding' => 'utf8', + 'null' => true, + 'default' => null + ]); + $table->addColumn('timezone', 'string', [ + 'limit' => 32, + 'default' => date_default_timezone_get(), + ]); + $table->addColumn('locale', 'string', [ + 'limit' => 8, + 'null' => true, + 'default' => null + ]); + $table->addColumn('locale_options', 'text', [ + 'null' => true, + 'default' => null + ]); + $table->addColumn('avatar', 'integer', [ + 'signed' => false, + 'limit' => 11, + 'null' => true, + 'default' => null + ]); + $table->addColumn('company', 'string', [ + 'limit' => 191, + 'null' => true, + 'default' => null + ]); + $table->addColumn('title', 'string', [ + 'limit' => 191, + 'null' => true, + 'default' => null + ]); + $table->addColumn('email_notifications', 'integer', [ + 'limit' => 1, + 'default' => 1 + ]); + $table->addColumn('last_access_on', 'datetime', [ + 'null' => true, + 'default' => null + ]); + $table->addColumn('last_page', 'string', [ + 'limit' => 192, + 'null' => true, + 'default' => null + ]); + $table->addColumn('external_id', 'string', [ + 'limit' => 255, + 'null' => true, + 'default' => null + ]); + $table->addColumn('theme', 'string', [ + 'limit' => 100, + 'encoding' => 'utf8', + 'null' => true, + 'default' => 'auto' + ]); + $table->addColumn('2fa_secret', 'string', [ + 'limit' => 100, + 'encoding' => 'utf8', + 'null' => true, + 'default' => null + ]); + + $table->addIndex('email', [ + 'unique' => true, + 'name' => 'idx_users_email' + ]); + + $table->addIndex('token', [ + 'unique' => true, + 'name' => 'idx_users_token' + ]); + + $table->addIndex('external_id', [ + 'unique' => true, + 'name' => 'idx_users_external_id' + ]); + + $table->create(); + + $data = [ + [ + 'collection' => 'directus_users', + 'field' => 'id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'primary-key', + 'locked' => 1, + 'required' => 1, + 'hidden_detail' => 1, + 'sort' => 1 + ], + [ + 'collection' => 'directus_users', + 'field' => 'status', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STATUS, + 'interface' => 'status', + 'options' => json_encode([ + 'status_mapping' => [ + 'draft' => [ + 'name' => 'Draft', + 'text_color' => 'white', + 'background_color' => 'light-gray', + 'listing_subdued' => false, + 'listing_badge' => true, + 'soft_delete' => false, + ], + 'invited' => [ + 'name' => 'Invited', + 'text_color' => 'white', + 'background_color' => 'light-gray', + 'listing_subdued' => false, + 'listing_badge' => true, + 'soft_delete' => false, + ], + 'active' => [ + 'name' => 'Active', + 'text_color' => 'white', + 'background_color' => 'success', + 'listing_subdued' => false, + 'listing_badge' => false, + 'soft_delete' => false, + ], + 'suspended' => [ + 'name' => 'Suspended', + 'text_color' => 'white', + 'background_color' => 'light-gray', + 'listing_subdued' => false, + 'listing_badge' => true, + 'soft_delete' => false, + ], + 'deleted' => [ + 'name' => 'Deleted', + 'text_color' => 'white', + 'background_color' => 'danger', + 'listing_subdued' => false, + 'listing_badge' => true, + 'soft_delete' => true, + ] + ] + ]), + 'locked' => 1, + 'sort' => 2, + 'required' => 1 + ], + [ + 'collection' => 'directus_users', + 'field' => 'first_name', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'account_circle' + ]), + 'locked' => 1, + 'required' => 1, + 'sort' => 3, + 'width' => 'half' + ], + [ + 'collection' => 'directus_users', + 'field' => 'last_name', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'account_circle' + ]), + 'locked' => 1, + 'required' => 1, + 'sort' => 4, + 'width' => 'half' + ], + [ + 'collection' => 'directus_users', + 'field' => 'email', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'alternate_email' + ]), + 'locked' => 1, + 'validation' => '$email', + 'required' => 1, + 'sort' => 5, + 'width' => 'half' + ], + [ + 'collection' => 'directus_users', + 'field' => 'email_notifications', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1, + 'sort' => 6, + 'width' => 'half' + ], + [ + 'collection' => 'directus_users', + 'field' => 'password', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_HASH, + 'interface' => 'password', + 'locked' => 1, + 'required' => 1, + 'sort' => 7, + 'width' => 'half' + ], + [ + 'collection' => 'directus_users', + 'field' => 'role', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, + 'interface' => 'user-roles', + 'locked' => 1, + 'sort' => 8, + 'width' => 'half', + 'required' => 1 + ], + [ + 'collection' => 'directus_users', + 'field' => 'company', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'location_city' + ]), + 'sort' => 9, + 'width' => 'half' + ], + [ + 'collection' => 'directus_users', + 'field' => 'title', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'text_fields' + ]), + 'sort' => 10, + 'width' => 'half' + ], + [ + 'collection' => 'directus_users', + 'field' => 'timezone', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'dropdown', + 'options' => json_encode([ + 'choices' => [ + 'Pacific/Midway' => '(UTC-11:00) Midway Island', + 'Pacific/Samoa' => '(UTC-11:00) Samoa', + 'Pacific/Honolulu' => '(UTC-10:00) Hawaii', + 'US/Alaska' => '(UTC-09:00) Alaska', + 'America/Los_Angeles' => '(UTC-08:00) Pacific Time (US & Canada)', + 'America/Tijuana' => '(UTC-08:00) Tijuana', + 'US/Arizona' => '(UTC-07:00) Arizona', + 'America/Chihuahua' => '(UTC-07:00) Chihuahua', + 'America/Mexico/La_Paz' => '(UTC-07:00) La Paz', + 'America/Mazatlan' => '(UTC-07:00) Mazatlan', + 'US/Mountain' => '(UTC-07:00) Mountain Time (US & Canada)', + 'America/Managua' => '(UTC-06:00) Central America', + 'US/Central' => '(UTC-06:00) Central Time (US & Canada)', + 'America/Guadalajara' => '(UTC-06:00) Guadalajara', + 'America/Mexico_City' => '(UTC-06:00) Mexico City', + 'America/Monterrey' => '(UTC-06:00) Monterrey', + 'Canada/Saskatchewan' => '(UTC-06:00) Saskatchewan', + 'America/Bogota' => '(UTC-05:00) Bogota', + 'US/Eastern' => '(UTC-05:00) Eastern Time (US & Canada)', + 'US/East-Indiana' => '(UTC-05:00) Indiana (East)', + 'America/Lima' => '(UTC-05:00) Lima', + 'America/Quito' => '(UTC-05:00) Quito', + 'Canada/Atlantic' => '(UTC-04:00) Atlantic Time (Canada)', + 'America/New_York' => '(UTC-04:00) New York', + 'America/Caracas' => '(UTC-04:30) Caracas', + 'America/La_Paz' => '(UTC-04:00) La Paz', + 'America/Santiago' => '(UTC-04:00) Santiago', + 'America/Santo_Domingo' => '(UTC-04:00) Santo Domingo', + 'Canada/Newfoundland' => '(UTC-03:30) Newfoundland', + 'America/Sao_Paulo' => '(UTC-03:00) Brasilia', + 'America/Argentina/Buenos_Aires' => '(UTC-03:00) Buenos Aires', + 'America/Argentina/GeorgeTown' => '(UTC-03:00) Georgetown', + 'America/Godthab' => '(UTC-03:00) Greenland', + 'America/Noronha' => '(UTC-02:00) Mid-Atlantic', + 'Atlantic/Azores' => '(UTC-01:00) Azores', + 'Atlantic/Cape_Verde' => '(UTC-01:00) Cape Verde Is.', + 'Africa/Casablanca' => '(UTC+00:00) Casablanca', + 'Europe/Edinburgh' => '(UTC+00:00) Edinburgh', + 'Etc/Greenwich' => '(UTC+00:00) Greenwich Mean Time : Dublin', + 'Europe/Lisbon' => '(UTC+00:00) Lisbon', + 'Europe/London' => '(UTC+00:00) London', + 'Africa/Monrovia' => '(UTC+00:00) Monrovia', + 'UTC' => '(UTC+00:00) UTC', + 'Europe/Amsterdam' => '(UTC+01:00) Amsterdam', + 'Europe/Belgrade' => '(UTC+01:00) Belgrade', + 'Europe/Berlin' => '(UTC+01:00) Berlin', + 'Europe/Bern' => '(UTC+01:00) Bern', + 'Europe/Bratislava' => '(UTC+01:00) Bratislava', + 'Europe/Brussels' => '(UTC+01:00) Brussels', + 'Europe/Budapest' => '(UTC+01:00) Budapest', + 'Europe/Copenhagen' => '(UTC+01:00) Copenhagen', + 'Europe/Ljubljana' => '(UTC+01:00) Ljubljana', + 'Europe/Madrid' => '(UTC+01:00) Madrid', + 'Europe/Paris' => '(UTC+01:00) Paris', + 'Europe/Prague' => '(UTC+01:00) Prague', + 'Europe/Rome' => '(UTC+01:00) Rome', + 'Europe/Sarajevo' => '(UTC+01:00) Sarajevo', + 'Europe/Skopje' => '(UTC+01:00) Skopje', + 'Europe/Stockholm' => '(UTC+01:00) Stockholm', + 'Europe/Vienna' => '(UTC+01:00) Vienna', + 'Europe/Warsaw' => '(UTC+01:00) Warsaw', + 'Africa/Lagos' => '(UTC+01:00) West Central Africa', + 'Europe/Zagreb' => '(UTC+01:00) Zagreb', + 'Europe/Athens' => '(UTC+02:00) Athens', + 'Europe/Bucharest' => '(UTC+02:00) Bucharest', + 'Africa/Cairo' => '(UTC+02:00) Cairo', + 'Africa/Harare' => '(UTC+02:00) Harare', + 'Europe/Helsinki' => '(UTC+02:00) Helsinki', + 'Europe/Istanbul' => '(UTC+02:00) Istanbul', + 'Asia/Jerusalem' => '(UTC+02:00) Jerusalem', + 'Europe/Kyiv' => '(UTC+02:00) Kyiv', + 'Africa/Johannesburg' => '(UTC+02:00) Pretoria', + 'Europe/Riga' => '(UTC+02:00) Riga', + 'Europe/Sofia' => '(UTC+02:00) Sofia', + 'Europe/Tallinn' => '(UTC+02:00) Tallinn', + 'Europe/Vilnius' => '(UTC+02:00) Vilnius', + 'Asia/Baghdad' => '(UTC+03:00) Baghdad', + 'Asia/Kuwait' => '(UTC+03:00) Kuwait', + 'Europe/Minsk' => '(UTC+03:00) Minsk', + 'Africa/Nairobi' => '(UTC+03:00) Nairobi', + 'Asia/Riyadh' => '(UTC+03:00) Riyadh', + 'Europe/Volgograd' => '(UTC+03:00) Volgograd', + 'Asia/Tehran' => '(UTC+03:30) Tehran', + 'Asia/Abu_Dhabi' => '(UTC+04:00) Abu Dhabi', + 'Asia/Baku' => '(UTC+04:00) Baku', + 'Europe/Moscow' => '(UTC+04:00) Moscow', + 'Asia/Muscat' => '(UTC+04:00) Muscat', + 'Europe/St_Petersburg' => '(UTC+04:00) St. Petersburg', + 'Asia/Tbilisi' => '(UTC+04:00) Tbilisi', + 'Asia/Yerevan' => '(UTC+04:00) Yerevan', + 'Asia/Kabul' => '(UTC+04:30) Kabul', + 'Asia/Islamabad' => '(UTC+05:00) Islamabad', + 'Asia/Karachi' => '(UTC+05:00) Karachi', + 'Asia/Tashkent' => '(UTC+05:00) Tashkent', + 'Asia/Calcutta' => '(UTC+05:30) Chennai', + 'Asia/Kolkata' => '(UTC+05:30) Kolkata', + 'Asia/Mumbai' => '(UTC+05:30) Mumbai', + 'Asia/New_Delhi' => '(UTC+05:30) New Delhi', + 'Asia/Sri_Jayawardenepura' => '(UTC+05:30) Sri Jayawardenepura', + 'Asia/Katmandu' => '(UTC+05:45) Kathmandu', + 'Asia/Almaty' => '(UTC+06:00) Almaty', + 'Asia/Astana' => '(UTC+06:00) Astana', + 'Asia/Dhaka' => '(UTC+06:00) Dhaka', + 'Asia/Yekaterinburg' => '(UTC+06:00) Ekaterinburg', + 'Asia/Rangoon' => '(UTC+06:30) Rangoon', + 'Asia/Bangkok' => '(UTC+07:00) Bangkok', + 'Asia/Hanoi' => '(UTC+07:00) Hanoi', + 'Asia/Jakarta' => '(UTC+07:00) Jakarta', + 'Asia/Novosibirsk' => '(UTC+07:00) Novosibirsk', + 'Asia/Beijing' => '(UTC+08:00) Beijing', + 'Asia/Chongqing' => '(UTC+08:00) Chongqing', + 'Asia/Hong_Kong' => '(UTC+08:00) Hong Kong', + 'Asia/Krasnoyarsk' => '(UTC+08:00) Krasnoyarsk', + 'Asia/Kuala_Lumpur' => '(UTC+08:00) Kuala Lumpur', + 'Australia/Perth' => '(UTC+08:00) Perth', + 'Asia/Singapore' => '(UTC+08:00) Singapore', + 'Asia/Taipei' => '(UTC+08:00) Taipei', + 'Asia/Ulan_Bator' => '(UTC+08:00) Ulaan Bataar', + 'Asia/Urumqi' => '(UTC+08:00) Urumqi', + 'Asia/Irkutsk' => '(UTC+09:00) Irkutsk', + 'Asia/Osaka' => '(UTC+09:00) Osaka', + 'Asia/Sapporo' => '(UTC+09:00) Sapporo', + 'Asia/Seoul' => '(UTC+09:00) Seoul', + 'Asia/Tokyo' => '(UTC+09:00) Tokyo', + 'Australia/Adelaide' => '(UTC+09:30) Adelaide', + 'Australia/Darwin' => '(UTC+09:30) Darwin', + 'Australia/Brisbane' => '(UTC+10:00) Brisbane', + 'Australia/Canberra' => '(UTC+10:00) Canberra', + 'Pacific/Guam' => '(UTC+10:00) Guam', + 'Australia/Hobart' => '(UTC+10:00) Hobart', + 'Australia/Melbourne' => '(UTC+10:00) Melbourne', + 'Pacific/Port_Moresby' => '(UTC+10:00) Port Moresby', + 'Australia/Sydney' => '(UTC+10:00) Sydney', + 'Asia/Yakutsk' => '(UTC+10:00) Yakutsk', + 'Asia/Vladivostok' => '(UTC+11:00) Vladivostok', + 'Pacific/Auckland' => '(UTC+12:00) Auckland', + 'Pacific/Fiji' => '(UTC+12:00) Fiji', + 'Pacific/Kwajalein' => '(UTC+12:00) International Date Line West', + 'Asia/Kamchatka' => '(UTC+12:00) Kamchatka', + 'Asia/Magadan' => '(UTC+12:00) Magadan', + 'Pacific/Marshall_Is' => '(UTC+12:00) Marshall Is.', + 'Asia/New_Caledonia' => '(UTC+12:00) New Caledonia', + 'Asia/Solomon_Is' => '(UTC+12:00) Solomon Is.', + 'Pacific/Wellington' => '(UTC+12:00) Wellington', + 'Pacific/Tongatapu' => '(UTC+13:00) Nuku\'alofa' + ], + 'placeholder' => 'Choose a timezone...' + ]), + 'locked' => 1, + 'sort' => 11, + 'width' => 'half', + 'required' => 1 + ], + [ + 'collection' => 'directus_users', + 'field' => 'locale', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'language', + 'options' => json_encode([ + 'limit' => true + ]), + 'locked' => 1, + 'sort' => 12, + 'width' => 'half', + 'required' => 0 + ], + [ + 'collection' => 'directus_users', + 'field' => 'avatar', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_FILE, + 'interface' => 'file', + 'locked' => 1, + 'sort' => 13 + ], + [ + 'collection' => 'directus_users', + 'field' => 'theme', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'radio-buttons', + 'options' => json_encode([ + 'format' => true, + 'choices' => [ + 'auto' => 'Auto', + 'light' => 'Light', + 'dark' => 'Dark' + ] + ]), + 'locked' => 1, + 'readonly' => 0, + 'sort' => 14 + ], + [ + 'collection' => 'directus_users', + 'field' => '2fa_secret', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => '2fa-secret', + 'locked' => 1, + 'readonly' => 1, + 'sort' => 15 + ], + [ + 'collection' => 'directus_users', + 'field' => 'locale_options', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'json', + 'locked' => 1, + 'hidden_browse' => 1, + 'hidden_detail' => 1, + 'sort' => 16 + ], + [ + 'collection' => 'directus_users', + 'field' => 'token', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'hidden_detail' => 1, + 'hidden_browse' => 1, + 'sort' => 17 + ], + [ + 'collection' => 'directus_users', + 'field' => 'last_access_on', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_DATETIME, + 'interface' => 'datetime', + 'locked' => 1, + 'readonly' => 1, + 'hidden_detail' => 1, + 'sort' => 18 + ], + [ + 'collection' => 'directus_users', + 'field' => 'last_page', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'readonly' => 1, + 'hidden_detail' => 1, + 'hidden_browse' => 1, + 'sort' => 19 + ], + [ + 'collection' => 'directus_users', + 'field' => 'external_id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'readonly' => 1, + 'hidden_detail' => 1, + 'hidden_browse' => 1 + ], + ]; + + foreach($data as $value){ + if(!$this->checkFieldExist($value['collection'], $value['field'])){ + $fileds = $this->table('directus_fields'); + $fileds->insert($value)->save(); + } + } + } + + public function checkFieldExist($collection,$field){ + $checkSql = sprintf('SELECT 1 FROM `directus_fields` WHERE `collection` = "%s" AND `field` = "%s";', $collection, $field); + return $this->query($checkSql)->fetch(); + } +} diff --git a/migrations/upgrades/schemas/20190912072543_create_user_sessions.php b/migrations/install/20190912072543_create_user_sessions.php similarity index 71% rename from migrations/upgrades/schemas/20190912072543_create_user_sessions.php rename to migrations/install/20190912072543_create_user_sessions.php index affa1768df..2ba131d026 100644 --- a/migrations/upgrades/schemas/20190912072543_create_user_sessions.php +++ b/migrations/install/20190912072543_create_user_sessions.php @@ -6,25 +6,7 @@ class CreateUserSessions extends AbstractMigration { /** - * Change Method. - * - * Write your reversible migrations using this method. - * - * More information on writing migrations is available here: - * http://docs.phinx.org/en/latest/migrations.html#the-abstractmigration-class - * - * The following commands can be used in this method and Phinx will - * automatically reverse them when rolling back: - * - * createTable - * renameTable - * addColumn - * renameColumn - * addIndex - * addForeignKey - * - * Remember to call "create()" or "update()" and NOT "save()" when working - * with the Table class. + * Create User Sessions Table */ public function change() { @@ -36,6 +18,11 @@ public function change() 'default' => null ]); + $table->addColumn('token_type', 'string', [ + 'null' => true, + 'default' => null + ]); + $table->addColumn('token', 'string', [ 'limit' => 520, 'encoding' => 'utf8', @@ -60,13 +47,15 @@ public function change() 'null' => true, 'default' => null ]); - + + $table->addColumn('token_expired_at', 'datetime', [ + 'null' => true, + 'default' => null + ]); + $table->create(); - // Insert Into Directus Fields $data = [ - // User Session - // ----------------------------------------------------------------- [ 'collection' => 'directus_user_sessions', 'field' => 'id', @@ -123,15 +112,14 @@ public function change() foreach($data as $value){ if(!$this->checkFieldExist($value['collection'], $value['field'])){ - $insertSqlFormat = "INSERT INTO `directus_fields` (`collection`, `field`, `type`, `interface`, `hidden_detail`, `required`, `locked`, `options`) VALUES ('%s', '%s', '%s', '%s', '%s', '%s','%s' , '%s');"; - $insertSql = sprintf($insertSqlFormat,$value['collection'], $value['field'], $value['type'], $value['interface'], isset($value['hidden_detail']) ? $value['hidden_detail'] : 0, isset($value['required']) ? $value['required'] : 0, isset($value['locked']) ? $value['locked'] : 0, isset($value['options']) ? $value['options'] : null); - $this->execute($insertSql); + $fileds = $this->table('directus_fields'); + $fileds->insert($value)->save(); } } } + public function checkFieldExist($collection,$field){ $checkSql = sprintf('SELECT 1 FROM `directus_fields` WHERE `collection` = "%s" AND `field` = "%s";', $collection, $field); return $this->query($checkSql)->fetch(); } - } diff --git a/migrations/upgrades/schemas/20190917090849_create_web_hooks.php b/migrations/install/20190917090849_create_webhooks.php similarity index 59% rename from migrations/upgrades/schemas/20190917090849_create_web_hooks.php rename to migrations/install/20190917090849_create_webhooks.php index 374a3c28fb..e3cbfebacb 100644 --- a/migrations/upgrades/schemas/20190917090849_create_web_hooks.php +++ b/migrations/install/20190917090849_create_webhooks.php @@ -1,4 +1,3 @@ - \Directus\Api\Routes\Webhook::STATUS_INACTIVE ]); - $table->addColumn('collection', 'string', [ - 'limit' => 255, - 'null' => true, - 'default' => null - ]); - $table->addColumn('directus_action', 'string', [ + $table->addColumn('http_action', 'string', [ 'limit' => 255, 'encoding' => 'utf8', 'null' => true, @@ -56,18 +32,21 @@ public function change() 'default' => null ]); - $table->addColumn('http_action', 'string', [ + $table->addColumn('collection', 'string', [ 'limit' => 255, - 'encoding' => 'utf8', 'null' => true, 'default' => null ]); - + $table->addColumn('directus_action', 'string', [ + 'limit' => 255, + 'encoding' => 'utf8', + 'null' => true, + 'default' => null + ]); $table->create(); - // Insert Into Directus Fields $data = [ [ 'collection' => 'directus_webhooks', @@ -85,39 +64,31 @@ public function change() 'interface' => 'status', 'options' => json_encode([ 'status_mapping' => [ - 'published' => [ - 'name' => 'Published', - 'value' => 'published', + 'active' => [ + 'name' => 'Active', + 'value' => 'active', 'text_color' => 'white', - 'background_color' => 'accent', + 'background_color' => 'green', 'browse_subdued' => false, 'browse_badge' => true, 'soft_delete' => false, 'published' => true, ], - 'draft' => [ - 'name' => 'Draft', - 'value' => 'draft', + 'inactive' => [ + 'name' => 'Inactive', + 'value' => 'inactive', 'text_color' => 'white', - 'background_color' => 'blue-grey-100', + 'background_color' => 'blue-grey', 'browse_subdued' => true, 'browse_badge' => true, 'soft_delete' => false, 'published' => false, - ], - 'deleted' => [ - 'name' => 'Deleted', - 'value' => 'deleted', - 'text_color' => 'white', - 'background_color' => 'red', - 'browse_subdued' => true, - 'browse_badge' => true, - 'soft_delete' => true, - 'published' => false, ] ] ]), - 'required' => 1 + 'locked' => 1, + 'width' => 'full', + 'sort' => 1 ], [ 'collection' => 'directus_webhooks', @@ -127,17 +98,39 @@ public function change() 'required' => 1, 'options' => json_encode([ 'choices' => [ - 'get' => 'Get', - 'post' => 'Post' + 'get' => 'GET', + 'post' => 'POST' ] - ]) + ]), + 'locked' => 1, + 'width' => 'half-space', + 'sort' => 2 + ], + [ + 'collection' => 'directus_webhooks', + 'field' => 'url', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'placeholder' => 'https://example.com', + 'iconRight' => 'link' + ]), + 'required' => 1, + 'locked' => 1, + 'width' => 'full', + 'note' => '', + 'sort' => 3 ], [ 'collection' => 'directus_webhooks', 'field' => 'collection', 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, 'interface' => 'collections', - 'required' => 1 + 'required' => 1, + 'locked' => 1, + 'width' => 'half', + 'note' => '', + 'sort' => 4 ], [ 'collection' => 'directus_webhooks', @@ -147,31 +140,39 @@ public function change() 'required' => 1, 'options' => json_encode([ 'choices' => [ - 'item.create:before' => 'item.create:before', - 'item.create:after' => 'item.create:after', - 'item.update:before' => 'item.update:before', - 'item.update:after' => 'item.update:after', - 'item.delete:before' => 'item.delete:before', - 'item.delete:after' => 'item.delete:after', + 'item.create:after' => 'Create', + 'item.update:after' => 'Update', + 'item.delete:after' => 'Delete', ] - ]) + ]), + 'locked' => 1, + 'width' => 'half', + 'note' => '', + 'sort' => 5 ], [ 'collection' => 'directus_webhooks', - 'field' => 'url', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'interface' => 'text-input', - 'required' => 1 - ] - + 'field' => 'info', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ALIAS, + 'interface' => 'divider', + 'options' => json_encode([ + 'style' => 'medium', + 'title' => 'How Webhooks Work', + 'hr' => true, + 'margin' => false, + 'description' => 'When the selected action occurs for the selected collection, Directus will send an HTTP request to the above URL.' + ]), + 'locked' => 1, + 'width' => 'full', + 'hidden_browse' => 1, + 'sort' => 6 + ], ]; foreach($data as $value){ if(!$this->checkFieldExist($value['collection'], $value['field'])){ - $insertSqlFormat = "INSERT INTO `directus_fields` (`collection`, `field`, `type`, `interface`, `hidden_detail`, `required`, `locked`, `options`) VALUES ('%s', '%s', '%s', '%s', '%s', '%s','%s' , '%s');"; - - $insertSql = sprintf($insertSqlFormat,$value['collection'], $value['field'], $value['type'], $value['interface'], isset($value['hidden_detail']) ? $value['hidden_detail'] : 0, $value['required'], isset($value['locked']) ? $value['locked'] : 0, isset($value['options']) ? $value['options'] : null); - $this->execute($insertSql); + $fileds = $this->table('directus_fields'); + $fileds->insert($value)->save(); } } } diff --git a/migrations/migrations.php b/migrations/migrations.php index cb6b9a5d5f..288783c0fa 100644 --- a/migrations/migrations.php +++ b/migrations/migrations.php @@ -2,8 +2,7 @@ return [ 'paths' => [ - 'migrations' => '%%PHINX_CONFIG_DIR%%/../migrations/db/schemas', - 'seeds' => '%%PHINX_CONFIG_DIR%%/../migrations/db/seeds' + 'migrations' => '%%PHINX_CONFIG_DIR%%/../migrations/install' ], 'version_order' => 'creation', diff --git a/migrations/migrations.upgrades.php b/migrations/migrations.upgrades.php index 3b6414a7b8..ab7481401e 100644 --- a/migrations/migrations.upgrades.php +++ b/migrations/migrations.upgrades.php @@ -2,8 +2,7 @@ return [ 'paths' => [ - 'migrations' => '%%PHINX_CONFIG_DIR%%/../migrations/upgrades/schemas', - 'seeds' => '%%PHINX_CONFIG_DIR%%/../migrations/upgrades/seeds' + 'migrations' => '%%PHINX_CONFIG_DIR%%/../migrations/upgrades' ], 'version_order' => 'creation', diff --git a/migrations/upgrades/schemas/20181022175715_upgrade_070003.php b/migrations/upgrades/20181022175715_upgrade_070003.php similarity index 100% rename from migrations/upgrades/schemas/20181022175715_upgrade_070003.php rename to migrations/upgrades/20181022175715_upgrade_070003.php diff --git a/migrations/upgrades/schemas/20181102153600_timezone_choices.php b/migrations/upgrades/20181102153600_timezone_choices.php similarity index 100% rename from migrations/upgrades/schemas/20181102153600_timezone_choices.php rename to migrations/upgrades/20181102153600_timezone_choices.php diff --git a/migrations/upgrades/schemas/20181105165224_upgrade_070006.php b/migrations/upgrades/20181105165224_upgrade_070006.php similarity index 100% rename from migrations/upgrades/schemas/20181105165224_upgrade_070006.php rename to migrations/upgrades/20181105165224_upgrade_070006.php diff --git a/migrations/upgrades/schemas/20181122195602_locale_interface.php b/migrations/upgrades/20181122195602_locale_interface.php similarity index 100% rename from migrations/upgrades/schemas/20181122195602_locale_interface.php rename to migrations/upgrades/20181122195602_locale_interface.php diff --git a/migrations/upgrades/schemas/20181123171520_remove_scope.php b/migrations/upgrades/20181123171520_remove_scope.php similarity index 100% rename from migrations/upgrades/schemas/20181123171520_remove_scope.php rename to migrations/upgrades/20181123171520_remove_scope.php diff --git a/migrations/upgrades/schemas/20181210204720_add_trusted_proxies_setting_field.php b/migrations/upgrades/20181210204720_add_trusted_proxies_setting_field.php similarity index 100% rename from migrations/upgrades/schemas/20181210204720_add_trusted_proxies_setting_field.php rename to migrations/upgrades/20181210204720_add_trusted_proxies_setting_field.php diff --git a/migrations/upgrades/schemas/20181222023800_add_project_url_setting_field.php b/migrations/upgrades/20181222023800_add_project_url_setting_field.php similarity index 100% rename from migrations/upgrades/schemas/20181222023800_add_project_url_setting_field.php rename to migrations/upgrades/20181222023800_add_project_url_setting_field.php diff --git a/migrations/upgrades/schemas/20181227042755_increase_users_last_page_length.php b/migrations/upgrades/20181227042755_increase_users_last_page_length.php similarity index 100% rename from migrations/upgrades/schemas/20181227042755_increase_users_last_page_length.php rename to migrations/upgrades/20181227042755_increase_users_last_page_length.php diff --git a/migrations/upgrades/schemas/20190104155309_add_users_email_validation.php b/migrations/upgrades/20190104155309_add_users_email_validation.php similarity index 100% rename from migrations/upgrades/schemas/20190104155309_add_users_email_validation.php rename to migrations/upgrades/20190104155309_add_users_email_validation.php diff --git a/migrations/upgrades/schemas/20190111193724_add_app_url_setting_field.php b/migrations/upgrades/20190111193724_add_app_url_setting_field.php similarity index 100% rename from migrations/upgrades/schemas/20190111193724_add_app_url_setting_field.php rename to migrations/upgrades/20190111193724_add_app_url_setting_field.php diff --git a/migrations/upgrades/schemas/20190111212736_add_missing_settings_field.php b/migrations/upgrades/20190111212736_add_missing_settings_field.php similarity index 100% rename from migrations/upgrades/schemas/20190111212736_add_missing_settings_field.php rename to migrations/upgrades/20190111212736_add_missing_settings_field.php diff --git a/migrations/upgrades/schemas/20190118181424_add_roles_users_field.php b/migrations/upgrades/20190118181424_add_roles_users_field.php similarity index 100% rename from migrations/upgrades/schemas/20190118181424_add_roles_users_field.php rename to migrations/upgrades/20190118181424_add_roles_users_field.php diff --git a/migrations/upgrades/schemas/20190130215921_add_files_checksum_field.php b/migrations/upgrades/20190130215921_add_files_checksum_field.php similarity index 100% rename from migrations/upgrades/schemas/20190130215921_add_files_checksum_field.php rename to migrations/upgrades/20190130215921_add_files_checksum_field.php diff --git a/migrations/upgrades/schemas/20190318173400_add_nav_override.php b/migrations/upgrades/20190318173400_add_nav_override.php similarity index 100% rename from migrations/upgrades/schemas/20190318173400_add_nav_override.php rename to migrations/upgrades/20190318173400_add_nav_override.php diff --git a/migrations/upgrades/schemas/20190412122400_set_password_type_to_hash.php b/migrations/upgrades/20190412122400_set_password_type_to_hash.php similarity index 100% rename from migrations/upgrades/schemas/20190412122400_set_password_type_to_hash.php rename to migrations/upgrades/20190412122400_set_password_type_to_hash.php diff --git a/migrations/upgrades/schemas/20190412123000_use_file_interface.php b/migrations/upgrades/20190412123000_use_file_interface.php similarity index 100% rename from migrations/upgrades/schemas/20190412123000_use_file_interface.php rename to migrations/upgrades/20190412123000_use_file_interface.php diff --git a/migrations/upgrades/schemas/20190412145800_set_o2m_options_roles.php b/migrations/upgrades/20190412145800_set_o2m_options_roles.php similarity index 100% rename from migrations/upgrades/schemas/20190412145800_set_o2m_options_roles.php rename to migrations/upgrades/20190412145800_set_o2m_options_roles.php diff --git a/migrations/upgrades/schemas/20190415125300_set_width.php b/migrations/upgrades/20190415125300_set_width.php similarity index 100% rename from migrations/upgrades/schemas/20190415125300_set_width.php rename to migrations/upgrades/20190415125300_set_width.php diff --git a/migrations/upgrades/schemas/20190419154400_settings_fields.php b/migrations/upgrades/20190419154400_settings_fields.php similarity index 100% rename from migrations/upgrades/schemas/20190419154400_settings_fields.php rename to migrations/upgrades/20190419154400_settings_fields.php diff --git a/migrations/upgrades/schemas/20190419161200_collection_notes.php b/migrations/upgrades/20190419161200_collection_notes.php similarity index 100% rename from migrations/upgrades/schemas/20190419161200_collection_notes.php rename to migrations/upgrades/20190419161200_collection_notes.php diff --git a/migrations/upgrades/schemas/20190419173000_misc_required_fields.php b/migrations/upgrades/20190419173000_misc_required_fields.php similarity index 100% rename from migrations/upgrades/schemas/20190419173000_misc_required_fields.php rename to migrations/upgrades/20190419173000_misc_required_fields.php diff --git a/migrations/upgrades/schemas/20190422131600_use_json.php b/migrations/upgrades/20190422131600_use_json.php similarity index 95% rename from migrations/upgrades/schemas/20190422131600_use_json.php rename to migrations/upgrades/20190422131600_use_json.php index dd26ee3f69..ba2ee9d67c 100644 --- a/migrations/upgrades/schemas/20190422131600_use_json.php +++ b/migrations/upgrades/20190422131600_use_json.php @@ -15,13 +15,13 @@ public function up() ['collection' => 'directus_activity', 'interface' => 'code'] )); - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'interface' => 'json' - ], - ['collection' => 'directus_activity_seen', 'interface' => 'code'] + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + [ + 'interface' => 'json' + ], + ['collection' => 'directus_activity_seen', 'interface' => 'code'] )); $this->execute(\Directus\phinx_update( diff --git a/migrations/upgrades/schemas/20190426156200_set_nullable_value_field_settings_table.php b/migrations/upgrades/20190426156200_set_nullable_value_field_settings_table.php similarity index 100% rename from migrations/upgrades/schemas/20190426156200_set_nullable_value_field_settings_table.php rename to migrations/upgrades/20190426156200_set_nullable_value_field_settings_table.php diff --git a/migrations/upgrades/schemas/20190520094300_use_timeline.php b/migrations/upgrades/20190520094300_use_timeline.php similarity index 100% rename from migrations/upgrades/schemas/20190520094300_use_timeline.php rename to migrations/upgrades/20190520094300_use_timeline.php diff --git a/migrations/upgrades/schemas/20190614103321_add_users_2fa_secret_field.php b/migrations/upgrades/20190614103321_add_users_2fa_secret_field.php similarity index 100% rename from migrations/upgrades/schemas/20190614103321_add_users_2fa_secret_field.php rename to migrations/upgrades/20190614103321_add_users_2fa_secret_field.php diff --git a/migrations/upgrades/schemas/20190618190024_add_enforce_2fa_role_field.php b/migrations/upgrades/20190618190024_add_enforce_2fa_role_field.php similarity index 100% rename from migrations/upgrades/schemas/20190618190024_add_enforce_2fa_role_field.php rename to migrations/upgrades/20190618190024_add_enforce_2fa_role_field.php diff --git a/migrations/upgrades/schemas/20190626114228_add_file_naming_in_setting.php b/migrations/upgrades/20190626114228_add_file_naming_in_setting.php similarity index 100% rename from migrations/upgrades/schemas/20190626114228_add_file_naming_in_setting.php rename to migrations/upgrades/20190626114228_add_file_naming_in_setting.php diff --git a/migrations/upgrades/schemas/20190702092323_add_login_attempts_allowed_setting.php b/migrations/upgrades/20190702092323_add_login_attempts_allowed_setting.php similarity index 100% rename from migrations/upgrades/schemas/20190702092323_add_login_attempts_allowed_setting.php rename to migrations/upgrades/20190702092323_add_login_attempts_allowed_setting.php diff --git a/migrations/upgrades/schemas/20190704063752_update_istanbul_timezone.php b/migrations/upgrades/20190704063752_update_istanbul_timezone.php similarity index 100% rename from migrations/upgrades/schemas/20190704063752_update_istanbul_timezone.php rename to migrations/upgrades/20190704063752_update_istanbul_timezone.php diff --git a/migrations/upgrades/schemas/20190708095522_add_file_extension_setting.php b/migrations/upgrades/20190708095522_add_file_extension_setting.php similarity index 100% rename from migrations/upgrades/schemas/20190708095522_add_file_extension_setting.php rename to migrations/upgrades/20190708095522_add_file_extension_setting.php diff --git a/migrations/upgrades/schemas/20190709053719_add_file_max_size_setting.php b/migrations/upgrades/20190709053719_add_file_max_size_setting.php similarity index 100% rename from migrations/upgrades/schemas/20190709053719_add_file_max_size_setting.php rename to migrations/upgrades/20190709053719_add_file_max_size_setting.php diff --git a/migrations/upgrades/schemas/20190722110232_password_validation_setting_field.php b/migrations/upgrades/20190722110232_password_validation_setting_field.php similarity index 100% rename from migrations/upgrades/schemas/20190722110232_password_validation_setting_field.php rename to migrations/upgrades/20190722110232_password_validation_setting_field.php diff --git a/migrations/upgrades/schemas/20190726064001_update_note_for_default_limit.php b/migrations/upgrades/20190726064001_update_note_for_default_limit.php similarity index 100% rename from migrations/upgrades/schemas/20190726064001_update_note_for_default_limit.php rename to migrations/upgrades/20190726064001_update_note_for_default_limit.php diff --git a/migrations/upgrades/schemas/20190819070856_update_directus_fields_field.php b/migrations/upgrades/20190819070856_update_directus_fields_field.php similarity index 100% rename from migrations/upgrades/schemas/20190819070856_update_directus_fields_field.php rename to migrations/upgrades/20190819070856_update_directus_fields_field.php diff --git a/migrations/upgrades/20191123053252_update_directus_files_field.php b/migrations/upgrades/20191123053252_update_directus_files_field.php new file mode 100644 index 0000000000..a56bd9e845 --- /dev/null +++ b/migrations/upgrades/20191123053252_update_directus_files_field.php @@ -0,0 +1,189 @@ +table('directus_files'); + $fieldsTable = $this->table('directus_fields'); + + if(!$this->checkFieldExist('directus_files', 'checksum')){ + $fieldsTable->insert([ + 'collection' => 'directus_files', + 'field' => 'checksum', + 'type' => 'string', + 'interface' => 'text-input', + ])->save(); + } + if (!$filesTable->hasColumn('checksum')) { + $filesTable->addColumn('checksum', 'string', [ + 'limit' => 32, + 'null' => true, + 'default' => null, + 'hidden_browse' => 1 + ])->save(); + } + + if($this->checkFieldExist('directus_files', 'filename')){ + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + [ + 'required' => 1 + ], + ['collection' => 'directus_files', 'field' => 'filename'] + )); + } + + if($this->checkFieldExist('directus_files', 'width')){ + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + [ + 'sort' => 10 + ], + ['collection' => 'directus_files', 'field' => 'width'] + )); + } + + if($this->checkFieldExist('directus_files', 'height')){ + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + [ + 'sort' => 11 + ], + ['collection' => 'directus_files', 'field' => 'height'] + )); + } + + if($this->checkFieldExist('directus_files', 'duration')){ + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + [ + 'readonly' => 1, + 'note' => 'Duration must be in seconds', + 'sort' => 12 + ], + ['collection' => 'directus_files', 'field' => 'duration'] + )); + } + + if($this->checkFieldExist('directus_files', 'filesize')){ + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + [ + 'sort' => 13 + ], + ['collection' => 'directus_files', 'field' => 'filesize'] + )); + } + + if($this->checkFieldExist('directus_files', 'uploaded_on')){ + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + [ + 'sort' => 8 + ], + ['collection' => 'directus_files', 'field' => 'uploaded_on'] + )); + + } + + if($this->checkFieldExist('directus_files', 'code')){ + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + [ + 'interface' => 'json' + ], + ['collection' => 'directus_files', 'interface' => 'code'] + )); + } + + if($this->checkFieldExist('directus_files', 'uploaded_by')){ + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + [ + 'type' => \Directus\Database\Schema\DataTypes::TYPE_USER_CREATED, + 'interface' => 'user-created', + 'sort' => 9 + ], + ['collection' => 'directus_files', 'field' => 'uploaded_by'] + )); + } + + if($this->checkFieldExist('directus_files', 'tags')){ + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + [ + 'options' => json_encode([ + 'placeholder' => 'Enter a keyword then hit enter...' + ]), + ], + ['collection' => 'directus_files', 'field' => 'tags'] + )); + } + + if($this->checkFieldExist('directus_files', 'description')){ + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + [ + 'options' => json_encode([ + 'toolbar' => ['bold','italic','underline','link','code'] + ]), + ], + ['collection' => 'directus_files', 'field' => 'description'] + )); + } + + if($this->checkFieldExist('directus_files', 'metadata')){ + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + [ + 'width' => 'full' + ], + ['collection' => 'directus_files', 'field' => 'metadata'] + )); + } + + if (!$filesTable->hasColumn('private_hash')) { + $filesTable->addColumn('private_hash', 'string', [ + 'limit' => 16, + 'null' => true, + 'default' => null + ])->save(); + } + + if(!$this->checkFieldExist('directus_files', 'private_hash')){ + $fieldsTable->insert([ + 'collection' => 'directus_files', + 'field' => 'private_hash', + 'type' => 'string', + 'interface'=>'text-input', + 'readonly' => 1, + 'hidden_browse' => 1, + 'hidden_detail' => 1 + ])->save(); + } + } + + public function checkFieldExist($collection,$field){ + $checkSql = sprintf('SELECT 1 FROM `directus_fields` WHERE `collection` = "%s" AND `field` = "%s";', $collection, $field); + return $this->query($checkSql)->fetch(); + } +} diff --git a/migrations/upgrades/20191129123300_use_new_wysiwyg.php b/migrations/upgrades/20191129123300_use_new_wysiwyg.php new file mode 100644 index 0000000000..f5ce6d740a --- /dev/null +++ b/migrations/upgrades/20191129123300_use_new_wysiwyg.php @@ -0,0 +1,24 @@ +execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + ['interface' => 'wysiwyg'], + ['interface' => 'wysiwyg_advanced'] + )); + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + ['interface' => 'wysiwyg'], + ['interface' => 'wysiwyg_full'] + )); + } +} diff --git a/migrations/upgrades/20191202164200_add_user_sessions.php b/migrations/upgrades/20191202164200_add_user_sessions.php new file mode 100644 index 0000000000..f7d41314e0 --- /dev/null +++ b/migrations/upgrades/20191202164200_add_user_sessions.php @@ -0,0 +1,58 @@ +hasTable('directus_user_sessions')) { + // Don't do anything if the table already exists + return; + } + + $table = $this->table('directus_user_sessions', ['signed' => false]); + + $table->addColumn('user', 'integer', [ + 'signed' => false, + 'null' => true, + 'default' => null + ]); + + $table->addColumn('token_type', 'string', [ + 'null' => true, + 'default' => null + ]); + + $table->addColumn('token', 'string', [ + 'limit' => 520, + 'encoding' => 'utf8', + 'null' => true, + 'default' => null + ]); + + $table->addColumn('ip_address', 'string', [ + 'limit' => 255, + 'encoding' => 'utf8', + 'null' => true, + 'default' => null + ]); + + $table->addColumn('user_agent', 'text', [ + 'default' => null, + 'null' => true, + 'default' => null + ]); + + $table->addColumn('created_on', 'datetime', [ + 'null' => true, + 'default' => null + ]); + + $table->addColumn('token_expired_at', 'datetime', [ + 'null' => true, + 'default' => null + ]); + + $table->create(); + } +} diff --git a/migrations/upgrades/20191202164201_add_webhooks.php b/migrations/upgrades/20191202164201_add_webhooks.php new file mode 100644 index 0000000000..858c1e89c2 --- /dev/null +++ b/migrations/upgrades/20191202164201_add_webhooks.php @@ -0,0 +1,50 @@ +hasTable('directus_webhooks')) { + // Don't do anything if the table already exists + return; + } + + $table = $this->table('directus_webhooks', ['signed' => false]); + + $table->addColumn('status', 'string', [ + 'limit' => 16, + 'default' => \Directus\Api\Routes\Webhook::STATUS_INACTIVE + ]); + + + $table->addColumn('http_action', 'string', [ + 'limit' => 255, + 'encoding' => 'utf8', + 'null' => true, + 'default' => null + ]); + + $table->addColumn('url', 'string', [ + 'limit' => 510, + 'encoding' => 'utf8', + 'null' => true, + 'default' => null + ]); + + $table->addColumn('collection', 'string', [ + 'limit' => 255, + 'null' => true, + 'default' => null + ]); + + $table->addColumn('directus_action', 'string', [ + 'limit' => 255, + 'encoding' => 'utf8', + 'null' => true, + 'default' => null + ]); + + $table->create(); + } +} diff --git a/migrations/upgrades/20191202164202_remove_activity_seen.php b/migrations/upgrades/20191202164202_remove_activity_seen.php new file mode 100644 index 0000000000..719451a2f8 --- /dev/null +++ b/migrations/upgrades/20191202164202_remove_activity_seen.php @@ -0,0 +1,15 @@ +hasTable('directus_activity_seen')) { + $this->table('directus_activity_seen')->drop(); + } + } +} diff --git a/migrations/upgrades/20191202164203_convert_user_roles.php b/migrations/upgrades/20191202164203_convert_user_roles.php new file mode 100644 index 0000000000..1d47ff753f --- /dev/null +++ b/migrations/upgrades/20191202164203_convert_user_roles.php @@ -0,0 +1,40 @@ +table('directus_users'); + + // ------------------------------------------------------------------------- + // Add role column to directus_users + // ------------------------------------------------------------------------- + if ($usersTable->hasColumn('role') == false) { + $usersTable->addColumn('role', 'integer', [ + 'null' => true, + 'default' => null + ]); + $usersTable->save(); + } + + // ------------------------------------------------------------------------- + // Add users' roles to directus_roles + // It does this by retrieving the role ID from directus_user_roles and adding + // that role to the role column in directus_users + // ------------------------------------------------------------------------- + if ($this->hasTable('directus_user_roles')) { + $stmt = $this->query("SELECT * FROM `directus_user_roles`"); + while($row = $stmt->fetch(PDO::FETCH_ASSOC)){ + $this->execute('UPDATE `directus_users` SET `role` = '.$row['role'].' where id = '.$row['user'].';'); + } + } + + // ------------------------------------------------------------------------- + // Remove directus_user_roles table + // ------------------------------------------------------------------------- + if ($this->hasTable('directus_user_roles')) { + $this->table('directus_user_roles')->drop(); + } + } +} diff --git a/migrations/upgrades/20191202164204_update_directus_users.php b/migrations/upgrades/20191202164204_update_directus_users.php new file mode 100644 index 0000000000..23ff79f702 --- /dev/null +++ b/migrations/upgrades/20191202164204_update_directus_users.php @@ -0,0 +1,39 @@ +table('directus_users'); + + // ------------------------------------------------------------------------- + // Add theme column + // ------------------------------------------------------------------------- + if ($usersTable->hasColumn('theme') == false) { + $usersTable->addColumn('theme', 'string', [ + 'limit' => 100, + 'encoding' => 'utf8', + 'null' => true, + 'default' => 'auto' + ]); + } + + // ------------------------------------------------------------------------- + // Add theme column + // ------------------------------------------------------------------------- + if ($usersTable->hasColumn('2fa_secret') == false) { + $usersTable->addColumn('2fa_secret', 'string', [ + 'limit' => 100, + 'encoding' => 'utf8', + 'null' => true, + 'default' => null + ]); + } + + // ------------------------------------------------------------------------- + // Save changes to table + // ------------------------------------------------------------------------- + $usersTable->save(); + } +} diff --git a/migrations/upgrades/20191202164205_update_directus_settings.php b/migrations/upgrades/20191202164205_update_directus_settings.php new file mode 100644 index 0000000000..53f4370bdb --- /dev/null +++ b/migrations/upgrades/20191202164205_update_directus_settings.php @@ -0,0 +1,131 @@ +table('directus_settings'); + + // ------------------------------------------------------------------------- + // Remove deprecated setting fields + // ------------------------------------------------------------------------- + $this->removeSetting('color'); + $this->removeSetting('thumbnail_dimensions'); + $this->removeSetting('thumbnail_quality_tags'); + $this->removeSetting('thumbnail_actions'); + $this->removeSetting('thumbnail_cache_ttl'); + $this->removeSetting('thumbnail_not_found_location'); + + // ------------------------------------------------------------------------- + // Rename logo to project_logo + // ------------------------------------------------------------------------- + if ($this->hasSetting('logo')) { + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_settings', + ['key' => 'project_logo'], + ['key' => 'logo'] + )); + } + + // ------------------------------------------------------------------------- + // Add new setting fields + // ------------------------------------------------------------------------- + $newSettings = [ + [ + 'key' => 'project_url', + 'value' => '', + ], + [ + 'key' => 'project_color', + 'value' => '#13181a', + ], + [ + 'key' => 'project_foreground', + 'value' => '', + ], + [ + 'key' => 'project_background', + 'value' => '', + ], + [ + 'key' => 'default_locale', + 'value' => 'en-US', + ], + [ + 'key' => 'telemetry', + 'value' => '1' + ], + [ + 'key' => 'password_policy', + 'value' => '' + ], + [ + 'key' => 'login_attempts_allowed', + 'value' => '' + ], + [ + 'key' => 'file_max_size', + 'value' => '100MB' + ], + [ + 'key' => 'file_mimetype_whitelist', + 'value' => '' + ], + [ + 'key' => 'asset_whitelist_system', + 'value' => json_encode([ + [ + "key" => "card", + "width" => 200, + "height" => 200, + "fit" => "crop", + "quality" => 80 + ], + [ + "key" => "avatar", + "width" => 100, + "height" => 100, + "fit" => "crop", + "quality" => 80 + ] + ]) + ], + [ + 'key' => 'asset_whitelist', + 'value' => json_encode([ + [ + "key" => "thumbnail", + "width" => 200, + "height" => 200, + "fit" => "contain", + "quality" => 80 + ] + ]) + ] + ]; + + foreach($newSettings as $setting) { + if ($this->hasSetting($setting['key']) == false) { + $settingsTable->insert($setting); + } + } + + // ------------------------------------------------------------------------- + // Save the changes + // ------------------------------------------------------------------------- + $settingsTable->save(); + } + + public function hasSetting($field){ + $checkSql = sprintf('SELECT 1 FROM `directus_settings` WHERE `key` = "%s"', $field); + return $this->query($checkSql)->fetch(); + } + + public function removeSetting($field) { + if (!$this->hasSetting($field)) { + $this->execute('DELETE FROM `directus_settings` where `key` = "' . $field . '";'); + } + } +} diff --git a/migrations/upgrades/20191202164206_update_directus_files.php b/migrations/upgrades/20191202164206_update_directus_files.php new file mode 100644 index 0000000000..e1893fbfce --- /dev/null +++ b/migrations/upgrades/20191202164206_update_directus_files.php @@ -0,0 +1,60 @@ +table('directus_files'); + + // ------------------------------------------------------------------------- + // Rename filename to filename_disk + // ------------------------------------------------------------------------- + if ($filesTable->hasColumn('filename')) { + $filesTable->renameColumn('filename', 'filename_disk'); + } + + // ------------------------------------------------------------------------- + // Add filename_download column + // ------------------------------------------------------------------------- + if ($filesTable->hasColumn('filename_download') == false) { + $filesTable->addColumn('filename_download', 'string', [ + 'limit' => 255, + 'null' => false + ]); + } + + // ------------------------------------------------------------------------- + // Add private hash column + // ------------------------------------------------------------------------- + if ($filesTable->hasColumn('private_hash') == false) { + $filesTable->addColumn('private_hash', 'string', [ + 'limit' => 16, + 'null' => true, + 'default' => null + ]); + } + + // ------------------------------------------------------------------------- + // Add a private hash for all existing files + // ------------------------------------------------------------------------- + $filesTable->save(); // Make sure the private hash column above is saved + + $filesWithoutPrivateHash = $this->fetchAll('SELECT id FROM directus_files WHERE private_hash = null;'); + + foreach($filesWithoutPrivateHash as $key => $value) { + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_files', + ['private_hash' => get_random_string()], + ['id' => $value['id']] + )); + } + + // ------------------------------------------------------------------------- + // Save changes to table + // ------------------------------------------------------------------------- + $filesTable->save(); + } +} diff --git a/migrations/upgrades/20191202164207_update_directus_roles.php b/migrations/upgrades/20191202164207_update_directus_roles.php new file mode 100644 index 0000000000..3c34136cb7 --- /dev/null +++ b/migrations/upgrades/20191202164207_update_directus_roles.php @@ -0,0 +1,59 @@ +table('directus_roles'); + + // ------------------------------------------------------------------------- + // Remove unused nav_blacklist column + // ------------------------------------------------------------------------- + if ($rolesTable->hasColumn('nav_blacklist')) { + $rolesTable->removeColumn('nav_blacklist'); + } + + // ------------------------------------------------------------------------- + // Remove deprecated nav_override + // ------------------------------------------------------------------------- + if ($rolesTable->hasColumn('nav_override')) { + $rolesTable->removeColumn('nav_override'); + } + + // ------------------------------------------------------------------------- + // Add module_listing column + // ------------------------------------------------------------------------- + if ($rolesTable->hasColumn('module_listing') == false) { + $rolesTable->addColumn('module_listing', 'string', [ + 'null' => true, + 'default' => null + ]); + } + + // ------------------------------------------------------------------------- + // Add collection_listing column + // ------------------------------------------------------------------------- + if ($rolesTable->hasColumn('collection_listing') == false) { + $rolesTable->addColumn('collection_listing', 'string', [ + 'null' => true, + 'default' => null + ]); + } + + // ------------------------------------------------------------------------- + // Add enforce_2fa column + // ------------------------------------------------------------------------- + if ($rolesTable->hasColumn('enforce_2fa') == false) { + $rolesTable->addColumn('enforce_2fa', 'integer', [ + 'null' => true, + 'default' => false + ]); + } + + // ------------------------------------------------------------------------- + // Save changes to table + // ------------------------------------------------------------------------- + $rolesTable->save(); + } +} diff --git a/migrations/upgrades/20191202164208_reset_directus_relations.php b/migrations/upgrades/20191202164208_reset_directus_relations.php new file mode 100644 index 0000000000..042ae9636e --- /dev/null +++ b/migrations/upgrades/20191202164208_reset_directus_relations.php @@ -0,0 +1,98 @@ +table('directus_relations'); + + // ------------------------------------------------------------------------- + // Delete all system relations from directus_relations + // This will not delete rows that were added by the user + // ------------------------------------------------------------------------- + $this->execute('DELETE FROM directus_relations WHERE collection_many LIKE "directus\_%" AND collection_one LIKE "directus\_%"'); + + // ------------------------------------------------------------------------- + // Add v8.0.0 data + // ------------------------------------------------------------------------- + $data = [ + [ + 'collection_many' => 'directus_activity', + 'field_many' => 'action_by', + 'collection_one' => 'directus_users' + ], + [ + 'collection_many' => 'directus_collections_presets', + 'field_many' => 'user', + 'collection_one' => 'directus_users' + ], + [ + 'collection_many' => 'directus_collections_presets', + 'field_many' => 'group', + 'collection_one' => 'directus_groups' + ], + [ + 'collection_many' => 'directus_fields', + 'field_many' => 'collection', + 'collection_one' => 'directus_collections', + 'field_one' => 'fields' + ], + [ + 'collection_many' => 'directus_files', + 'field_many' => 'uploaded_by', + 'collection_one' => 'directus_users' + ], + [ + 'collection_many' => 'directus_files', + 'field_many' => 'folder', + 'collection_one' => 'directus_folders' + ], + [ + 'collection_many' => 'directus_folders', + 'field_many' => 'parent_folder', + 'collection_one' => 'directus_folders' + ], + [ + 'collection_many' => 'directus_permissions', + 'field_many' => 'group', + 'collection_one' => 'directus_groups' + ], + [ + 'collection_many' => 'directus_revisions', + 'field_many' => 'activity', + 'collection_one' => 'directus_activity' + ], + [ + 'collection_many' => 'directus_users', + 'field_many' => 'role', + 'collection_one' => 'directus_roles', + 'field_one' => 'users' + ], + [ + 'collection_many' => 'directus_users', + 'field_many' => 'avatar', + 'collection_one' => 'directus_files' + ] + ]; + + // ------------------------------------------------------------------------- + // Add the data to the table + // ------------------------------------------------------------------------- + $relationsTable->insert($data); + + // ------------------------------------------------------------------------- + // Save changes to table + // ------------------------------------------------------------------------- + $relationsTable->save(); + } +} diff --git a/migrations/upgrades/20191202164209_reset_directus_fields.php b/migrations/upgrades/20191202164209_reset_directus_fields.php new file mode 100644 index 0000000000..b515b17a42 --- /dev/null +++ b/migrations/upgrades/20191202164209_reset_directus_fields.php @@ -0,0 +1,2068 @@ +table('directus_fields'); + + // ------------------------------------------------------------------------- + // Delete all rows that apply to directus_* collections from fields + // ------------------------------------------------------------------------- + $this->execute('DELETE FROM directus_fields WHERE collection LIKE "directus\_%"'); + + // ------------------------------------------------------------------------- + // Add v8.0.0 data + // ------------------------------------------------------------------------- + $data = [ + // --------------------------------------------------------------------- + // directus_fields + [ + 'collection' => 'directus_fields', + 'field' => 'id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'primary-key', + 'locked' => 1, + 'required' => 1, + 'hidden_detail' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'collection', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, + 'interface' => 'many-to-one', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'field', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'type', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'primary-key', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'interface', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'primary-key', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'options', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'json', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'locked', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'translation', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'repeater', + 'locked' => 1, + 'options' => '{ + "fields": [ + { + "field": "locale", + "type": "string", + "interface": "language", + "options": { + "limit": true + }, + "width": "half" + }, + { + "field": "translation", + "type": "string", + "interface": "text-input", + "width": "half" + } + ] + }' + ], + [ + 'collection' => 'directus_fields', + 'field' => 'readonly', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'validation', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + ], + [ + 'collection' => 'directus_fields', + 'field' => 'required', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'sort', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_SORT, + 'interface' => 'sort', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'note', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'hidden_detail', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'hidden_browse', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'width', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'numeric', + 'locked' => 1 + ], + [ + 'collection' => 'directus_fields', + 'field' => 'group', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, + 'interface' => 'many-to-one', + 'locked' => 1 + ], + + // --------------------------------------------------------------------- + // directus_activity + [ + 'collection' => 'directus_activity', + 'field' => 'id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'primary-key', + 'locked' => 1, + 'readonly' => 1, + 'required' => 1, + 'hidden_detail' => 1 + ], + [ + 'collection' => 'directus_activity', + 'field' => 'action', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'change_history' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 1, + 'width' => 'full' + ], + [ + 'collection' => 'directus_activity', + 'field' => 'collection', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'collections', + 'options' => json_encode([ + 'iconRight' => 'list_alt', + 'include_system' => true + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 2, + 'width' => 'half' + ], + [ + 'collection' => 'directus_activity', + 'field' => 'item', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'link' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 3, + 'width' => 'half' + ], + [ + 'collection' => 'directus_activity', + 'field' => 'action_by', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'user', + 'options' => json_encode([ + 'iconRight' => 'account_circle' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 4, + 'width' => 'half' + ], + [ + 'collection' => 'directus_activity', + 'field' => 'action_on', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_DATETIME, + 'interface' => 'datetime', + 'options' => json_encode([ + 'showRelative' => true, + 'iconRight' => 'calendar_today' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 5, + 'width' => 'half' + ], + [ + 'collection' => 'directus_activity', + 'field' => 'edited_on', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_DATETIME, + 'interface' => 'datetime', + 'options' => json_encode([ + 'showRelative' => true, + 'iconRight' => 'edit' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 6, + 'width' => 'half' + ], + [ + 'collection' => 'directus_activity', + 'field' => 'comment_deleted_on', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_DATETIME, + 'interface' => 'datetime', + 'options' => json_encode([ + 'showRelative' => true, + 'iconRight' => 'delete_outline' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 7, + 'width' => 'half' + ], + [ + 'collection' => 'directus_activity', + 'field' => 'ip', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'my_location' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 8, + 'width' => 'half' + ], + [ + 'collection' => 'directus_activity', + 'field' => 'user_agent', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'devices_other' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 9, + 'width' => 'half' + ], + [ + 'collection' => 'directus_activity', + 'field' => 'comment', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'textarea', + 'locked' => 1, + 'readonly' => 1, + 'sort' => 10, + 'width' => 'full' + ], + + // --------------------------------------------------------------------- + // directus_collections_presets + [ + 'collection' => 'directus_collection_presets', + 'field' => 'id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'primary-key', + 'locked' => 1 + ], + [ + 'collection' => 'directus_collection_presets', + 'field' => 'title', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_collection_presets', + 'field' => 'user', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'user', + 'locked' => 1 + ], + [ + 'collection' => 'directus_collection_presets', + 'field' => 'role', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, + 'interface' => 'many-to-one', + 'locked' => 1 + ], + [ + 'collection' => 'directus_collection_presets', + 'field' => 'collection', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, + 'interface' => 'many-to-one', + 'locked' => 1 + ], + [ + 'collection' => 'directus_collection_presets', + 'field' => 'search_query', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_collection_presets', + 'field' => 'filters', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'json', + 'locked' => 1 + ], + [ + 'collection' => 'directus_collection_presets', + 'field' => 'view_options', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'json', + 'locked' => 1 + ], + [ + 'collection' => 'directus_collection_presets', + 'field' => 'view_type', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_collection_presets', + 'field' => 'view_query', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'json', + 'locked' => 1 + ], + + // --------------------------------------------------------------------- + // directus_collections + [ + 'collection' => 'directus_collections', + 'field' => 'fields', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_O2M, + 'interface' => 'one-to-many', + 'locked' => 1, + 'hidden_detail' => 1, + 'hidden_browse' => 1, + 'sort' => 1 + ], + [ + 'collection' => 'directus_collections', + 'field' => 'collection', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'primary-key', + 'locked' => 1, + 'readonly' => 1, + 'required' => 1, + 'sort' => 2, + 'width' => 'half' + ], + [ + 'collection' => 'directus_collections', + 'field' => 'note', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'sort' => 3, + 'width' => 'half', + 'note' => 'An internal description.' + ], + [ + 'collection' => 'directus_collections', + 'field' => 'managed', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1, + 'sort' => 4, + 'width' => 'half', + 'hidden_detail' => 1, + 'note' => '[Learn More](https://docs.directus.io/guides/collections.html#managing-collections).' + ], + [ + 'collection' => 'directus_collections', + 'field' => 'hidden', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1, + 'sort' => 5, + 'width' => 'half', + 'note' => '[Learn More](https://docs.directus.io/guides/collections.html#hidden).' + ], + [ + 'collection' => 'directus_collections', + 'field' => 'single', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1, + 'sort' => 6, + 'width' => 'half', + 'note' => '[Learn More](https://docs.directus.io/guides/collections.html#single).' + ], + [ + 'collection' => 'directus_collections', + 'field' => 'translation', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'repeater', + 'locked' => 1, + 'sort' => 7, + 'hidden_detail' => 0, + 'options' => '{ + "fields": [ + { + "field": "locale", + "type": "string", + "interface": "language", + "options": { + "limit": true + }, + "width": "half" + }, + { + "field": "translation", + "type": "string", + "interface": "text-input", + "width": "half" + } + ] + }' + ], + [ + 'collection' => 'directus_collections', + 'field' => 'icon', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'icon', + 'locked' => 1, + 'sort' => 8, + 'note' => 'The icon shown in the App\'s navigation sidebar.' + ], + + // --------------------------------------------------------------------- + // directus_files + [ + 'collection' => 'directus_files', + 'field' => 'preview', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ALIAS, + 'interface' => 'file-preview', + 'locked' => 1, + 'sort' => 1, + 'width' => 'full' + ], + [ + 'collection' => 'directus_files', + 'field' => 'title', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'placeholder' => 'Enter a descriptive title...', + 'iconRight' => 'title' + ]), + 'locked' => 1, + 'sort' => 2, + 'width' => 'full' + ], + [ + 'collection' => 'directus_files', + 'field' => 'tags', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ARRAY, + 'interface' => 'tags', + 'options' => json_encode([ + 'placeholder' => 'Enter a keyword then hit enter...' + ]), + 'sort' => 3, + 'width' => 'half', + 'locked' => 1 + ], + [ + 'collection' => 'directus_files', + 'field' => 'location', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'placeholder' => 'Enter a location...', + 'iconRight' => 'place' + ]), + 'sort' => 4, + 'width' => 'half', + 'locked' => 1 + ], + [ + 'collection' => 'directus_files', + 'field' => 'description', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'wysiwyg', + 'options' => json_encode([ + 'toolbar' => ['bold','italic','underline','link','code'] + ]), + 'sort' => 5, + 'width' => 'full', + 'locked' => 1 + ], + [ + 'collection' => 'directus_files', + 'field' => 'filename_download', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'options' => json_encode([ + 'monospace' => true, + 'iconRight' => 'get_app' + ]), + 'sort' => 6, + 'width' => 'full' + ], + [ + 'collection' => 'directus_files', + 'field' => 'filename_disk', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'placeholder' => 'Enter a unique file name...', + 'iconRight' => 'insert_drive_file' + ]), + 'locked' => 1, + 'sort' => 7, + 'width' => 'full' + ], + [ + 'collection' => 'directus_files', + 'field' => 'private_hash', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface'=> 'slug', + 'width' => 'half', + 'locked' => 1, + 'sort' => 8, + 'options' => json_encode([ + 'iconRight' => 'lock' + ]) + ], + [ + 'collection' => 'directus_files', + 'field' => 'checksum', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'readonly' => 1, + 'sort' => 9, + 'width' => 'half', + 'options' => json_encode([ + 'iconRight' => 'check', + 'monospace' => true + ]) + ], + [ + 'collection' => 'directus_files', + 'field' => 'uploaded_on', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_DATETIME, + 'interface' => 'datetime', + 'options' => json_encode([ + 'iconRight' => 'today' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 10, + 'width' => 'half', + 'required' => 1 + ], + [ + 'collection' => 'directus_files', + 'field' => 'uploaded_by', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_USER_CREATED, + 'interface' => 'user-created', + 'locked' => 1, + 'readonly' => 1, + 'sort' => 11, + 'width' => 'half', + 'required' => 1 + ], + [ + 'collection' => 'directus_files', + 'field' => 'width', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'numeric', + 'options' => json_encode([ + 'iconRight' => 'straighten' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 12, + 'width' => 'half' + ], + [ + 'collection' => 'directus_files', + 'field' => 'height', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'numeric', + 'options' => json_encode([ + 'iconRight' => 'straighten' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 13, + 'width' => 'half' + ], + [ + 'collection' => 'directus_files', + 'field' => 'duration', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'numeric', + 'options' => json_encode([ + 'iconRight' => 'timer' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 14, + 'width' => 'half' + ], + [ + 'collection' => 'directus_files', + 'field' => 'filesize', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'file-size', + 'options' => json_encode([ + 'iconRight' => 'storage' + ]), + 'locked' => 1, + 'readonly' => 1, + 'sort' => 15, + 'width' => 'half' + ], + [ + 'collection' => 'directus_files', + 'field' => 'metadata', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'key-value', + 'locked' => 1, + 'sort' => 15, + 'width' => 'full', + 'options' => json_encode([ + 'keyInterface' => 'text-input', + 'keyDataType' => 'string', + 'keyOptions' => [ + 'monospace' => true, + 'placeholder' => 'Key' + ], + 'valueInterface' => 'text-input', + 'valueDataType' => 'string', + 'valueOptions' => [ + 'monospace' => true, + 'placeholder' => 'Value' + ] + ]) + ], + [ + 'collection' => 'directus_files', + 'field' => 'data', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ALIAS, + 'interface' => 'file', + 'locked' => 1, + 'hidden_detail' => 1 + ], + [ + 'collection' => 'directus_files', + 'field' => 'id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'primary-key', + 'locked' => 1, + 'required' => 1, + 'hidden_detail' => 1 + ], + [ + 'collection' => 'directus_files', + 'field' => 'type', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'readonly' => 1, + 'hidden_detail' => 1 + ], + [ + 'collection' => 'directus_files', + 'field' => 'charset', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'readonly' => 1, + 'hidden_detail' => 1, + 'hidden_browse' => 1 + ], + [ + 'collection' => 'directus_files', + 'field' => 'embed', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'readonly' => 1, + 'hidden_detail' => 1 + ], + [ + 'collection' => 'directus_files', + 'field' => 'folder', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, + 'interface' => 'many-to-one', + 'locked' => 1, + 'hidden_detail' => 1 + ], + [ + 'collection' => 'directus_files', + 'field' => 'storage', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'hidden_detail' => 1, + 'hidden_browse' => 1 + ], + + // --------------------------------------------------------------------- + // directus_folders + [ + 'collection' => 'directus_folders', + 'field' => 'id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'primary-key', + 'locked' => 1, + 'required' => 1, + 'hidden_detail' => 1 + ], + [ + 'collection' => 'directus_folders', + 'field' => 'name', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_folders', + 'field' => 'parent_folder', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, + 'interface' => 'many-to-one', + 'locked' => 1 + ], + + // --------------------------------------------------------------------- + // directus_roles + [ + 'collection' => 'directus_roles', + 'field' => 'id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'primary-key', + 'locked' => 1, + 'required' => 1, + 'hidden_detail' => 1 + ], + [ + 'collection' => 'directus_roles', + 'field' => 'external_id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'readonly' => 1, + 'hidden_detail' => 1, + 'hidden_browse' => 1 + ], + [ + 'collection' => 'directus_roles', + 'field' => 'name', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'sort' => 1, + 'width' => 'half', + 'required' => 1 + ], + [ + 'collection' => 'directus_roles', + 'field' => 'description', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'sort' => 2, + 'width' => 'half' + ], + [ + 'collection' => 'directus_roles', + 'field' => 'ip_whitelist', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ARRAY, + 'interface' => 'tags', + 'options' => json_encode([ + '' => 'Add an IP address...' + ]), + 'locked' => 1 + ], + [ + 'collection' => 'directus_roles', + 'field' => 'enforce_2fa', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1 + ], + [ + 'collection' => 'directus_roles', + 'field' => 'users', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_O2M, + 'interface' => 'one-to-many', + 'locked' => 1, + 'options' => json_encode([ + 'fields' => "first_name,last_name" + ]) + ], + [ + 'collection' => 'directus_roles', + 'field' => 'module_listing', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'repeater', + 'locked' => 1, + 'options' => '{ + "fields": [ + { + "field": "name", + "interface": "text-input", + "type": "string", + "width": "half" + }, + { + "field": "link", + "interface": "text-input", + "type": "string", + "width": "half" + }, + { + "field": "icon", + "interface": "icon", + "type": "string", + "width": "full" + } + ] + }' + ], + [ + 'collection' => 'directus_roles', + 'field' => 'collection_listing', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'repeater', + 'locked' => 1, + 'options' => '{ + "fields": [ + { + "field": "groups", + "width": "full", + "interface": "repeater", + "type": "JSON", + "options": { + "template": "{{ label }}", + "fields": [ + { + "field": "label", + "interface": "text-input", + "type": "string" + }, + { + "field": "value", + "interface": "text-input", + "type": "string" + }, + { + "field": "icon", + "width": "full", + "interface": "icon", + "type": "string" + } + ] + } + } + ] + }' + ], + + // --------------------------------------------------------------------- + // directus_permissions + [ + 'collection' => 'directus_permissions', + 'field' => 'id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'primary-key', + 'locked' => 1, + 'required' => 1, + 'hidden_detail' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'collection', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, + 'interface' => 'many-to-one', + 'locked' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'role', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, + 'interface' => 'many-to-one', + 'locked' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'status', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'create', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'read', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'update', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'delete', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'primary-key', + 'locked' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'comment', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'explain', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'status_blacklist', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ARRAY, + 'interface' => 'tags', + 'locked' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'read_field_blacklist', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ARRAY, + 'interface' => 'tags', + 'locked' => 1 + ], + [ + 'collection' => 'directus_permissions', + 'field' => 'write_field_blacklist', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ARRAY, + 'interface' => 'tags', + 'locked' => 1 + ], + + // --------------------------------------------------------------------- + // directus_relations + [ + 'collection' => 'directus_relations', + 'field' => 'id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'primary-key', + 'locked' => 1 + ], + [ + 'collection' => 'directus_relations', + 'field' => 'collection_many', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'collections', + 'locked' => 1 + ], + [ + 'collection' => 'directus_relations', + 'field' => 'field_many', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_relations', + 'field' => 'collection_one', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'collections', + 'locked' => 1 + ], + [ + 'collection' => 'directus_relations', + 'field' => 'field_one', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_relations', + 'field' => 'junction_field', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + + // --------------------------------------------------------------------- + // directus_revisions + [ + 'collection' => 'directus_revisions', + 'field' => 'id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'primary-key', + 'locked' => 1 + ], + [ + 'collection' => 'directus_revisions', + 'field' => 'activity', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, + 'interface' => 'many-to-one', + 'locked' => 1 + ], + [ + 'collection' => 'directus_revisions', + 'field' => 'collection', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, + 'interface' => 'many-to-one', + 'locked' => 1 + ], + [ + 'collection' => 'directus_revisions', + 'field' => 'item', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_revisions', + 'field' => 'data', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'json', + 'locked' => 1 + ], + [ + 'collection' => 'directus_revisions', + 'field' => 'delta', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'json', + 'locked' => 1 + ], + [ + 'collection' => 'directus_revisions', + 'field' => 'parent_item', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1 + ], + [ + 'collection' => 'directus_revisions', + 'field' => 'parent_collection', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'collections', + 'locked' => 1 + ], + [ + 'collection' => 'directus_revisions', + 'field' => 'parent_changed', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1 + ], + + // --------------------------------------------------------------------- + // directus_revisions + [ + 'collection' => 'directus_settings', + 'field' => 'project_name', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'title' + ]), + 'locked' => 1, + 'required' => 1, + 'width' => 'half', + 'note' => 'Logo in the top-left of the App (40x40)', + 'sort' => 1 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'project_url', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'link' + ]), + 'locked' => 1, + 'width' => 'half', + 'note' => 'External link for the App\'s top-left logo', + 'sort' => 2 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'project_logo', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_FILE, + 'interface' => 'file', + 'locked' => 1, + 'width' => 'half', + 'note' => 'A 40x40 brand logo, ideally a white SVG/PNG', + 'sort' => 3 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'project_color', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'color', + 'locked' => 1, + 'width' => 'half', + 'note' => 'Color for login background and App\'s logo', + 'sort' => 4 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'project_foreground', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_FILE, + 'interface' => 'file', + 'locked' => 1, + 'width' => 'half', + 'note' => 'Centered image (eg: logo) for the login page', + 'sort' => 5 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'project_background', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_FILE, + 'interface' => 'file', + 'locked' => 1, + 'width' => 'half', + 'note' => 'Full-screen background for the login page', + 'sort' => 6 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'default_locale', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'language', + 'locked' => 1, + 'width' => 'half', + 'note' => 'Default locale for Directus Users', + 'sort' => 7, + 'options' => json_encode([ + 'limit' => true + ]) + ], + [ + 'collection' => 'directus_settings', + 'field' => 'telemetry', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1, + 'width' => 'half', + 'note' => 'Learn More', + 'sort' => 8 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'data_divider', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ALIAS, + 'interface' => 'divider', + 'options' => json_encode([ + 'style' => 'large', + 'title' => 'Data', + 'hr' => true + ]), + 'locked' => 1, + 'width' => 'full', + 'hidden_browse' => 1, + 'sort' => 10 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'default_limit', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'numeric', + 'options' => json_encode([ + 'iconRight' => 'keyboard_tab' + ]), + 'locked' => 1, + 'required' => 1, + 'width' => 'half', + 'note' => 'Default item count in API and App responses', + 'sort' => 11 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'sort_null_last', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1, + 'note' => 'NULL values are sorted last', + 'width' => 'half', + 'sort' => 12 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'security_divider', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ALIAS, + 'interface' => 'divider', + 'options' => json_encode([ + 'style' => 'large', + 'title' => 'Security', + 'hr' => true + ]), + 'locked' => 1, + 'hidden_browse' => 1, + 'width' => 'full', + 'sort' => 20 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'auto_sign_out', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'numeric', + 'options' => json_encode([ + 'iconRight' => 'timer' + ]), + 'locked' => 1, + 'required' => 1, + 'width' => 'half', + 'note' => 'Minutes before idle users are signed out', + 'sort' => 22 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'login_attempts_allowed', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'numeric', + 'options' => json_encode([ + 'iconRight' => 'lock' + ]), + 'locked' => 1, + 'width' => 'half', + 'note' => 'Failed login attempts before suspending users', + 'sort' => 23 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'password_policy', + 'type' => 'string', + 'note' => 'Weak: Minimum length 8; Strong: 1 small-case letter, 1 capital letter, 1 digit, 1 special character and the length should be minimum 8', + 'interface' => 'dropdown', + 'options' => json_encode([ + 'choices' => [ + '' => 'None', + '/^.{8,}$/' => 'Weak', + '/(?=^.{8,}$)(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*()_+}{\';\'?>.<,])(?!.*\s).*$/' => 'Strong' + ] + ]), + 'sort' => 24, + 'locked' => 1, + 'width' => 'half' + ], + [ + 'collection' => 'directus_settings', + 'field' => 'files_divider', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ALIAS, + 'interface' => 'divider', + 'options' => json_encode([ + 'style' => 'large', + 'title' => 'Files & Thumbnails', + 'hr' => true + ]), + 'locked' => 1, + 'hidden_browse' => 1, + 'width' => 'full', + 'sort' => 30 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'file_naming', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'dropdown', + 'locked' => 1, + 'width' => 'half', + 'note' => 'File-system naming convention for uploads', + 'sort' => 31, + 'options' => json_encode([ + 'choices' => [ + 'uuid' => 'UUID (Obfuscated)', + 'file_name' => 'File Name (Readable)' + ] + ]) + ], + [ + 'collection' => 'directus_settings', + 'field' => 'file_max_size', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'placeholder' => 'eg: 4MB', + 'iconRight' => 'storage' + ]), + 'locked' => 1, + 'width' => 'half', + 'sort' => 32 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'file_mimetype_whitelist', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ARRAY, + 'interface' => 'tags', + 'options' => json_encode([ + 'placeholder' => 'Enter a file mimetype then hit enter (eg: image/jpeg)' + ]), + 'locked' => 1, + 'width' => 'full', + 'sort' => 33 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'asset_whitelist', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'repeater', + 'width' => 'full', + 'note' => 'Defines how the thumbnail will be generated based on the requested params.', + 'sort' => 34, + 'options' => json_encode([ + 'template' => '{{key}}', + 'fields' => [ + [ + 'field' => 'key', + 'interface' => 'slug', + 'width' => 'half', + 'type' => 'string', + 'required' => true + ], + [ + 'field' => 'fit', + 'interface' => 'dropdown', + 'width' => 'half', + 'type' => 'string', + 'options' => [ + 'choices' => [ + 'crop' => 'Crop (forces exact size)', + 'contain' => 'Contain (preserve aspect ratio)' + ] + ], + 'required' => true + ], + [ + 'field' => 'width', + 'interface' => 'numeric', + 'width' => 'half', + 'type' => 'integer', + 'required' => true + ], + [ + 'field' => 'height', + 'interface' => 'numeric', + 'width' => 'half', + 'type' => 'integer', + 'required' => true + ], + [ + 'field' => 'quality', + 'interface' => 'slider', + 'width' => 'full', + 'type' => 'integer', + 'default' => 80, + 'options' => [ + 'min' => 0, + 'max' => 100, + 'step' => 1 + ], + 'required' => true + ] + ] + ]) + ], + [ + 'collection' => 'directus_settings', + 'field' => 'asset_whitelist_system', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'json', + 'readonly' => 1, + 'width' => 'half', + 'hidden_browse' => 1, + 'hidden_detail' => 1, + 'sort' => 35 + ], + [ + 'collection' => 'directus_settings', + 'field' => 'youtube_api_key', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'videocam' + ]), + 'locked' => 1, + 'width' => 'full', + 'note' => 'Allows fetching more YouTube Embed info', + 'sort' => 36 + ], + + // --------------------------------------------------------------------- + // directus_users + [ + 'collection' => 'directus_users', + 'field' => 'id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'primary-key', + 'locked' => 1, + 'required' => 1, + 'hidden_detail' => 1, + 'sort' => 1 + ], + [ + 'collection' => 'directus_users', + 'field' => 'status', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STATUS, + 'interface' => 'status', + 'options' => json_encode([ + 'status_mapping' => [ + 'draft' => [ + 'name' => 'Draft', + 'text_color' => 'white', + 'background_color' => 'light-gray', + 'listing_subdued' => false, + 'listing_badge' => true, + 'soft_delete' => false, + ], + 'invited' => [ + 'name' => 'Invited', + 'text_color' => 'white', + 'background_color' => 'light-gray', + 'listing_subdued' => false, + 'listing_badge' => true, + 'soft_delete' => false, + ], + 'active' => [ + 'name' => 'Active', + 'text_color' => 'white', + 'background_color' => 'success', + 'listing_subdued' => false, + 'listing_badge' => false, + 'soft_delete' => false, + ], + 'suspended' => [ + 'name' => 'Suspended', + 'text_color' => 'white', + 'background_color' => 'light-gray', + 'listing_subdued' => false, + 'listing_badge' => true, + 'soft_delete' => false, + ], + 'deleted' => [ + 'name' => 'Deleted', + 'text_color' => 'white', + 'background_color' => 'danger', + 'listing_subdued' => false, + 'listing_badge' => true, + 'soft_delete' => true, + ] + ] + ]), + 'locked' => 1, + 'sort' => 2, + 'required' => 1 + ], + [ + 'collection' => 'directus_users', + 'field' => 'first_name', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'account_circle' + ]), + 'locked' => 1, + 'required' => 1, + 'sort' => 3, + 'width' => 'half' + ], + [ + 'collection' => 'directus_users', + 'field' => 'last_name', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'account_circle' + ]), + 'locked' => 1, + 'required' => 1, + 'sort' => 4, + 'width' => 'half' + ], + [ + 'collection' => 'directus_users', + 'field' => 'email', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'alternate_email' + ]), + 'locked' => 1, + 'validation' => '$email', + 'required' => 1, + 'sort' => 5, + 'width' => 'half' + ], + [ + 'collection' => 'directus_users', + 'field' => 'email_notifications', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_BOOLEAN, + 'interface' => 'toggle', + 'locked' => 1, + 'sort' => 6, + 'width' => 'half' + ], + [ + 'collection' => 'directus_users', + 'field' => 'password', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_HASH, + 'interface' => 'password', + 'locked' => 1, + 'required' => 1, + 'sort' => 7, + 'width' => 'half' + ], + [ + 'collection' => 'directus_users', + 'field' => 'role', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_M2O, + 'interface' => 'user-roles', + 'locked' => 1, + 'sort' => 8, + 'width' => 'half', + 'required' => 1 + ], + [ + 'collection' => 'directus_users', + 'field' => 'company', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'location_city' + ]), + 'sort' => 9, + 'width' => 'half' + ], + [ + 'collection' => 'directus_users', + 'field' => 'title', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'iconRight' => 'text_fields' + ]), + 'sort' => 10, + 'width' => 'half' + ], + [ + 'collection' => 'directus_users', + 'field' => 'timezone', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'dropdown', + 'options' => json_encode([ + 'choices' => [ + 'Pacific/Midway' => '(UTC-11:00) Midway Island', + 'Pacific/Samoa' => '(UTC-11:00) Samoa', + 'Pacific/Honolulu' => '(UTC-10:00) Hawaii', + 'US/Alaska' => '(UTC-09:00) Alaska', + 'America/Los_Angeles' => '(UTC-08:00) Pacific Time (US & Canada)', + 'America/Tijuana' => '(UTC-08:00) Tijuana', + 'US/Arizona' => '(UTC-07:00) Arizona', + 'America/Chihuahua' => '(UTC-07:00) Chihuahua', + 'America/Mexico/La_Paz' => '(UTC-07:00) La Paz', + 'America/Mazatlan' => '(UTC-07:00) Mazatlan', + 'US/Mountain' => '(UTC-07:00) Mountain Time (US & Canada)', + 'America/Managua' => '(UTC-06:00) Central America', + 'US/Central' => '(UTC-06:00) Central Time (US & Canada)', + 'America/Guadalajara' => '(UTC-06:00) Guadalajara', + 'America/Mexico_City' => '(UTC-06:00) Mexico City', + 'America/Monterrey' => '(UTC-06:00) Monterrey', + 'Canada/Saskatchewan' => '(UTC-06:00) Saskatchewan', + 'America/Bogota' => '(UTC-05:00) Bogota', + 'US/Eastern' => '(UTC-05:00) Eastern Time (US & Canada)', + 'US/East-Indiana' => '(UTC-05:00) Indiana (East)', + 'America/Lima' => '(UTC-05:00) Lima', + 'America/Quito' => '(UTC-05:00) Quito', + 'Canada/Atlantic' => '(UTC-04:00) Atlantic Time (Canada)', + 'America/New_York' => '(UTC-04:00) New York', + 'America/Caracas' => '(UTC-04:30) Caracas', + 'America/La_Paz' => '(UTC-04:00) La Paz', + 'America/Santiago' => '(UTC-04:00) Santiago', + 'America/Santo_Domingo' => '(UTC-04:00) Santo Domingo', + 'Canada/Newfoundland' => '(UTC-03:30) Newfoundland', + 'America/Sao_Paulo' => '(UTC-03:00) Brasilia', + 'America/Argentina/Buenos_Aires' => '(UTC-03:00) Buenos Aires', + 'America/Argentina/GeorgeTown' => '(UTC-03:00) Georgetown', + 'America/Godthab' => '(UTC-03:00) Greenland', + 'America/Noronha' => '(UTC-02:00) Mid-Atlantic', + 'Atlantic/Azores' => '(UTC-01:00) Azores', + 'Atlantic/Cape_Verde' => '(UTC-01:00) Cape Verde Is.', + 'Africa/Casablanca' => '(UTC+00:00) Casablanca', + 'Europe/Edinburgh' => '(UTC+00:00) Edinburgh', + 'Etc/Greenwich' => '(UTC+00:00) Greenwich Mean Time : Dublin', + 'Europe/Lisbon' => '(UTC+00:00) Lisbon', + 'Europe/London' => '(UTC+00:00) London', + 'Africa/Monrovia' => '(UTC+00:00) Monrovia', + 'UTC' => '(UTC+00:00) UTC', + 'Europe/Amsterdam' => '(UTC+01:00) Amsterdam', + 'Europe/Belgrade' => '(UTC+01:00) Belgrade', + 'Europe/Berlin' => '(UTC+01:00) Berlin', + 'Europe/Bern' => '(UTC+01:00) Bern', + 'Europe/Bratislava' => '(UTC+01:00) Bratislava', + 'Europe/Brussels' => '(UTC+01:00) Brussels', + 'Europe/Budapest' => '(UTC+01:00) Budapest', + 'Europe/Copenhagen' => '(UTC+01:00) Copenhagen', + 'Europe/Ljubljana' => '(UTC+01:00) Ljubljana', + 'Europe/Madrid' => '(UTC+01:00) Madrid', + 'Europe/Paris' => '(UTC+01:00) Paris', + 'Europe/Prague' => '(UTC+01:00) Prague', + 'Europe/Rome' => '(UTC+01:00) Rome', + 'Europe/Sarajevo' => '(UTC+01:00) Sarajevo', + 'Europe/Skopje' => '(UTC+01:00) Skopje', + 'Europe/Stockholm' => '(UTC+01:00) Stockholm', + 'Europe/Vienna' => '(UTC+01:00) Vienna', + 'Europe/Warsaw' => '(UTC+01:00) Warsaw', + 'Africa/Lagos' => '(UTC+01:00) West Central Africa', + 'Europe/Zagreb' => '(UTC+01:00) Zagreb', + 'Europe/Athens' => '(UTC+02:00) Athens', + 'Europe/Bucharest' => '(UTC+02:00) Bucharest', + 'Africa/Cairo' => '(UTC+02:00) Cairo', + 'Africa/Harare' => '(UTC+02:00) Harare', + 'Europe/Helsinki' => '(UTC+02:00) Helsinki', + 'Europe/Istanbul' => '(UTC+02:00) Istanbul', + 'Asia/Jerusalem' => '(UTC+02:00) Jerusalem', + 'Europe/Kyiv' => '(UTC+02:00) Kyiv', + 'Africa/Johannesburg' => '(UTC+02:00) Pretoria', + 'Europe/Riga' => '(UTC+02:00) Riga', + 'Europe/Sofia' => '(UTC+02:00) Sofia', + 'Europe/Tallinn' => '(UTC+02:00) Tallinn', + 'Europe/Vilnius' => '(UTC+02:00) Vilnius', + 'Asia/Baghdad' => '(UTC+03:00) Baghdad', + 'Asia/Kuwait' => '(UTC+03:00) Kuwait', + 'Europe/Minsk' => '(UTC+03:00) Minsk', + 'Africa/Nairobi' => '(UTC+03:00) Nairobi', + 'Asia/Riyadh' => '(UTC+03:00) Riyadh', + 'Europe/Volgograd' => '(UTC+03:00) Volgograd', + 'Asia/Tehran' => '(UTC+03:30) Tehran', + 'Asia/Abu_Dhabi' => '(UTC+04:00) Abu Dhabi', + 'Asia/Baku' => '(UTC+04:00) Baku', + 'Europe/Moscow' => '(UTC+04:00) Moscow', + 'Asia/Muscat' => '(UTC+04:00) Muscat', + 'Europe/St_Petersburg' => '(UTC+04:00) St. Petersburg', + 'Asia/Tbilisi' => '(UTC+04:00) Tbilisi', + 'Asia/Yerevan' => '(UTC+04:00) Yerevan', + 'Asia/Kabul' => '(UTC+04:30) Kabul', + 'Asia/Islamabad' => '(UTC+05:00) Islamabad', + 'Asia/Karachi' => '(UTC+05:00) Karachi', + 'Asia/Tashkent' => '(UTC+05:00) Tashkent', + 'Asia/Calcutta' => '(UTC+05:30) Chennai', + 'Asia/Kolkata' => '(UTC+05:30) Kolkata', + 'Asia/Mumbai' => '(UTC+05:30) Mumbai', + 'Asia/New_Delhi' => '(UTC+05:30) New Delhi', + 'Asia/Sri_Jayawardenepura' => '(UTC+05:30) Sri Jayawardenepura', + 'Asia/Katmandu' => '(UTC+05:45) Kathmandu', + 'Asia/Almaty' => '(UTC+06:00) Almaty', + 'Asia/Astana' => '(UTC+06:00) Astana', + 'Asia/Dhaka' => '(UTC+06:00) Dhaka', + 'Asia/Yekaterinburg' => '(UTC+06:00) Ekaterinburg', + 'Asia/Rangoon' => '(UTC+06:30) Rangoon', + 'Asia/Bangkok' => '(UTC+07:00) Bangkok', + 'Asia/Hanoi' => '(UTC+07:00) Hanoi', + 'Asia/Jakarta' => '(UTC+07:00) Jakarta', + 'Asia/Novosibirsk' => '(UTC+07:00) Novosibirsk', + 'Asia/Beijing' => '(UTC+08:00) Beijing', + 'Asia/Chongqing' => '(UTC+08:00) Chongqing', + 'Asia/Hong_Kong' => '(UTC+08:00) Hong Kong', + 'Asia/Krasnoyarsk' => '(UTC+08:00) Krasnoyarsk', + 'Asia/Kuala_Lumpur' => '(UTC+08:00) Kuala Lumpur', + 'Australia/Perth' => '(UTC+08:00) Perth', + 'Asia/Singapore' => '(UTC+08:00) Singapore', + 'Asia/Taipei' => '(UTC+08:00) Taipei', + 'Asia/Ulan_Bator' => '(UTC+08:00) Ulaan Bataar', + 'Asia/Urumqi' => '(UTC+08:00) Urumqi', + 'Asia/Irkutsk' => '(UTC+09:00) Irkutsk', + 'Asia/Osaka' => '(UTC+09:00) Osaka', + 'Asia/Sapporo' => '(UTC+09:00) Sapporo', + 'Asia/Seoul' => '(UTC+09:00) Seoul', + 'Asia/Tokyo' => '(UTC+09:00) Tokyo', + 'Australia/Adelaide' => '(UTC+09:30) Adelaide', + 'Australia/Darwin' => '(UTC+09:30) Darwin', + 'Australia/Brisbane' => '(UTC+10:00) Brisbane', + 'Australia/Canberra' => '(UTC+10:00) Canberra', + 'Pacific/Guam' => '(UTC+10:00) Guam', + 'Australia/Hobart' => '(UTC+10:00) Hobart', + 'Australia/Melbourne' => '(UTC+10:00) Melbourne', + 'Pacific/Port_Moresby' => '(UTC+10:00) Port Moresby', + 'Australia/Sydney' => '(UTC+10:00) Sydney', + 'Asia/Yakutsk' => '(UTC+10:00) Yakutsk', + 'Asia/Vladivostok' => '(UTC+11:00) Vladivostok', + 'Pacific/Auckland' => '(UTC+12:00) Auckland', + 'Pacific/Fiji' => '(UTC+12:00) Fiji', + 'Pacific/Kwajalein' => '(UTC+12:00) International Date Line West', + 'Asia/Kamchatka' => '(UTC+12:00) Kamchatka', + 'Asia/Magadan' => '(UTC+12:00) Magadan', + 'Pacific/Marshall_Is' => '(UTC+12:00) Marshall Is.', + 'Asia/New_Caledonia' => '(UTC+12:00) New Caledonia', + 'Asia/Solomon_Is' => '(UTC+12:00) Solomon Is.', + 'Pacific/Wellington' => '(UTC+12:00) Wellington', + 'Pacific/Tongatapu' => '(UTC+13:00) Nuku\'alofa' + ], + 'placeholder' => 'Choose a timezone...' + ]), + 'locked' => 1, + 'sort' => 11, + 'width' => 'half', + 'required' => 1 + ], + [ + 'collection' => 'directus_users', + 'field' => 'locale', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'language', + 'options' => json_encode([ + 'limit' => true + ]), + 'locked' => 1, + 'sort' => 12, + 'width' => 'half', + 'required' => 0 + ], + [ + 'collection' => 'directus_users', + 'field' => 'avatar', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_FILE, + 'interface' => 'file', + 'locked' => 1, + 'sort' => 13 + ], + [ + 'collection' => 'directus_users', + 'field' => 'theme', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'radio-buttons', + 'options' => json_encode([ + 'format' => true, + 'choices' => [ + 'auto' => 'Auto', + 'light' => 'Light', + 'dark' => 'Dark' + ] + ]), + 'locked' => 1, + 'readonly' => 0, + 'sort' => 14 + ], + [ + 'collection' => 'directus_users', + 'field' => '2fa_secret', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => '2fa-secret', + 'locked' => 1, + 'readonly' => 1, + 'sort' => 15 + ], + [ + 'collection' => 'directus_users', + 'field' => 'locale_options', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_JSON, + 'interface' => 'json', + 'locked' => 1, + 'hidden_browse' => 1, + 'hidden_detail' => 1, + 'sort' => 16 + ], + [ + 'collection' => 'directus_users', + 'field' => 'token', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'hidden_detail' => 1, + 'hidden_browse' => 1, + 'sort' => 17 + ], + [ + 'collection' => 'directus_users', + 'field' => 'last_access_on', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_DATETIME, + 'interface' => 'datetime', + 'locked' => 1, + 'readonly' => 1, + 'hidden_detail' => 1, + 'sort' => 18 + ], + [ + 'collection' => 'directus_users', + 'field' => 'last_page', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'readonly' => 1, + 'hidden_detail' => 1, + 'hidden_browse' => 1, + 'sort' => 19 + ], + [ + 'collection' => 'directus_users', + 'field' => 'external_id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'locked' => 1, + 'readonly' => 1, + 'hidden_detail' => 20 + ], + + // --------------------------------------------------------------------- + // directus_user_sessions + [ + 'collection' => 'directus_user_sessions', + 'field' => 'id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'primary-key', + 'locked' => 1, + 'required' => 1, + 'hidden_detail' => 1 + ], + [ + 'collection' => 'directus_user_sessions', + 'field' => 'user', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_USER, + 'required' => 1, + 'interface' => 'user' + ], + [ + 'collection' => 'directus_user_sessions', + 'field' => 'token_type', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input' + ], + [ + 'collection' => 'directus_user_sessions', + 'field' => 'token', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input' + ], + [ + 'collection' => 'directus_user_sessions', + 'field' => 'ip_address', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input' + ], + [ + 'collection' => 'directus_user_sessions', + 'field' => 'user_agent', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input' + ], + [ + 'collection' => 'directus_user_sessions', + 'field' => 'created_on', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_DATETIME, + 'interface' => 'datetime' + ], + [ + 'collection' => 'directus_user_sessions', + 'field' => 'token_expired_at', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_DATETIME, + 'interface' => 'datetime' + ], + + // --------------------------------------------------------------------- + // directus_webhooks + [ + 'collection' => 'directus_webhooks', + 'field' => 'id', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_INTEGER, + 'interface' => 'primary-key', + 'locked' => 1, + 'required' => 1, + 'hidden_detail' => 1 + ], + [ + 'collection' => 'directus_webhooks', + 'field' => 'status', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STATUS, + 'interface' => 'status', + 'options' => json_encode([ + 'status_mapping' => [ + 'active' => [ + 'name' => 'Active', + 'value' => 'active', + 'text_color' => 'white', + 'background_color' => 'green', + 'browse_subdued' => false, + 'browse_badge' => true, + 'soft_delete' => false, + 'published' => true, + ], + 'inactive' => [ + 'name' => 'Inactive', + 'value' => 'inactive', + 'text_color' => 'white', + 'background_color' => 'blue-grey', + 'browse_subdued' => true, + 'browse_badge' => true, + 'soft_delete' => false, + 'published' => false, + ] + ] + ]), + 'locked' => 1, + 'width' => 'full', + 'sort' => 1 + ], + [ + 'collection' => 'directus_webhooks', + 'field' => 'http_action', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'dropdown', + 'required' => 1, + 'options' => json_encode([ + 'choices' => [ + 'get' => 'GET', + 'post' => 'POST' + ] + ]), + 'locked' => 1, + 'width' => 'half-space', + 'sort' => 2 + ], + [ + 'collection' => 'directus_webhooks', + 'field' => 'url', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'text-input', + 'options' => json_encode([ + 'placeholder' => 'https://example.com', + 'iconRight' => 'link' + ]), + 'required' => 1, + 'locked' => 1, + 'width' => 'full', + 'note' => '', + 'sort' => 3 + ], + [ + 'collection' => 'directus_webhooks', + 'field' => 'collection', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'collections', + 'required' => 1, + 'locked' => 1, + 'width' => 'half', + 'note' => '', + 'sort' => 4 + ], + [ + 'collection' => 'directus_webhooks', + 'field' => 'directus_action', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'dropdown', + 'required' => 1, + 'options' => json_encode([ + 'choices' => [ + 'item.create:after' => 'Create', + 'item.update:after' => 'Update', + 'item.delete:after' => 'Delete', + ] + ]), + 'locked' => 1, + 'width' => 'half', + 'note' => '', + 'sort' => 5 + ], + [ + 'collection' => 'directus_webhooks', + 'field' => 'info', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_ALIAS, + 'interface' => 'divider', + 'options' => json_encode([ + 'style' => 'medium', + 'title' => 'How Webhooks Work', + 'hr' => true, + 'margin' => false, + 'description' => 'When the selected action occurs for the selected collection, Directus will send an HTTP request to the above URL.' + ]), + 'locked' => 1, + 'width' => 'full', + 'hidden_browse' => 1, + 'sort' => 6 + ], + ]; + + // ------------------------------------------------------------------------- + // Add the data to the table + // ------------------------------------------------------------------------- + $fieldsTable->insert($data); + + // ------------------------------------------------------------------------- + // Save changes to table + // ------------------------------------------------------------------------- + $fieldsTable->save(); + } +} diff --git a/migrations/upgrades/20191203105400_add_hash_filedownload_if_empty.php b/migrations/upgrades/20191203105400_add_hash_filedownload_if_empty.php new file mode 100644 index 0000000000..a9a256e706 --- /dev/null +++ b/migrations/upgrades/20191203105400_add_hash_filedownload_if_empty.php @@ -0,0 +1,44 @@ +table('directus_files'); + + // ------------------------------------------------------------------------- + // Add a private hash for all existing files + // ------------------------------------------------------------------------- + $filesWithoutPrivateHash = $this->fetchAll('SELECT id FROM directus_files WHERE private_hash IS NULL OR private_hash = "";'); + + foreach($filesWithoutPrivateHash as $key => $value) { + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_files', + ['private_hash' => get_random_string()], + ['id' => $value['id']] + )); + } + + // ------------------------------------------------------------------------- + // Add a private hash for all existing files + // ------------------------------------------------------------------------- + $filesWithoutFiledownload = $this->fetchAll('SELECT id, filename_disk FROM directus_files WHERE filename_download IS NULL OR filename_download = "";'); + + foreach($filesWithoutFiledownload as $key => $value) { + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_files', + ['filename_download' => $value['filename_disk']], + ['id' => $value['id']] + )); + } + + // ------------------------------------------------------------------------- + // Save changes to table + // ------------------------------------------------------------------------- + $filesTable->save(); + } +} diff --git a/migrations/upgrades/20191204151300_use_correct_wysiwyg.php b/migrations/upgrades/20191204151300_use_correct_wysiwyg.php new file mode 100644 index 0000000000..b38419454f --- /dev/null +++ b/migrations/upgrades/20191204151300_use_correct_wysiwyg.php @@ -0,0 +1,24 @@ +execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + ['interface' => 'wysiwyg'], + ['interface' => 'wysiwyg-advanced'] + )); + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + ['interface' => 'wysiwyg'], + ['interface' => 'wysiwyg-full'] + )); + } +} diff --git a/migrations/upgrades/20191205150000_update_directus_thumbnail_sizes.php b/migrations/upgrades/20191205150000_update_directus_thumbnail_sizes.php new file mode 100644 index 0000000000..b1e2891c4d --- /dev/null +++ b/migrations/upgrades/20191205150000_update_directus_thumbnail_sizes.php @@ -0,0 +1,61 @@ +execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_settings', + ['value' => json_encode([ + [ + 'key' => 'directus-small-crop', + 'width' => 64, + 'height' => 64, + 'fit' => 'crop', + 'quality' => 80 + ], + [ + 'key' => 'directus-small-contain', + 'width' => 64, + 'height' => 64, + 'fit' => 'contain', + 'quality' => 80 + ], + [ + 'key' => 'directus-medium-crop', + 'width' => 300, + 'height' => 300, + 'fit' => 'crop', + 'quality' => 80 + ], + [ + 'key' => 'directus-medium-contain', + 'width' => 300, + 'height' => 300, + 'fit' => 'contain', + 'quality' => 80 + ], + [ + 'key' => 'directus-large-crop', + 'width' => 800, + 'height' => 600, + 'fit' => 'crop', + 'quality' => 80 + ], + [ + 'key' => 'directus-large-contain', + 'width' => 800, + 'height' => 600, + 'fit' => 'contain', + 'quality' => 80 + ] + ])], + ['key' => 'asset_whitelist_system'] + )); + } +} diff --git a/migrations/upgrades/20191209130300_add_project_public_note.php b/migrations/upgrades/20191209130300_add_project_public_note.php new file mode 100644 index 0000000000..86e14a38e0 --- /dev/null +++ b/migrations/upgrades/20191209130300_add_project_public_note.php @@ -0,0 +1,82 @@ +table('directus_fields'); + $settingsTable = $this->table('directus_settings'); + + $exists = $this->fetchRow('SELECT `key` FROM directus_settings WHERE `key` = "project_public_note";'); + + if ($exists !== false) return; + + $settingsTable->insert([ + 'key' => 'project_public_note', + 'value' => '' + ]); + + $fieldsTable->insert([ + 'collection' => 'directus_settings', + 'field' => 'project_public_note', + 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, + 'interface' => 'markdown', + 'locked' => 1, + 'width' => 'full', + 'note' => 'This value will be shown on the public pages of the app', + 'sort' => 7 + ]); + + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + [ + 'sort' => 8 + ], + ['collection' => 'directus_settings', 'field' => 'default_locale'] + )); + + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + [ + 'sort' => 9 + ], + ['collection' => 'directus_settings', 'field' => 'telemetry'] + )); + + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + [ + 'sort' => 10 + ], + ['collection' => 'directus_settings', 'field' => 'data_divider'] + )); + + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + [ + 'sort' => 11 + ], + ['collection' => 'directus_settings', 'field' => 'default_limit'] + )); + + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + [ + 'sort' => 12 + ], + ['collection' => 'directus_settings', 'field' => 'sort_null_last'] + )); + + $fieldsTable->save(); + $settingsTable->save(); + } +} diff --git a/migrations/upgrades/20191209141700_set_options_collection_listing.php b/migrations/upgrades/20191209141700_set_options_collection_listing.php new file mode 100644 index 0000000000..ce9c3a8ab3 --- /dev/null +++ b/migrations/upgrades/20191209141700_set_options_collection_listing.php @@ -0,0 +1,29 @@ +execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + [ + 'options' => '{"template":"{{ name }}","createItemText":"Add Module","fields":[{"field":"name","interface":"text-input","type":"string","width":"half"},{"field":"link","interface":"text-input","type":"string","width":"half"},{"field":"icon","interface":"icon","type":"string","width":"full"}]}' + ], + ['collection' => 'directus_roles', 'field' => 'module_listing'] + )); + + $this->execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + [ + 'options' => '{"template":"{{ group_name }}","createItemText":"Add Group","fields":[{"field":"group_name","width":"full","interface":"text-input","type":"string"},{"field":"collections","interface":"repeater","type":"JSON","options":{"createItemText":"Add Collection","fields":[{"field":"collection","type":"string","interface":"collections","width":"full"}]}}]}' + ], + ['collection' => 'directus_roles', 'field' => 'collection_listing'] + )); + } +} diff --git a/migrations/upgrades/20191211140300_allow_null_in_settings.php b/migrations/upgrades/20191211140300_allow_null_in_settings.php new file mode 100644 index 0000000000..82f150c425 --- /dev/null +++ b/migrations/upgrades/20191211140300_allow_null_in_settings.php @@ -0,0 +1,14 @@ +table('directus_settings'); + $settingsTable->changeColumn('value', 'text', [ + 'null' => true + ]); + $settingsTable->save(); + } +} diff --git a/migrations/upgrades/20191211141600_hide_externalid.php b/migrations/upgrades/20191211141600_hide_externalid.php new file mode 100644 index 0000000000..f57534b14f --- /dev/null +++ b/migrations/upgrades/20191211141600_hide_externalid.php @@ -0,0 +1,19 @@ +execute(\Directus\phinx_update( + $this->getAdapter(), + 'directus_fields', + [ + 'hidden_detail' => 1, + 'hidden_browse' => 1 + ], + ['collection' => 'directus_users', 'field' => 'external_id'] + )); + + } +} diff --git a/migrations/upgrades/schemas/20190914095509_update_directus_user_sessions.php b/migrations/upgrades/schemas/20190914095509_update_directus_user_sessions.php deleted file mode 100644 index 35c826317a..0000000000 --- a/migrations/upgrades/schemas/20190914095509_update_directus_user_sessions.php +++ /dev/null @@ -1,48 +0,0 @@ -table('directus_user_sessions'); - if (!$table->hasColumn('token_type')) { - $table->addColumn('token_type', 'string', [ - 'null' => true, - 'default' => null - ]); - } - - if (!$table->hasColumn('token_expired_at')) { - $table->addColumn('token_expired_at', 'datetime', [ - 'null' => true, - 'default' => null - ]); - } - - $table->save(); - } -} diff --git a/migrations/upgrades/schemas/20191001092213_add_project_settings.php b/migrations/upgrades/schemas/20191001092213_add_project_settings.php deleted file mode 100644 index ffe9e0005c..0000000000 --- a/migrations/upgrades/schemas/20191001092213_add_project_settings.php +++ /dev/null @@ -1,61 +0,0 @@ -execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'width' => 'half' - ], - ['collection' => 'directus_settings', 'field' => 'file_naming'] - )); - - // Update width of file nameing option - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'width' => 'half' - ], - ['collection' => 'directus_settings', 'field' => 'login_attempts_allowed'] - )); - - - $settings = [ - 'project_icon' => [ - 'interface' => 'icon', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_STRING, - 'width' => 'half', - ], - 'project_image' => [ - 'interface' => 'file', - 'width' => 'half', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_FILE, - ] - ]; - foreach ($settings as $field => $options) { - $this->addField($field, $options); - } - } - - protected function addField($field, $options) - { - $collection = 'directus_settings'; - $checkSql = sprintf('SELECT 1 FROM `directus_fields` WHERE `collection` = "%s" AND `field` = "%s";', $collection, $field); - $result = $this->query($checkSql)->fetch(); - - if (!$result) { - $insertSqlFormat = 'INSERT INTO `directus_fields` (`collection`, `field`, `type`, `interface`,`width`) VALUES ("%s", "%s", "%s", "%s", "%s");'; - $insertSql = sprintf($insertSqlFormat, $collection, $field, $options['type'], $options['interface'], $options['width']); - $this->execute($insertSql); - } - } -} diff --git a/migrations/upgrades/schemas/20191002070945_rename_settings.php b/migrations/upgrades/schemas/20191002070945_rename_settings.php deleted file mode 100644 index 0e3c103d3c..0000000000 --- a/migrations/upgrades/schemas/20191002070945_rename_settings.php +++ /dev/null @@ -1,31 +0,0 @@ -execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'field' => 'project_color' - ], - ['collection' => 'directus_settings', 'field' => 'color'] - )); - - // Update the key in directus_settings collection - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_settings', - [ - 'key' => 'project_color' - ], - ['key' => 'color'] - )); - } -} diff --git a/migrations/upgrades/schemas/20191007113144_update_type_for_uploaded_by_directus_files.php b/migrations/upgrades/schemas/20191007113144_update_type_for_uploaded_by_directus_files.php deleted file mode 100644 index 40847ce4a7..0000000000 --- a/migrations/upgrades/schemas/20191007113144_update_type_for_uploaded_by_directus_files.php +++ /dev/null @@ -1,20 +0,0 @@ -execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'type' => \Directus\Database\Schema\DataTypes::TYPE_USER_CREATED, - 'interface' => 'user-created' - ], - ['collection' => 'directus_files', 'field' => 'uploaded_by'] - )); - } -} diff --git a/migrations/upgrades/schemas/20191105063515_update_general_setting_variabe_name.php b/migrations/upgrades/schemas/20191105063515_update_general_setting_variabe_name.php deleted file mode 100644 index 695757ccb1..0000000000 --- a/migrations/upgrades/schemas/20191105063515_update_general_setting_variabe_name.php +++ /dev/null @@ -1,100 +0,0 @@ -execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'field' => 'project_logo' - ], - ['collection' => 'directus_settings', 'field' => 'logo'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_settings', - [ - 'key' => 'project_logo' - ], - ['key' => 'logo'] - )); - - // Update the interface of project_icon from icon to file and rename it. - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'field' => 'project_foreground', - 'type' => \Directus\Database\Schema\DataTypes::TYPE_FILE, - 'interface' => 'file' - ], - ['collection' => 'directus_settings', 'field' => 'project_icon'] - )); - - // Need to delete the project_icon as this migration will change the interface to file and the icon will contain the string - $result = $this->query('SELECT 1 FROM `directus_settings` WHERE `key` = "project_icon";')->fetch(); - - if ($result) { - $this->execute('DELETE FROM `directus_settings` where `key` = "project_icon";'); - } - - // Rename project_image - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'field' => 'project_background' - ], - ['collection' => 'directus_settings', 'field' => 'project_image'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_settings', - [ - 'key' => 'project_background' - ], - ['key' => 'project_image'] - )); - - $result = $this->query('SELECT 1 FROM `directus_fields` WHERE `field` = "app_url";')->fetch(); - - if ($result) { - $this->execute('DELETE FROM `directus_fields` where `field` = "app_url";'); - } - - $result = $this->query('SELECT 1 FROM `directus_settings` WHERE `key` = "app_url";')->fetch(); - - if ($result) { - $this->execute('DELETE FROM `directus_settings` where `key` = "app_url";'); - } - } -} diff --git a/migrations/upgrades/schemas/20191112161523_add_locale_and_telemetry_to_settings_table.php b/migrations/upgrades/schemas/20191112161523_add_locale_and_telemetry_to_settings_table.php deleted file mode 100644 index 04eae23283..0000000000 --- a/migrations/upgrades/schemas/20191112161523_add_locale_and_telemetry_to_settings_table.php +++ /dev/null @@ -1,32 +0,0 @@ - [ - 'type' => 'string', - 'interface' => 'language' - ], - 'telemetry' => [ - 'type' => 'boolean', - 'interface' => 'toggle' - ] - ]; - foreach ($settings as $field => $options) { - $this->addField($field, $options['type'], $options['interface']); - } - } - protected function addField($field, $type, $interface) - { - $collection = 'directus_settings'; - $checkSql = sprintf('SELECT 1 FROM `directus_fields` WHERE `collection` = "%s" AND `field` = "%s";', $collection, $field); - $result = $this->query($checkSql)->fetch(); - if (!$result) { - $insertSqlFormat = 'INSERT INTO `directus_fields` (`collection`, `field`, `type`, `interface`) VALUES ("%s", "%s", "%s", "%s");'; - $insertSql = sprintf($insertSqlFormat, $collection, $field, $type, $interface); - $this->execute($insertSql); - } - } -} \ No newline at end of file diff --git a/migrations/upgrades/schemas/20191112175432_remove_activity_seen_fields.php b/migrations/upgrades/schemas/20191112175432_remove_activity_seen_fields.php deleted file mode 100644 index 5ed8e97d47..0000000000 --- a/migrations/upgrades/schemas/20191112175432_remove_activity_seen_fields.php +++ /dev/null @@ -1,48 +0,0 @@ -query('SELECT 1 FROM `directus_fields` WHERE `collection` = "directus_activity_seen"')->fetch(); - - if ($result) { - $this->execute('DELETE FROM `directus_fields` WHERE `collection` = "directus_activity_seen"'); - } - - $result = $this->query('SELECT 1 FROM `directus_relations` WHERE `collection_many` = "directus_activity_seen"')->fetch(); - - if ($result) { - $this->execute('DELETE FROM `directus_relations` WHERE `collection_many` = "directus_activity_seen"'); - } - - - $this->execute('DROP TABLE IF EXISTS directus_activity_seen'); - - } -} diff --git a/migrations/upgrades/schemas/20191113083711_update_current_migrations.php b/migrations/upgrades/schemas/20191113083711_update_current_migrations.php deleted file mode 100644 index 9e028af84e..0000000000 --- a/migrations/upgrades/schemas/20191113083711_update_current_migrations.php +++ /dev/null @@ -1,665 +0,0 @@ -getContainer()->get('config'); - $dbName = $config->get('database.name'); - - - // Fields Table - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'iconRight' => 'change_history' - ]), - 'width' => 'full' - ], - ['collection' => 'directus_activity', 'field' => 'action'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'iconRight' => 'list_alt' - ]) - ], - ['collection' => 'directus_activity', 'field' => 'collection'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'iconRight' => 'link' - ]) - ], - ['collection' => 'directus_activity', 'field' => 'item'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'iconRight' => 'account_circle' - ]) - ], - ['collection' => 'directus_activity', 'field' => 'action_by'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'showRelative' => true, - 'iconRight' => 'calendar_today' - ]) - ], - ['collection' => 'directus_activity', 'field' => 'action_on'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'showRelative' => true, - 'iconRight' => 'edit' - ]) - ], - ['collection' => 'directus_activity', 'field' => 'edited_on'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'showRelative' => true, - 'iconRight' => 'delete_outline' - ]) - ], - ['collection' => 'directus_activity', 'field' => 'comment_deleted_on'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'iconRight' => 'my_location' - ]) - ], - ['collection' => 'directus_activity', 'field' => 'ip'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'iconRight' => 'devices_other' - ]), - 'width' => 'full' - ], - ['collection' => 'directus_activity', 'field' => 'user_agent'] - )); - - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'placeholder' => 'Enter a keyword then hit enter...' - ]), - ], - ['collection' => 'directus_files', 'field' => 'tags'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'toolbar' => ['bold','italic','underline','link','code'] - ]), - ], - ['collection' => 'directus_files', 'field' => 'description'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'width' => 'full' - ], - ['collection' => 'directus_files', 'field' => 'metadata'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'iconRight' => 'title' - ]), - 'width' => 'half', - 'note' => 'Logo in the top-left of the App (40x40)', - ], - ['collection' => 'directus_settings', 'field' => 'project_name'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'note' => 'A 40x40 brand logo, ideally a white SVG/PNG', - ], - ['collection' => 'directus_settings', 'field' => 'project_logo'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'iconRight' => 'keyboard_tab' - ]), - 'note' => 'Default item count in API and App responses', - 'sort' => 11 - ], - ['collection' => 'directus_settings', 'field' => 'default_limit'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'note' => 'NULL values are sorted last', - ], - ['collection' => 'directus_settings', 'field' => 'sort_null_last'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'iconRight' => 'timer' - ]), - 'note' => 'Minutes before idle users are signed out', - 'sort' => 22 - ], - ['collection' => 'directus_settings', 'field' => 'auto_sign_out'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'placeholder' => 'Allowed dimensions for thumbnails (eg: 200x200)' - ]), - 'width' => 'full', - 'sort' => 34 - ], - ['collection' => 'directus_settings', 'field' => 'thumbnail_dimensions'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'note' => 'Allowed qualities for thumbnails', - 'sort' => 35 - ], - ['collection' => 'directus_settings', 'field' => 'thumbnail_quality_tags'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'note' => 'Defines how the thumbnail will be generated based on the requested dimensions', - 'sort' => 36 - ], - ['collection' => 'directus_settings', 'field' => 'thumbnail_actions'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'iconRight' => 'broken_image' - ]), - 'locked' => 1, - 'width' => 'full', - 'note' => 'A fallback image used when thumbnail generation fails', - 'sort' => 37 - ], - ['collection' => 'directus_settings', 'field' => 'thumbnail_not_found_location'] - )); - - $result = $this->query('SELECT 1 FROM `directus_fields` WHERE `collection` = "directus_settings" and `field` = "thumbnail_cache_ttl";')->fetch(); - - if ($result) { - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'iconRight' => 'cached' - ]), - 'required' => 1, - 'note' => 'Seconds before browsers re-fetch thumbnails', - 'sort' => 38 - ], - ['collection' => 'directus_settings', 'field' => 'thumbnail_cache_ttl'] - )); - } - - $result = $this->query('SELECT 1 FROM `directus_fields` WHERE `collection` = "directus_settings" and `field` = "youtube_api";')->fetch(); - - if ($result) { - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'iconRight' => 'videocam' - ]), - 'width' => 'half', - 'note' => 'Allows fetching more YouTube Embed info', - 'sort' => 39 - ], - ['collection' => 'directus_settings', 'field' => 'youtube_api'] - )); - } - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'required' => 0 - ], - ['collection' => 'directus_users', 'field' => 'locale'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'iconRight' => 'account_circle' - ]), - ], - ['collection' => 'directus_users', 'field' => 'first_name'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'iconRight' => 'account_circle' - ]), - ], - ['collection' => 'directus_users', 'field' => 'last_name'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'iconRight' => 'alternate_email' - ]), - ], - ['collection' => 'directus_users', 'field' => 'email'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'iconRight' => 'location_city' - ]), - ], - ['collection' => 'directus_users', 'field' => 'company'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'iconRight' => 'text_fields' - ]), - ], - ['collection' => 'directus_users', 'field' => 'title'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'sort' => 15 - ], - ['collection' => 'directus_users', 'field' => 'locale_options'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'sort' => 16 - ], - ['collection' => 'directus_users', 'field' => 'token'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'sort' => 17 - ], - ['collection' => 'directus_users', 'field' => 'last_access_on'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'sort' => 18 - ], - ['collection' => 'directus_users', 'field' => 'last_page'] - )); - - - $result = $this->query('SELECT 1 FROM `directus_fields` WHERE `collection` = "directus_users" and `field` = "last_login";')->fetch(); - - if ($result) { - $this->execute('DELETE FROM `directus_fields` where `collection` = "directus_users" and `field` = "last_login";'); - } - - $result = $this->query('SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = "'.$dbName.'" AND TABLE_NAME = "directus_users" AND COLUMN_NAME = "last_login"')->fetch(); - - if ($result) { - $this->execute('ALTER TABLE `directus_users` DROP last_login;'); - } - - $result = $this->query('SELECT 1 FROM `directus_fields` WHERE `collection` = "directus_users" and `field` = "invite_token";')->fetch(); - - if ($result) { - $this->execute('DELETE FROM `directus_fields` where `collection` = "directus_users" and `field` = "invite_token";'); - } - - $result = $this->query('SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = "'.$dbName.'" AND TABLE_NAME = "directus_users" AND COLUMN_NAME = "invite_token"')->fetch(); - - if ($result) { - $this->execute('ALTER TABLE `directus_users` DROP invite_token;'); - } - - - $result = $this->query('SELECT 1 FROM `directus_fields` WHERE `collection` = "directus_users" and `field` = "invite_accepted";')->fetch(); - - if ($result) { - $this->execute('DELETE FROM `directus_fields` where `collection` = "directus_users" and `field` = "invite_accepted";'); - } - - $result = $this->query('SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = "'.$dbName.'" AND TABLE_NAME = "directus_users" AND COLUMN_NAME = "invite_accepted"')->fetch(); - - if ($result) { - $this->execute('ALTER TABLE `directus_users` DROP invite_accepted;'); - } - - - $result = $this->query('SELECT 1 FROM `directus_fields` WHERE `collection` = "directus_users" and `field` = "last_ip";')->fetch(); - - if ($result) { - $this->execute('DELETE FROM `directus_fields` where `collection` = "directus_users" and `field` = "last_ip";'); - } - - $result = $this->query('SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = "'.$dbName.'" AND TABLE_NAME = "directus_users" AND COLUMN_NAME = "last_ip"')->fetch(); - - if ($result) { - $this->execute('ALTER TABLE `directus_users` DROP last_ip;'); - } - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'limit' => true - ]), - 'width' => 'half', - 'note' => 'Default locale for Directus Users', - 'sort' => 7 - ], - ['collection' => 'directus_settings', 'field' => 'default_locale'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'hidden_browse' => 1, - ], - ['collection' => 'directus_settings', 'field' => 'data_divider'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'hidden_browse' => 1, - ], - ['collection' => 'directus_settings', 'field' => 'security_divider'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'hidden_browse' => 1, - ], - ['collection' => 'directus_settings', 'field' => 'files_divider'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'hidden_browse' => 1, - ], - ['collection' => 'directus_webhooks', 'field' => 'info'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'width' => 'half', - 'note' => 'Learn More', - 'sort' => 8 - ], - ['collection' => 'directus_settings', 'field' => 'telemetry'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'locked' => 1, - 'width' => 'full', - 'sort' => 1 - ], - ['collection' => 'directus_webhooks', 'field' => 'status'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'locked' => 1, - 'width' => 'half-space', - 'sort' => 2 - ], - ['collection' => 'directus_webhooks', 'field' => 'http_action'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'placeholder' => 'https://example.com', - 'iconRight' => 'link' - ]), - 'locked' => 1, - 'width' => 'full', - 'note' => '', - 'sort' => 3 - ], - ['collection' => 'directus_webhooks', 'field' => 'url'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'interface' => 'collections', - 'locked' => 1, - 'width' => 'half', - 'note' => '', - 'sort' => 4 - ], - ['collection' => 'directus_webhooks', 'field' => 'collection'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'locked' => 1, - 'width' => 'half', - 'note' => '', - 'sort' => 5 - ], - ['collection' => 'directus_webhooks', 'field' => 'directus_action'] - )); - - $result = $this->query('SELECT 1 FROM `directus_fields` WHERE `collection` = "directus_webhooks" and `field` = "info";')->fetch(); - - if (!$result) { - $options = json_encode([ - 'style' => 'medium', - 'title' => 'How Webhooks Work', - 'hr' => true, - 'margin' => false, - 'description' => 'When the selected action occurs for the selected collection, Directus will send an HTTP request to the above URL.' - ]); - - $this->execute("INSERT INTO `directus_fields` (`collection`, `field`, `type`, `interface`,`options`, `locked`, `width`, `sort`) VALUES ('directus_webhooks', 'info', '".\Directus\Database\Schema\DataTypes::TYPE_ALIAS."', 'divider', '".$options."', '1', 'full', '6');"); - } - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'status_mapping' => [ - 'active' => [ - 'name' => 'Active', - 'value' => 'active', - 'text_color' => 'white', - 'background_color' => 'green', - 'browse_subdued' => false, - 'browse_badge' => true, - 'soft_delete' => false, - 'published' => true, - ], - 'inactive' => [ - 'name' => 'Inactive', - 'value' => 'inactive', - 'text_color' => 'white', - 'background_color' => 'blue-grey', - 'browse_subdued' => true, - 'browse_badge' => true, - 'soft_delete' => false, - 'published' => false, - ] - ] - ]), - ], - ['collection' => 'directus_webhooks', 'field' => 'status'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'choices' => [ - 'get' => 'GET', - 'post' => 'POST' - ] - ]), - ], - ['collection' => 'directus_webhooks', 'field' => 'http_action'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'options' => json_encode([ - 'choices' => [ - 'item.create:after' => 'Create', - 'item.update:after' => 'Update', - 'item.delete:after' => 'Delete', - ] - ]), - ], - ['collection' => 'directus_webhooks', 'field' => 'directus_action'] - )); - - - // Role table - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_roles', - [ - 'description' => 'Controls what API data is publicly available without authenticating' - ], - ['name' => 'public'] - )); - - // Settings Table - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_settings', - [ - 'value' => '10080' - ], - ['key' => 'auto_sign_out'] - )); - - - } -} diff --git a/migrations/upgrades/schemas/20191114095051_update_user_role_type.php b/migrations/upgrades/schemas/20191114095051_update_user_role_type.php deleted file mode 100644 index e6e1a2c915..0000000000 --- a/migrations/upgrades/schemas/20191114095051_update_user_role_type.php +++ /dev/null @@ -1,86 +0,0 @@ -execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'interface' => 'one-to-many' - ], - ['collection' => 'directus_roles', 'field' => 'users'] - )); - - $this->execute(\Directus\phinx_update( - $this->getAdapter(), - 'directus_fields', - [ - 'field' => 'role', - 'type' => 'm2o' - ], - ['collection' => 'directus_users', 'field' => 'roles'] - )); - - $result = $this->query('SELECT 1 FROM `directus_fields` WHERE `collection` = "directus_user_roles";')->fetch(); - - if ($result) { - $this->execute('DELETE FROM `directus_fields` where `collection` = "directus_user_roles";'); - } - - $config = \Directus\Application\Application::getInstance()->getContainer()->get('config'); - $dbName = $config->get('database.name'); - - - $result = $this->query('SELECT * FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = "'.$dbName.'" AND TABLE_NAME = "directus_users" AND COLUMN_NAME = "role"')->fetch(); - - if(!$result){ - $this->execute('ALTER TABLE `directus_users` ADD COLUMN `role` INT;'); - } - - $result = $this->query('SELECT 1 FROM `directus_relations` WHERE `collection_many` = "directus_user_roles";')->fetch(); - - if ($result) { - $this->execute('DELETE FROM `directus_relations` where `collection_many` = "directus_user_roles";'); - } - - $this->execute("INSERT INTO `directus_relations` (`collection_many`, `field_many`, `collection_one`, `field_one`) VALUES ('directus_users', 'role','directus_roles','users');"); - - $tableExist = $this->query('SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = "'.$dbName.'" AND TABLE_NAME = "directus_user_roles"')->fetch(); - - if($tableExist){ - $stmt = $this->query("SELECT * FROM `directus_user_roles`"); - while($row = $stmt->fetch(PDO::FETCH_ASSOC)){ - $this->execute('UPDATE `directus_users` SET `role` = '.$row['role'].' where id = '.$row['user'].';'); - } - } - - $this->execute('DROP TABLE IF EXISTS directus_user_roles'); - } -} diff --git a/migrations/upgrades/schemas/20191118181240_add_theme_field_for_user.php b/migrations/upgrades/schemas/20191118181240_add_theme_field_for_user.php deleted file mode 100644 index 67f819784d..0000000000 --- a/migrations/upgrades/schemas/20191118181240_add_theme_field_for_user.php +++ /dev/null @@ -1,56 +0,0 @@ -table('directus_users'); - if (!$table->hasColumn('theme')) { - $table->addColumn('theme', 'string', [ - 'limit' => 255, - 'null' => true, - 'default' => null - ]); - - $table->save(); - } - - $result = $this->query('SELECT 1 FROM `directus_fields` WHERE `collection` = "directus_users" AND `field` = "theme"')->fetch(); - - if (!$result) { - $options = json_encode([ - 'format' => true, - 'choices' => [ - 'auto' => 'Auto', - 'light' => 'Light', - 'dark' => 'Dark' - ] - ]); - $this->execute("INSERT INTO `directus_fields` (`collection`, `field`, `type`, `interface`, `options`, `locked`, `readonly`, `sort`) VALUES ('directus_users', 'theme', 'string', 'radio-buttons', '".$options."', 1,0 , 14);"); - } - } -} diff --git a/migrations/upgrades/seeds/.gitkeep b/public/extensions/custom/modules/.gitkeep similarity index 100% rename from migrations/upgrades/seeds/.gitkeep rename to public/extensions/custom/modules/.gitkeep diff --git a/public/thumbnail/.htaccess b/public/thumbnail/.htaccess deleted file mode 100644 index 6b419793d5..0000000000 --- a/public/thumbnail/.htaccess +++ /dev/null @@ -1,2 +0,0 @@ -RewriteEngine On -RewriteRule (.*) index.php [L] diff --git a/public/thumbnail/index.php b/public/thumbnail/index.php deleted file mode 100644 index 0dafda9cd9..0000000000 --- a/public/thumbnail/index.php +++ /dev/null @@ -1,90 +0,0 @@ - [ - 'error' => 8, - 'message' => 'API Project Configuration Not Found: ' . $projectName - ] - ]); - exit; -} - -$settings = \Directus\get_directus_thumbnail_settings(); -$timeToLive = \Directus\array_get($settings, 'thumbnail_cache_ttl', 86400); -try { - - parse_str($_SERVER['QUERY_STRING'], $queryParams); - - // if the thumb already exists, return it - $thumbnailer = new Thumbnailer( - $app->getContainer()->get('filesystem'), - $app->getContainer()->get('filesystem_thumb'), - $settings, - urldecode(\Directus\get_virtual_path()), - $queryParams - ); - - $image = $thumbnailer->get(); - - if (!$image) { - // now we can create the thumb - switch ($thumbnailer->action) { - // http://image.intervention.io/api/resize - case 'contain': - $image = $thumbnailer->contain(); - break; - // http://image.intervention.io/api/fit - case 'crop': - default: - $image = $thumbnailer->crop(); - } - } - - header('HTTP/1.1 200 OK'); - header('Content-type: ' . $thumbnailer->getThumbnailMimeType()); - header("Pragma: cache"); - header('Cache-Control: max-age=' . $timeToLive); - header("Access-Control-Allow-Origin: *"); - header("Access-Control-Allow-Methods: PUT, GET, POST, DELETE, OPTIONS"); - header("Access-Control-Allow-Headers: Access-Control-Allow-Headers,Content-Type"); - header('Last-Modified: ' . gmdate('D, d M Y H:i:s \G\M\T', time())); - header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', time() + $timeToLive)); - echo $image; - exit(0); -} catch (Exception $e) { - $filePath = ArrayUtils::get($settings, 'thumbnail_not_found_location'); - if (is_string($filePath) && !empty($filePath) && $filePath[0] !== '/') { - $filePath = $basePath . '/' . $filePath; - } - - // TODO: Throw message if the error is a invalid configuration - if (file_exists($filePath)) { - $mime = image_type_to_mime_type(exif_imagetype($filePath)); - - // TODO: Do we need to cache non-existing files? - header('Content-type: ' . $mime); - header("Pragma: cache"); - header('Cache-Control: max-age=' . $timeToLive); - header('Last-Modified: ' . gmdate('D, d M Y H:i:s \G\M\T', time())); - header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', time() + $timeToLive)); - echo file_get_contents($filePath); - } else { - http_response_code(404); - } - - exit(0); -} diff --git a/public/extensions/custom/pages/.gitkeep b/public/uploads/.gitkeep similarity index 100% rename from public/extensions/custom/pages/.gitkeep rename to public/uploads/.gitkeep diff --git a/public/uploads/_/originals/.gitkeep b/public/uploads/_/originals/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/public/uploads/_/originals/.htaccess b/public/uploads/_/originals/.htaccess deleted file mode 100644 index df5cb35238..0000000000 --- a/public/uploads/_/originals/.htaccess +++ /dev/null @@ -1,24 +0,0 @@ - - ExpiresActive On - ExpiresDefault "access 1 year" - - - - order allow,deny - deny from all - - - - - Header set Access-Control-Allow-Origin "*" - Header set Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS" - Header set Access-Control-Allow-Headers "Access-Control-Allow-Headers,Content-Type" - - - -# Respond with 404 if the file doesn't exists -# Before the API mod_rewrite catches the request - - RewriteCond %{REQUEST_FILENAME} !-f - RewriteRule .* - [L,R=404] - diff --git a/public/uploads/_/thumbnails/.gitkeep b/public/uploads/_/thumbnails/.gitkeep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/core/Directus/Application/Application.php b/src/core/Directus/Application/Application.php index cfcea8ac80..12fb2dc0ea 100644 --- a/src/core/Directus/Application/Application.php +++ b/src/core/Directus/Application/Application.php @@ -13,7 +13,7 @@ class Application extends App * * @var string */ - const DIRECTUS_VERSION = '8.0.0-rc.2'; + const DIRECTUS_VERSION = '8.3.0'; /** * NOT USED diff --git a/src/core/Directus/Application/CoreServicesProvider.php b/src/core/Directus/Application/CoreServicesProvider.php index 354a7bc7d3..761294c74a 100644 --- a/src/core/Directus/Application/CoreServicesProvider.php +++ b/src/core/Directus/Application/CoreServicesProvider.php @@ -70,6 +70,7 @@ use Slim\Views\Twig; use Zend\Db\TableGateway\TableGateway; use Directus\Api\Routes\Roles; +use function Directus\get_random_string; class CoreServicesProvider { @@ -125,8 +126,8 @@ protected function getLogger() // TODO: Move log configuration outside "slim app" settings $path = $container->get('path_base') . '/logs'; $config = $container->get('config'); - if ($config->has('settings.logger.path')) { - $path = $config->get('settings.logger.path'); + if ($config->has('logger.path')) { + $path = $config->get('logger.path'); } $pathIsStream = $path == 'php://stdout' || $path == 'php://stderr'; @@ -184,7 +185,7 @@ protected function getErrorHandler() $hookEmitter = $container['hook_emitter']; return new ErrorHandler($hookEmitter, [ - 'env' => $container->get('config')->get('app.env', 'development') + 'env' => $container->get('config')->get('env', 'development') ]); }; @@ -291,66 +292,8 @@ protected function getEmitter() return $payload; }, Emitter::P_HIGH); - $savesFile = function (Payload $payload, $replace = false) use ($container) { - $collectionName = $payload->attribute('collection_name'); - if ($collectionName !== SchemaManager::COLLECTION_FILES) { - return; - } - - if ($replace === true && !$payload->has('data')) { - return; - } - - // NOTE: "data" should be ignore if it isn't a string on update - if ($replace === true && !is_string($payload->get('data'))) { - $payload->remove('data'); - return; - } - - $data = $payload->getData(); - /** @var \Directus\Filesystem\Files $files */ - $files = $container->get('files'); - - $fileData = ArrayUtils::get($data, 'data'); - - $dataInfo = []; - if (is_a_url($fileData)) { - $dataInfo = $files->getLink($fileData); - // Set the URL payload data - $payload['data'] = ArrayUtils::get($dataInfo, 'data'); - $payload['filename'] = ArrayUtils::get($dataInfo, 'filename'); - } else if (!is_object($fileData)) { - $dataInfo = $files->getDataInfo($fileData); - } - - $type = ArrayUtils::get($dataInfo, 'type', ArrayUtils::get($data, 'type')); - - if (strpos($type, 'embed/') === 0) { - $recordData = $files->saveEmbedData(array_merge($dataInfo, ArrayUtils::pick($data, ['filename']))); - } else { - $recordData = $files->saveData($payload['data'], $payload['filename'], $replace); - } - - // NOTE: Use the user input title, tags, description and location when exists. - $recordData = ArrayUtils::defaults($recordData, ArrayUtils::pick($data, [ - 'title', - 'tags', - 'description', - 'location', - ])); - - $payload->replace($recordData); - $payload->remove('data'); - $payload->remove('html'); - if (!$replace) { - /** @var Acl $auth */ - $acl = $container->get('acl'); - $payload->set('uploaded_by', $acl->getUserId()); - $payload->set('uploaded_on', DateTimeUtils::now()->toString()); - } - }; - $emitter->addFilter('item.update:before', function (Payload $payload) use ($container, $savesFile) { + $emitter->addFilter('item.update:before', function (Payload $payload) use ($container) { $collection = SchemaService::getCollection($payload->attribute('collection_name')); /** @var Acl $acl */ @@ -368,22 +311,16 @@ protected function getEmitter() $payload->remove('date_uploaded'); } - $savesFile($payload, true); - return $payload; }, Emitter::P_HIGH); - $emitter->addFilter('item.create:before', function (Payload $payload) use ($savesFile) { - $savesFile($payload, false); - return $payload; - }); $addFilesUrl = function ($rows) { return \Directus\append_storage_information($rows); }; $emitter->addFilter('item.read.directus_files:before', function (Payload $payload) { $columns = $payload->get('columns'); - if (!in_array('filename', $columns)) { - $columns[] = 'filename'; + if (!in_array('filename_disk', $columns)) { + $columns[] = 'filename_disk'; $payload->set('columns', $columns); } return $payload; @@ -547,7 +484,6 @@ protected function getEmitter() $emitter->addFilter('item.read.directus_files', function (Payload $payload) use ($addFilesUrl, $container) { $rows = $addFilesUrl($payload->getData()); - $payload->replace($rows); return $payload; @@ -676,7 +612,7 @@ protected function getEmitter() $emitter->addFilter('item.create:before', $onInsertOrUpdate); $emitter->addFilter('item.update:before', $onInsertOrUpdate); - + $beforeSavingFiles = function ($payload) use ($container) { $acl = $container->get('acl'); if (!$acl->canCreate('directus_files')) { @@ -711,19 +647,23 @@ protected function getEmitter() $emitter->addAction('auth.request:credentials', function () use ($container) { /** @var Session $session */ $session = $container->get('session'); - if ($session->getStorage()->get('telemetry') === true) { - return; - } + $useTelemetry = get_directus_setting('telemetry',true); - $data = [ - 'version' => Application::DIRECTUS_VERSION, - 'url' => get_url(), - 'type' => 'api' - ]; - \Directus\request_send_json('POST', 'https://telemetry.directus.io/count', $data); + if($useTelemetry) { + if ($session->getStorage()->get('telemetry') === true) { + return; + } - // NOTE: this only works when the client sends subsequent request with the same cookie - $session->getStorage()->set('telemetry', true); + $data = [ + 'version' => Application::DIRECTUS_VERSION, + 'url' => get_url(), + 'type' => 'api' + ]; + \Directus\request_send_json('POST', 'https://telemetry.directus.io/count', $data); + + // NOTE: this only works when the client sends subsequent request with the same cookie + $session->getStorage()->set('telemetry', true); + } }); return $emitter; diff --git a/src/core/Directus/Application/Http/Middleware/ResponseCacheMiddleware.php b/src/core/Directus/Application/Http/Middleware/ResponseCacheMiddleware.php index 542173a581..f86955eaef 100644 --- a/src/core/Directus/Application/Http/Middleware/ResponseCacheMiddleware.php +++ b/src/core/Directus/Application/Http/Middleware/ResponseCacheMiddleware.php @@ -41,7 +41,7 @@ public function __invoke(Request $request, Response $response, callable $next) $requestPath = $request->getUri()->getPath(); - $key = md5($container->get('acl')->getUserId().'@'.$requestPath.'?'.http_build_query($parameters)); + $key = md5($container->get('acl')->getUserId() . '@' . $requestPath . '?' . http_build_query($parameters)); } else if ($request->isPost() && StringUtils::endsWith($request->getUri()->getPath(), '/gql')) { // Handle caching for GraphQL query that are POST. // TODO:: Add support for ACL and Mutation @@ -71,17 +71,17 @@ public function __invoke(Request $request, Response $response, callable $next) $authorizationTokenObject = get_request_authorization_token($request); $accessToken = null; - try{ - if(!empty($authorizationTokenObject['token'])){ + try { + if (!empty($authorizationTokenObject['token'])) { $userSessionService = new UserSessionService($container); $userSessionService->destroy([ 'token_expired_at < ?' => DateTimeUtils::now()->toString() ]); $expirationMinutes = get_directus_setting('auto_sign_out'); - $expiry = new \DateTimeImmutable('now + '.$expirationMinutes.'minutes'); + $expiry = new \DateTimeImmutable('now + ' . $expirationMinutes . 'minutes'); - switch($authorizationTokenObject['type']){ - case DirectusUserSessionsTableGateway::TOKEN_COOKIE : + switch ($authorizationTokenObject['type']) { + case DirectusUserSessionsTableGateway::TOKEN_COOKIE: $accessToken = decrypt_static_token($authorizationTokenObject['token']); $userSession = $userSessionService->find(['token' => $accessToken]); $cookie = new Cookies(); @@ -91,29 +91,41 @@ public function __invoke(Request $request, Response $response, callable $next) [ 'value' => $authorizationTokenObject['token'], 'expires' => $expiryAt, - 'path'=>'/', + 'path' => '/', 'httponly' => true ] ); - $response = $response->withAddedHeader('Set-Cookie',$cookie->toHeaders()); + $response = $response->withAddedHeader('Set-Cookie', $cookie->toHeaders()); break; - default : + default: $userSession = $userSessionService->find(['token' => $authorizationTokenObject['token']]); break; } } - if(isset($userSession)){ - $userSessionService->update($userSession['id'],['token_expired_at' => $expiry->format('Y-m-d H:i:s')]); + if (isset($userSession)) { + $userSessionService->update($userSession['id'], ['token_expired_at' => $expiry->format('Y-m-d H:i:s')]); } - }catch(\Exception $e){ + } catch (\Exception $e) { $container->get('logger')->error($e->getMessage()); + $cookie = new Cookies(); + $cookie->set( + get_project_session_cookie_name($request), + [ + 'value' => $authorizationTokenObject['token'], + 'expires' => DateTimeUtils::now()->toString(), + 'path' => '/', + 'httponly' => true + ] + ); + + $response = $response->withAddedHeader('Set-Cookie', $cookie->toHeaders()); } $response = $response->withHeader('Access-Control-Allow-Origin', $request->getHeader('Origin')); $config = $container->get('config'); if ($config->get('cors.credentials')) { - $response = $response->withHeader('Access-Control-Allow-Credentials', 'true'); + $response = $response->withHeader('Access-Control-Allow-Credentials', 'true'); } return $response; } diff --git a/src/core/Directus/Authentication/Exception/TFAEnforcedException.php b/src/core/Directus/Authentication/Exception/TFAEnforcedException.php index 91980f4e52..4ca9c1da0d 100644 --- a/src/core/Directus/Authentication/Exception/TFAEnforcedException.php +++ b/src/core/Directus/Authentication/Exception/TFAEnforcedException.php @@ -11,6 +11,6 @@ class TFAEnforcedException extends UnauthorizedException public function __construct() { - parent::__construct(ERROR_MESSAGE); + parent::__construct(static::ERROR_MESSAGE); } } diff --git a/src/core/Directus/Config/Schema/Schema.php b/src/core/Directus/Config/Schema/Schema.php index 3b4c27537f..692febd531 100644 --- a/src/core/Directus/Config/Schema/Schema.php +++ b/src/core/Directus/Config/Schema/Schema.php @@ -19,14 +19,9 @@ public static function get() { } return new Group('directus', [ - new Group('app', [ - new Value('env', Types::STRING, 'production'), - new Value('timezone', Types::STRING, 'America/New_York'), - ]), - new Group('settings', [ - new Group('logger', [ - new Value('path', Types::STRING, $loggerPath), - ]) + new Value('env', Types::STRING, 'production'), + new Group('logger', [ + new Value('path', Types::STRING, $loggerPath), ]), new Group('database', [ new Value('type', Types::STRING, 'mysql'), @@ -54,7 +49,7 @@ public static function get() { new Value('adapter', Types::STRING, 'local'), new Value('root', Types::STRING, 'public/uploads/_/originals'), new Value('root_url', Types::STRING, '/uploads/_/originals'), - new Value('thumb_root', Types::STRING, 'public/uploads/_/thumbnails'), + new Value('thumb_root', Types::STRING, 'public/uploads/_/generated'), new Value('proxy_downloads?', Types::BOOLEAN, false), // S3 @@ -117,10 +112,6 @@ public static function get() { new Value('actions', Types::ARRAY, []), new Value('filters', Types::ARRAY, []), ]), - new Group('feedback', [ - new Value('token', Types::STRING, 'a-kind-of-unique-token'), - new Value('login', Types::STRING, true), - ]), new Value('tableBlacklist', Types::ARRAY, []), new Group('auth', [ new Value('secret_key', Types::STRING, ''), diff --git a/src/core/Directus/Console/Modules/DatabaseModule.php b/src/core/Directus/Console/Modules/DatabaseModule.php index f103383c9c..76f57c04d9 100644 --- a/src/core/Directus/Console/Modules/DatabaseModule.php +++ b/src/core/Directus/Console/Modules/DatabaseModule.php @@ -40,6 +40,7 @@ public function cmdUpgrade($args, $extra) foreach ($args as $key => $value) { switch ($key) { case 'N': + case 'k': $project = $value; break; } diff --git a/src/core/Directus/Console/Modules/InstallModule.php b/src/core/Directus/Console/Modules/InstallModule.php index d1ef957bf3..f9dd47ab36 100644 --- a/src/core/Directus/Console/Modules/InstallModule.php +++ b/src/core/Directus/Console/Modules/InstallModule.php @@ -2,13 +2,17 @@ namespace Directus\Console\Modules; +use Directus\Database\Exception\ConnectionFailedException; use Directus\Console\Common\Exception\PasswordChangeException; use Directus\Console\Common\Exception\UserUpdateException; +use Directus\Console\Common\Exception\InvalidDatabaseConnectionException; use Directus\Console\Common\Setting; use Directus\Console\Common\User; use Directus\Console\Exception\CommandFailedException; use Directus\Util\ArrayUtils; use Directus\Util\Installation\InstallerUtils; +use Directus\Util\StringUtils; +use Directus\Exception\UnauthorizedException; class InstallModule extends ModuleBase { @@ -25,6 +29,7 @@ public function __construct($basePath) $this->help = [ 'config' => '' + . PHP_EOL . "\t\t-k " . 'Project key for the created project' . PHP_EOL . "\t\t-h " . 'Hostname or IP address of the MySQL DB to be used. Default: localhost' . PHP_EOL . "\t\t-n " . 'Name of the database to use for Directus. Default: directus' . PHP_EOL . "\t\t-u " . 'Username for DB connection. Default: directus' @@ -35,6 +40,7 @@ public function __construct($basePath) . PHP_EOL . "\t\t-r " . 'Directus root URI. Default: /', 'database' => '', 'install' => '' + . PHP_EOL . "\t\t-k " . 'Project key for the created project' . PHP_EOL . "\t\t-e " . 'Administrator e-mail address, used for administration login. Default: admin@directus.com' . PHP_EOL . "\t\t-p " . 'Initial administrator password. Default: directus' . PHP_EOL . "\t\t-t " . 'Name for this Directus installation. Default: Directus' @@ -44,7 +50,7 @@ public function __construct($basePath) $this->commands_help = [ 'config' => 'Configure Directus: ' . PHP_EOL . PHP_EOL . "\t\t" - . $this->__module_name . ':config -h db_host -n db_name -u db_user -p db_pass -d directus_path' . PHP_EOL, + . $this->__module_name . ':config -k my-project -h db_host -n db_name -u db_user -p db_pass -d directus_path -a super_admin_token' . PHP_EOL, 'database' => 'Populate the Database Schema: ' . PHP_EOL . PHP_EOL . "\t\t" . $this->__module_name . ':database -d directus_path' . PHP_EOL, 'install' => 'Install Initial Configurations: ' . PHP_EOL . PHP_EOL . "\t\t" @@ -59,6 +65,7 @@ public function __construct($basePath) 'f' => 'boolean', ] ]; + } public function cmdConfig($args, $extra) @@ -70,6 +77,11 @@ public function cmdConfig($args, $extra) foreach ($args as $key => $value) { switch ($key) { + case 'a': + $data['super_admin_token'] = $value; + case 'k': + $data['project'] = (string) $value; + break; case 't': $data['db_type'] = $value; break; @@ -94,9 +106,6 @@ public function cmdConfig($args, $extra) case 'c': $data['cors_enabled'] = (bool) $value; break; - case 'N': // project Name - $data['project'] = (string) $value; - break; case 's': $data['db_socket'] = $value; break; @@ -114,12 +123,40 @@ public function cmdConfig($args, $extra) throw new \Exception(sprintf('Path "%s" does not exist', $apiPath)); } + $scannedDirectory = \Directus\scan_folder($this->getBasePath().'/config'); + $projectNames = $scannedDirectory; + + $superadminFilePath = $this->getBasePath().'/config/__api.json'; + if(empty($projectNames)){ + $requiredAttributes = ['db_name', 'db_user']; + $data['super_admin_token'] = StringUtils::randomString(16,false); + }else{ + $requiredAttributes = ['db_name', 'db_user', 'super_admin_token']; + $superadminFileData = json_decode(file_get_contents($superadminFilePath), true); + if (!is_null($data['super_admin_token']) && $data['super_admin_token'] !== $superadminFileData['super_admin_token']) { + throw new UnauthorizedException('Permission denied: Superadmin Only'); + } + } + if (!ArrayUtils::contains($data, $requiredAttributes)) { + throw new \InvalidArgumentException( + 'Creating config files required: ' . implode(', ', $requiredAttributes) + ); + } + if(empty($projectNames)){ + $configStub = InstallerUtils::createJsonFileContent($data); + file_put_contents($superadminFilePath, $configStub); + } InstallerUtils::createConfig($directusPath, $data, $force); + + if(empty($projectNames)){ + echo PHP_EOL . "Make sure to copy the generated Super-Admin password below. You won't be able to see it again!". PHP_EOL; + echo PHP_EOL . $data['super_admin_token'] . PHP_EOL; + } } public function cmdDatabase($args, $extra) { - $directus_path = $this->getBasePath() . DIRECTORY_SEPARATOR; + $directus_path = $this->getBasePath(); $projectName = null; $force = false; @@ -128,7 +165,7 @@ public function cmdDatabase($args, $extra) case 'd': $directus_path = $value; break; - case 'N': + case 'k': $projectName = $value; break; case 'f': @@ -137,7 +174,33 @@ public function cmdDatabase($args, $extra) } } + + if (getenv('DIRECTUS_USE_ENV') === "1") { + $data = [ + 'project' => '_', + 'db_name' => getenv('DIRECTUS_DATABASE_NAME'), + 'db_host' => getenv('DIRECTUS_DATABASE_HOST'), + 'db_port' => getenv('DIRECTUS_DATABASE_PORT'), + 'db_user' => getenv('DIRECTUS_DATABASE_USERNAME'), + 'db_password' => getenv('DIRECTUS_DATABASE_PASSWORD'), + ]; + } else { + $app = InstallerUtils::createApp($directus_path, $projectName); + $config = $app->getConfig(); + $data = [ + 'project' => $projectName, + 'db_name' => $config['database']['name'], + 'db_host' => $config['database']['host'], + 'db_port' => $config['database']['port'], + 'db_user' => $config['database']['username'], + 'db_password' => $config['database']['password'], + ]; + } + + InstallerUtils::ensureCanCreateTables($directus_path, $data, $force); + InstallerUtils::createTables($directus_path, $projectName, $force); + InstallerUtils::addUpgradeMigrations($this->getBasePath(),$projectName); } public function cmdSeeder($args, $extra) @@ -170,7 +233,7 @@ public function cmdInstall($args, $extra) case 'T': $data['user_token'] = $value; break; - case 'N': + case 'k': $projectName = $value; break; case 'timezone': diff --git a/src/core/Directus/Database/Schema/Sources/AbstractSchema.php b/src/core/Directus/Database/Schema/Sources/AbstractSchema.php index 16b7e158a0..38734b494a 100644 --- a/src/core/Directus/Database/Schema/Sources/AbstractSchema.php +++ b/src/core/Directus/Database/Schema/Sources/AbstractSchema.php @@ -130,16 +130,24 @@ public function getTypeFromSource($sourceType) case 'tinyint': case 'smallint': case 'mediumint': + case 'bigint': + case 'serial': case 'int': // alias: integer case 'year': $type = DataTypes::TYPE_INTEGER; break; case 'decimal': // alias: dec, fixed case 'numeric': + case 'real': case 'float': // alias: real case 'double': // alias: double precision, real $type = DataTypes::TYPE_DECIMAL; break; + case 'bool': + case 'boolean': + $type = DataTypes::TYPE_BOOLEAN; + case 'blob': + case 'longblob': case 'bit': case 'binary': case 'varbinary': diff --git a/src/core/Directus/Database/TableGateway/BaseTableGateway.php b/src/core/Directus/Database/TableGateway/BaseTableGateway.php index 425d0a4e8e..a8725c9cda 100644 --- a/src/core/Directus/Database/TableGateway/BaseTableGateway.php +++ b/src/core/Directus/Database/TableGateway/BaseTableGateway.php @@ -47,6 +47,7 @@ use Zend\Db\TableGateway\Feature; use Zend\Db\TableGateway\Feature\RowGatewayFeature; use Zend\Db\TableGateway\TableGateway; +use function Directus\get_random_string; class BaseTableGateway extends TableGateway { @@ -376,7 +377,7 @@ public function addRecordByArray(array $recordData) $hookName = 'item.create.' . SchemaManager::COLLECTION_FILES; // TODO: Implement once execute. Allowing a hook callback to run once. $listenerId = static::$emitter->addAction($hookName, function ($data) use (&$recordData) { - $recordData['filename'] = $data['filename']; + $recordData['filename_disk'] = $data['filename_disk']; }, Emitter::P_LOW); } @@ -388,7 +389,6 @@ public function addRecordByArray(array $recordData) } $TableGateway->insert($recordData); - if (static::$emitter && $listenerId) { static::$emitter->removeListenerWithIndex($listenerId); } @@ -399,9 +399,8 @@ public function addRecordByArray(array $recordData) $recordData[$primaryKey] = $TableGateway->getLastInsertValue(); } - $this->afterAddOrUpdate($recordData); - $columns = SchemaService::getAllNonAliasCollectionFieldNames($this->table); + return $TableGateway->fetchAll(function (Select $select) use ($recordData, $columns, $primaryKey) { $select ->columns($columns) @@ -426,26 +425,6 @@ public function updateRecordByArray(array $recordData) } $recordId = $recordData[$primaryKey]; - if ($collectionName === SchemaManager::COLLECTION_FILES) { - $select = new Select($collectionName); - $select->columns(['filename']); - $select->where([ - $primaryKey => $recordId - ]); - $select->limit(1); - $result = $TableGateway->ignoreFilters()->selectWith($select); - - if ($result->count() === 0) { - throw new ItemNotFoundException(); - } - - $currentItem = $result->current()->toArray(); - - $originalFilename = ArrayUtils::get($currentItem, 'filename'); - $recordData = array_merge([ - 'filename' => $originalFilename - ], $recordData); - } $Update = new Update($collectionName); $Update->set($recordData); @@ -454,20 +433,6 @@ public function updateRecordByArray(array $recordData) ]); $TableGateway->updateWith($Update); - $replace = true; - if ($collectionName === SchemaManager::COLLECTION_FILES && static::$container) { - $changeFilename = $originalFilename && $recordData['filename'] !== $originalFilename; - $replace = !$changeFilename; - - if ($changeFilename) { - /** @var Files $Files */ - $Files = static::$container->get('files'); - $Files->delete(['filename' => $originalFilename]); - } - } - - $this->afterAddOrUpdate($recordData, $replace); - $columns = SchemaService::getAllNonAliasCollectionFieldNames($collectionName); return $TableGateway->fetchAll(function ($select) use ($recordData, $columns, $primaryKey) { $select @@ -727,7 +692,6 @@ protected function executeSelect(Select $select) $selectState = $select->getRawState(); $selectCollectionName = $selectState['table']; - if ($useFilter) { $selectState = $this->applyHooks([ 'item.read:before', @@ -912,7 +876,7 @@ protected function executeDelete(Delete $delete) $deleteState = $delete->getRawState(); $deleteTable = $this->getRawTableNameFromQueryStateTable($deleteState['table']); - + // Runs select PK with passed delete's $where before deleting, to use those for the even hook if ($pk = $this->primaryKeyFieldName) { $select = $this->sql->select(); @@ -931,7 +895,7 @@ protected function executeDelete(Delete $delete) $delete = $this->sql->delete(); $expression = new In($pk, $ids); $delete->where($expression); - + foreach ($ids as $id) { $deleteData = [$this->primaryKeyFieldName => $id]; $this->runHook('item.delete:before', [$deleteTable, $deleteData]); @@ -947,7 +911,7 @@ protected function executeDelete(Delete $delete) ); } - + //Invalidate individual cache if (static::$container) { $config = static::$container->get('config'); @@ -1809,55 +1773,6 @@ protected function validateRecordArray(array $record) } } - /** - * @param array $record - * @param bool $replace - */ - protected function afterAddOrUpdate(array $record, $replace = false) - { - // TODO: Move this to a proper hook - $hasData = ArrayUtils::has($record, 'data') && is_string($record['data']); - $isFilesCollection = $this->table == SchemaManager::COLLECTION_FILES; - - if (!static::$container || !$hasData || !$isFilesCollection) { - return; - } - - $Files = static::$container->get('files'); - - $updateArray = []; - if ($Files->getSettings('file_naming') == 'id') { - $ext = $thumbnailExt = pathinfo($record['filename'], PATHINFO_EXTENSION); - $fileId = $record[$this->primaryKeyFieldName]; - // TODO: The left padding should be based on the max length of the primary - $newFilename = filename_put_ext(str_pad( - $fileId, - 11, - '0', - STR_PAD_LEFT - ), $ext); - - // overwrite a file with this file content if it already exists - if (!$replace) { - $Files->rename( - $record['filename'], - $newFilename, - true - ); - } - - $updateArray['filename'] = $newFilename; - $record['filename'] = $updateArray['filename']; - } - - if (!empty($updateArray)) { - $Update = new Update($this->table); - $Update->set($updateArray); - $Update->where([$this->primaryKeyFieldName => $record[$this->primaryKeyFieldName]]); - $this->updateWith($Update); - } - } - /** * Checks whether or not null should be sorted last * diff --git a/src/core/Directus/Database/TableGateway/RelationalTableGateway.php b/src/core/Directus/Database/TableGateway/RelationalTableGateway.php index 9ea1a0b6d5..0804a3ecbf 100644 --- a/src/core/Directus/Database/TableGateway/RelationalTableGateway.php +++ b/src/core/Directus/Database/TableGateway/RelationalTableGateway.php @@ -721,9 +721,9 @@ public function addOrUpdateToManyRelationships($schema, $parentRow, &$childLogEn if(strtolower($column->getType()) == DataTypes::TYPE_USER_CREATED || strtolower($column->getType()) == DataTypes::TYPE_USER_UPDATED){ unset($foreignRecord[$column->getName()]); } - } + } } - + $foreignRecord = $ForeignTable->manageRecordUpdate( $foreignTableName, $foreignRecord, @@ -793,7 +793,7 @@ public function applyDefaultEntriesSelectParams(array $params) if (!isset($params['offset']) && isset($params['page']) && isset($params['limit'])) { $params['offset'] = $params['limit'] * ($params['page'] - 1); } - + $params = array_merge($defaultParams, $params); if (ArrayUtils::get($params, 'sort')) { @@ -1017,7 +1017,7 @@ public function createEntriesMetadata(array $entries, array $list = []) $condition = new In($fieldName, $this->getNonSoftDeleteStatuses()); } $countedData['total_count'] = $this->countTotal($condition); - + if (in_array('total_count', $list)) { $metadata['total_count'] = $this->countTotal($condition); } @@ -1060,7 +1060,7 @@ public function createMetadataPagination(array $metadata = [], array $params = [ $meta_param=explode(',',$params['meta']); if((in_array('filter_count',$meta_param) || in_array('*',$meta_param))) { $metadata['filter_count'] = $countedData['total_count']; - } + } if ($filtered) { $filteredparams = array_merge($params, [ @@ -1075,7 +1075,7 @@ public function createMetadataPagination(array $metadata = [], array $params = [ $metadata['filter_count'] = $total; } } - + $limit = $limit < 1 ? $rows : $limit; $pages = $total ? ceil($total / $limit) : 1; $page = $page > $pages ? $pages : ($page && $offset >= 0 ? (floor($offset / $limit) + 1) : $page); @@ -1089,7 +1089,7 @@ public function createMetadataPagination(array $metadata = [], array $params = [ $last = ($pages < 2) ? null : (($pages - 1) * $limit); } - + if(in_array('page',$meta_param) || in_array('*',$meta_param)) { $metadata = array_merge($metadata, [ "limit" => $limit, @@ -1127,7 +1127,7 @@ public function fetchItems(array $params = [], \Closure $queryCallback = null) $params = $this->applyDefaultEntriesSelectParams($params); $fields = ArrayUtils::get($params, 'fields'); - + // TODO: Check for all collections + fields permission/existence before querying // TODO: Create a new TableGateway Query Builder based on Query\Builder @@ -1135,7 +1135,7 @@ public function fetchItems(array $params = [], \Closure $queryCallback = null) $builder->from($this->getTable()); $selectedFields = $this->getSelectedNonAliasFields($fields ?: ['*']); - + if (!in_array($collectionObject->getPrimaryKeyName(), $selectedFields)) { array_unshift($selectedFields, $collectionObject->getPrimaryKeyName()); } @@ -1145,10 +1145,17 @@ public function fetchItems(array $params = [], \Closure $queryCallback = null) array_unshift($selectedFields, $statusField->getName()); } - // NOTE: Make sure to have the `type` field for files to determine if the supports thumbnails - if ($this->table == SchemaManager::COLLECTION_FILES && !in_array('type', $selectedFields)) { + if ($this->table == SchemaManager::COLLECTION_FILES) { + // NOTE: Make sure to have the `type` field for files to determine if the supports thumbnails + if(!in_array('type', $selectedFields)) { $selectedFields[] = 'type'; + } + // NOTE: Make sure to have the `private_hash` field for files to display in URLs + if (!in_array('private_hash', $selectedFields)) { + $selectedFields[] = 'private_hash'; + } } + $builder->columns($selectedFields); $builder = $this->applyParamsToTableEntriesSelect( @@ -1221,11 +1228,15 @@ public function fetchItems(array $params = [], \Closure $queryCallback = null) 'lang' => ArrayUtils::get($params, 'lang') ]; - $results = $this->loadRelationalData( + $resultArray = $this->loadRelationalData( $results, \Directus\get_array_flat_columns($relatedFields), $relationalParams ); + + if(!empty($resultArray)){ + $results = $resultArray; + } } // When the params column list doesn't include the primary key @@ -1332,7 +1343,7 @@ protected function parseCondition($condition) 'logical' => $logical ]; } - + protected function parseDotFilters(Builder $mainQuery, array $filters) { foreach ($filters as $column => $condition) { @@ -1637,7 +1648,7 @@ protected function shouldIgnoreQueryFilter($operator, $value) return in_array($operator, $operators) && empty($value) && !is_numeric($value); } - + /** * Process single relation field filter * @@ -1645,7 +1656,7 @@ protected function shouldIgnoreQueryFilter($operator, $value) * @param string $column * @param array | string $condition * - * @return + * @return */ protected function processRelationalFilter(Builder $mainQuery, $column, $condition){ $columnList = $filterColumns = explode('.', $column); @@ -1786,7 +1797,7 @@ protected function processRelationalFilter(Builder $mainQuery, $column, $conditi $mainTable ); } - + /** * Process Select Filters (Where conditions) * @@ -1806,7 +1817,7 @@ protected function processFilter(Builder $query, array $filters = []) } else if (isset($fieldReadBlackListDetails['statuses']) && !empty($fieldReadBlackListDetails['statuses'])) { $blackListStatuses = array_merge($blackListStatuses, array_values($fieldReadBlackListDetails['statuses'])); } - + if (!(!is_string($column) || strpos($column, '.') === false)){ //Process relational & non relation field filters sequentially //Earlier, all the relation field filters were processing first and then non relation fields, due to that logical operators were not working in mix filters @@ -1898,13 +1909,13 @@ protected function processQ(Builder $query, $search) $relatedTable = $relationship->getCollectionMany(); $relatedRightColumn = $relationship->getFieldMany(); $relatedTableColumns = SchemaService::getAllCollectionFields($relatedTable); - + //Condition for table related to same table with O2M, to avoid right join of same tables if($relatedTable == $table){ $relatedQuery = new Builder($this->getAdapter()); $relatedQuery->columns([$relatedRightColumn]); $relatedQuery->from($relatedTable); - + foreach ($relatedTableColumns as $relatedColumn) { // NOTE: Only search numeric or string type columns $isNumeric = $this->getSchemaManager()->isFieldNumericType($relatedColumn); @@ -2335,7 +2346,7 @@ public function getSelectedFields(array $fields) public function getSelectedNonAliasFields(array $fields) { $nonAliasFields = SchemaService::getAllNonAliasCollectionFieldsName($this->getTableSchema()->getName()); - + $allFields = $this->replaceWildcardFieldWith( $fields, $nonAliasFields diff --git a/src/core/Directus/Filesystem/Files.php b/src/core/Directus/Filesystem/Files.php index 35ca45cba8..c49af8220c 100644 --- a/src/core/Directus/Filesystem/Files.php +++ b/src/core/Directus/Filesystem/Files.php @@ -5,7 +5,7 @@ use Char0n\FFMpegPHP\Movie; use Directus\Application\Application; use function Directus\filename_put_ext; -use function Directus\generate_uuid5; +use function Directus\generate_uuid4; use function Directus\is_a_url; use Directus\Util\ArrayUtils; use Directus\Util\DateTimeUtils; @@ -265,11 +265,14 @@ public function getDataInfo($data) /** * Copy base64 data into Directus Media * - * @param string $fileData - base64 data + * @param string $fileData - base64 data or directus_files object * @param string $fileName - name of the file * @param bool $replace * * @return array + * + * @todo Refactor this to be clearer what's going on. $fileData can be anything, + * all kinds of things are happening, and nothing is documented */ public function saveData($fileData, $fileName, $replace = false) { @@ -289,12 +292,17 @@ public function saveData($fileData, $fileName, $replace = false) } // @TODO: merge with upload() $fileName = $this->getFileName($fileName, $replace !== true); - $filePath = $this->getConfig('root') . '/' . $fileName; - + $ext = pathinfo($fileName, PATHINFO_EXTENSION); $event = $replace ? 'file.update' : 'file.save'; $this->emitter->run($event, ['name' => $fileName, 'size' => $size]); - $this->write($fileName, $fileData, $replace); + + // On name change, the file would be overwritten with the empty file data. + // This prevents you can't update a file to a zero-byte file. + if (!empty($fileData)) { + $this->write($fileName, $fileData, $replace); + } + $this->emitter->run($event.':after', ['name' => $fileName, 'size' => $size]); #open local tmp file since s3 bucket is private @@ -308,7 +316,7 @@ public function saveData($fileData, $fileName, $replace = false) $fileData = $this->getFileInfo($fileName); $fileData['title'] = Formatting::fileNameToFileTitle($title); - $fileData['filename'] = basename($filePath); + $fileData['filename_disk'] = basename($filePath); $fileData['storage'] = $this->config['adapter']; $fileData = array_merge($this->defaults, $fileData); @@ -333,11 +341,10 @@ public function saveData($fileData, $fileName, $replace = false) } unset($tmpData); - return [ - // The MIME type will be based on its extension, rather than its content - 'type' => MimeTypeUtils::getFromFilename($fileData['filename']), - 'filename' => $fileData['filename'], - 'title' => $fileData['title'], + $response = [ + // The MIME type will be based on its extension, rather than its extension + 'type' => MimeTypeUtils::getFromFilename($fileData['filename_disk']), + 'filename_disk' => $fileData['filename_disk'], 'tags' => $fileData['tags'], 'description' => $fileData['description'], 'location' => $fileData['location'], @@ -349,6 +356,12 @@ public function saveData($fileData, $fileName, $replace = false) 'checksum' => $checksum, 'duration' => isset($duration) ? $duration : 0 ]; + + if(!$replace){ + $response['title'] = $fileData['title']; + } + + return $response; } /** @@ -365,7 +378,7 @@ public function saveEmbedData(array $fileInfo) } $fileName = isset($fileInfo['filename']) ? $fileInfo['filename'] : md5(time()) . '.jpg'; - $thumbnailData = $this->saveData($fileInfo['data'], $fileName); + $thumbnailData = $this->saveData($fileInfo['data'], $fileName,$fileInfo['fileId']); return array_merge( $fileInfo, @@ -675,16 +688,14 @@ public function uniqueName($fileName, $targetPath = null, $attempt = 0) */ private function getFileName($fileName, $unique = true) { - switch ($this->getSettings('file_naming')) { - case 'uuid': - $fileName = $this->uuidFileName($fileName); - break; - } - if ($unique) { + switch ($this->getSettings('file_naming')) { + case 'uuid': + $fileName = $this->uuidFileName($fileName); + break; + } $fileName = $this->uniqueName($fileName, $this->filesystem->getPath()); } - return $fileName; } @@ -698,7 +709,7 @@ private function getFileName($fileName, $unique = true) private function uuidFileName($fileName) { $ext = pathinfo($fileName, PATHINFO_EXTENSION); - $fileHashName = generate_uuid5(null, $fileName); + $fileHashName = generate_uuid4(); return $fileHashName . '.' . $ext; } diff --git a/src/core/Directus/Filesystem/Thumbnailer.php b/src/core/Directus/Filesystem/Thumbnailer.php index e30f187429..fe5c89f90b 100644 --- a/src/core/Directus/Filesystem/Thumbnailer.php +++ b/src/core/Directus/Filesystem/Thumbnailer.php @@ -59,30 +59,27 @@ public function __construct(Filesystem $main, Filesystem $thumb, array $config, $this->config = $config; $this->params = $params; - $this->thumbnailParams = $this->extractThumbnailParams($path); + $this->thumbnailParams = $this->validateThumbnailParams($path,$params); - // check if the original file exists in storage + // // check if the original file exists in storage if (! $this->filesystem->exists($this->fileName)) { throw new Exception($this->fileName . ' does not exist.'); // original file doesn't exist } - // check if dimensions are supported - if (! $this->isSupportedThumbnailDimension($this->width, $this->height)) { - throw new Exception('Invalid dimensions.'); - } + $this->validateThumbnailWhitelist($params); - // check if action is supported - if ( $this->action && ! $this->isSupportedAction($this->action)) { - throw new Exception('Invalid action.'); - } + $otherParams=$params; + unset($otherParams['width'],$otherParams['height'],$otherParams['fit'],$otherParams['quality']); - // check if quality is supported - if ( $this->quality && ! $this->isSupportedQualityTag($this->quality)) { - throw new Exception('Invalid quality.'); + if(count($otherParams) > 0) { + ksort($otherParams); + $paramsString=''; + foreach($otherParams as $key => $value) { + $paramsString .= ','. substr($key, 0, 1) . $value; + } } - // relative to configuration['storage']['thumb_root'] - $this->thumbnailDir = $this->width . '/' . $this->height . ($this->action ? '/' . $this->action : '') . ($this->quality ? '/' . $this->quality : ''); + $this->thumbnailDir = 'w'.$params['width'] . ',h' . $params['height'] . ',f' . $params['fit'] . ',q' . $params['quality'].$paramsString; } catch (Exception $e) { throw $e; } @@ -171,7 +168,7 @@ public function contain() { try { // action options - $options = $this->getSupportedActionOptions($this->action); + $options = $this->getSupportedActionOptions($this->$params); // open file image resource $img = $this->load(); @@ -188,7 +185,7 @@ public function contain() $img->resizeCanvas($this->width, $this->height, ArrayUtils::get($options, 'position', 'center'), ArrayUtils::get($options, 'resizeRelative', false), ArrayUtils::get($options, 'canvasBackground', [255, 255, 255, 0])); } - $encodedImg = (string) $img->encode($this->format, ($this->quality ? $this->translateQuality($this->quality) : null)); + $encodedImg = (string) $img->encode($this->format, ($this->quality ? $this->quality : null)); $this->filesystemThumb->write($this->thumbnailDir . '/' . $this->thumbnailFileName, $encodedImg); return $encodedImg; @@ -220,7 +217,7 @@ public function crop() // resize/crop image $img->fit($this->width, $this->height, function($constraint){}, ArrayUtils::get($options, 'position', 'center')); - $encodedImg = (string) $img->encode($this->format, ($this->quality ? $this->translateQuality($this->quality) : null)); + $encodedImg = (string) $img->encode($this->format, ($this->quality ? $this->quality : null)); $this->filesystemThumb->write($this->thumbnailDir . '/' . $this->thumbnailFileName, $encodedImg); return $encodedImg; @@ -232,13 +229,14 @@ public function crop() } /** - * Parse url and extract thumbnail params + * validate params and file extensions and return it * * @param string $thumbnailUrlPath + * @param array $params * @throws Exception * @return array */ - public function extractThumbnailParams($thumbnailUrlPath) + public function validateThumbnailParams($thumbnailUrlPath,$params) { // cache if ($this->thumbnailParams) { @@ -248,31 +246,37 @@ public function extractThumbnailParams($thumbnailUrlPath) // get URL parts // https://docs.directus.io/guides/thumbnailer.html#url-syntax $urlSegments = explode('/', $thumbnailUrlPath); - if (count($urlSegments) < 6) { - throw new Exception('Invalid URL syntax.'); - } // pull the env out of the segments array_shift($urlSegments); // set thumbnail parameters $thumbnailParams = [ - 'action' => filter_var($urlSegments[2], FILTER_SANITIZE_STRING), - 'height' => filter_var($urlSegments[1], FILTER_SANITIZE_NUMBER_INT), - 'quality' => filter_var($urlSegments[3], FILTER_SANITIZE_STRING), - 'width' => filter_var($urlSegments[0], FILTER_SANITIZE_NUMBER_INT), + 'fit' => filter_var($params['fit'], FILTER_SANITIZE_STRING), + 'height' => filter_var($params['height'], FILTER_SANITIZE_NUMBER_INT), + 'quality' => filter_var($params['quality'], FILTER_SANITIZE_STRING), + 'width' => filter_var($params['width'], FILTER_SANITIZE_NUMBER_INT), ]; - // validate parameters - if (! ArrayUtils::contains($thumbnailParams, [ - 'width', - 'height' - ])) { - throw new Exception('No height or width provided.'); + if($thumbnailParams['width'] == null || $thumbnailParams['width'] == '') + { + throw new Exception('No width is provided.'); + } + if($thumbnailParams['height'] == null || $thumbnailParams['height'] == '') + { + throw new Exception('No height is provided.'); + } + if($thumbnailParams['fit'] == null || $thumbnailParams['fit'] == '') + { + throw new Exception('No fit is provided.'); + } + if($thumbnailParams['quality'] == null || $thumbnailParams['quality'] == '') + { + throw new Exception('No quality is provided.'); } // make sure filename is valid - $filename = implode('/', array_slice($urlSegments, 4)); + $filename = $urlSegments[1]; $ext = pathinfo($filename, PATHINFO_EXTENSION); $name = pathinfo($filename, PATHINFO_FILENAME); if (! $this->isSupportedFileExtension($ext)) { @@ -305,6 +309,44 @@ public function extractThumbnailParams($thumbnailUrlPath) return $thumbnailParams; } + /** + * validate params against thumbnail whitelist + * + * @param array $params + * @throws Exception + * @return boolean + */ + + public function validateThumbnailWhitelist($params) + { + $userWhitelist = json_decode(ArrayUtils::get($this->getConfig(), 'asset_whitelist'), true); + + $thumbnailWhitelistEnabled = empty($userWhitelist) === false; + + if($thumbnailWhitelistEnabled) + { + $userWhitelist = json_decode($userWhitelist,true); + $result = false; + foreach($userWhitelist as $key=>$value) + { + if( + $value['width'] == $params['width'] && + $value['height'] == $params['height'] && + $value['fit'] == $params['fit'] && + $value['quality'] == $params['quality'] + ) + { + $result = true; + } + } + if(!$result){ + throw new Exception(); + } + } + + } + + /** * Translate quality text to number and return * diff --git a/src/core/Directus/GraphQL/FieldsConfig.php b/src/core/Directus/GraphQL/FieldsConfig.php index 524654c573..9353c435f0 100644 --- a/src/core/Directus/GraphQL/FieldsConfig.php +++ b/src/core/Directus/GraphQL/FieldsConfig.php @@ -50,7 +50,10 @@ public function getFields() $fields[$v['field']] = Types::directusFile(); break; case 'integer': - $fields[$v['field']] = ($v['interface'] == 'primary-key') ? $fields[$v['field']] = Types::id() : Types::int(); + $fields[$v['field']] = Types::int(); + if ($v['primary_key']) { + $fields[$v['field']] = Types::id(); + } break; case 'decimal': $fields[$v['field']] = Types::float(); diff --git a/src/core/Directus/Services/AbstractService.php b/src/core/Directus/Services/AbstractService.php index 213d3f1d7b..e1a01e86f3 100644 --- a/src/core/Directus/Services/AbstractService.php +++ b/src/core/Directus/Services/AbstractService.php @@ -193,7 +193,7 @@ protected function throwErrorIfAny(array $violations,$errorCode = "") * @param string $collectionName * @param array $fields List of columns name * @param array $skipRelatedCollectionField To skip parent collection field validation - * + * * @return array */ protected function createConstraintFor($collectionName, array $fields = [], $skipRelatedCollectionField = '', array $params = [] ) @@ -209,17 +209,17 @@ protected function createConstraintFor($collectionName, array $fields = [], $ski } foreach ($collectionObject->getFields($fields) as $field) { - //This condition is placed to skip alias validation field which is related to parent collection, + //This condition is placed to skip alias validation field which is related to parent collection, //to avoid nested payload validation for O2M and M2O collections - if($field->hasRelationship() && !empty($skipRelatedCollectionField) && (($field->getRelationship()->isManyToOne() && $field->getRelationship()->getCollectionOne() == $skipRelatedCollectionField) || ($field->getRelationship()->isOneToMany() && $field->getRelationship()->getCollectionMany() == $skipRelatedCollectionField))) + if($field->hasRelationship() && !empty($skipRelatedCollectionField) && (($field->getRelationship()->isManyToOne() && $field->getRelationship()->getCollectionOne() == $skipRelatedCollectionField) || ($field->getRelationship()->isOneToMany() && $field->getRelationship()->getCollectionMany() == $skipRelatedCollectionField))) continue; - + $columnConstraints = []; if ($field->hasAutoIncrement()) { continue; } - + if($field->isSystemDateTimeType() || $field->isSystemUserType()){ continue; } @@ -232,9 +232,12 @@ protected function createConstraintFor($collectionName, array $fields = [], $ski $isStatusField = $field->isStatusType(); if (!$isRequired && $isStatusField && $field->getDefaultValue() === null) { $isRequired = true; + } else if ($isRequired === true && $field->getDefaultValue() !== null) { + $isRequired = false; } - if ($isRequired || (!$field->isNullable() && $field->getDefaultValue() == null)) { + if ($isRequired || (!$field->isNullable() && $field->getDefaultValue() == null)) + { $columnConstraints[] = 'required'; } else if (in_array($field->getName(), $fields) && !$field->isNullable()) { $columnConstraints[] = 'notnullable'; @@ -370,13 +373,13 @@ protected function getCRUDParams(array $params) * @param array $payload * @param array $params * @param array $skipRelatedCollectionField To skip parent collection field validation - * + * * @throws UnprocessableEntityException */ protected function validatePayload($collectionName, $fields, array $payload, array $params, $skipRelatedCollectionField = '') { $columnsToValidate = []; - + // TODO: Validate empty request // If the user PATCH, POST or PUT with empty body, must throw an exception to avoid continue the execution // with the exception of POST, that can use the default value instead @@ -384,13 +387,13 @@ protected function validatePayload($collectionName, $fields, array $payload, arr if (is_array($fields)) { $columnsToValidate = $fields; } - + if($this->validationRequired($collectionName,$payload)){ $this->validatePayloadFields($collectionName, $payload); // TODO: Ideally this should be part of the validator constraints // we need to accept options for the constraint builder $this->validatePayloadWithFieldsValidation($collectionName, $payload); - + $this->validate($payload, $this->createConstraintFor($collectionName, $columnsToValidate, $skipRelatedCollectionField,$params)); } @@ -485,7 +488,7 @@ protected function validatePayloadWithFieldsValidation($collectionName, array $p } $this->validateFieldLength($field, $value); - + if ($validation = $field->getValidation()) { $isCustomValidation = \Directus\is_custom_validation($validation); @@ -512,10 +515,10 @@ protected function validatePayloadWithFieldsValidation($collectionName, array $p /** * To validate the length of input with the DB. - * + * * @param array $field * @param string $value - * + * * @throws UnprocessableEntityException */ protected function validateFieldLength($field, $value) @@ -538,8 +541,8 @@ protected function validateFieldLength($field, $value) sprintf("The value submitted (%s) for '%s' is longer than the field's supported length (%s). Please submit a shorter value or ask an Admin to increase the length.",$value,$field->getFormatisedName(),$field['length']) ); } - }else{ - if(!is_null($field['length']) && ((is_array($value) && $field['length'] < strlen(json_encode($value))) || (!is_array($value) && $field['length'] < strlen($value)))){ + } else { + if (!is_null($field['length']) && ((is_array($value) && $field['length'] < strlen(json_encode($value))) || (!is_array($value) && $field['length'] < mb_strlen($value, 'UTF-8')))){ throw new UnprocessableEntityException( sprintf("The value submitted (%s) for '%s' is longer than the field's supported length (%s). Please submit a shorter value or ask an Admin to increase the length.",!is_array($value) ? $value : 'Json / Array',$field->getFormatisedName(),$field['length']) ); diff --git a/src/core/Directus/Services/AssetService.php b/src/core/Directus/Services/AssetService.php new file mode 100644 index 0000000000..dab61d0e17 --- /dev/null +++ b/src/core/Directus/Services/AssetService.php @@ -0,0 +1,434 @@ +collection = SchemaManager::COLLECTION_FILES; + $this->filesystem =$this->container->get('filesystem'); + $this->filesystemThumb =$this->container->get('filesystem_thumb'); + $this->config = get_directus_thumbnail_settings(); + $this->thumbnailParams = []; + } + + public function getAsset($fileHashId, array $params = []) + { + $tableGateway = $this->createTableGateway($this->collection); + $select = new Select($this->collection); + $select->columns(['filename_disk', 'filename_download', 'id', 'type']); + $select->where(['private_hash' => $fileHashId]); + $select->limit(1); + $result = $tableGateway->ignoreFilters()->selectWith($select); + + if ($result->count() == 0) { + throw new ItemNotFoundException(); + } + + $file = $result->current()->toArray(); + + // Get original image + if (count($params) == 0) { + $lastModified = $this->filesystem->getAdapter()->getTimestamp($file['filename_disk']); + $lastModified = new DateTimeUtils(date('c', $lastModified)); + + $img = $this->filesystem->read($file['filename_disk']); + $result = []; + $result['last_modified'] = $lastModified->toRFC2616Format(); + $result['mimeType'] = $file['type']; + $result['file'] = isset($img) && $img ? $img : null; + $result['filename'] = $file['filename_disk']; + $result['filename_download'] = $file['filename_download']; + + return $result; + }else{ + + $this->fileName = $file['filename_disk']; + $this->fileNameDownload = $file['filename_download']; + try { + return $this->getThumbnail($params); + } + catch(Exception $e) + { + throw new UnprocessableEntityException(sprintf($e->getMessage())); + } + } + } + + public function getThumbnail($params) + { + $this->thumbnailParams=$params; + + $this->validateThumbnailParams($params); + + if (! $this->filesystem->exists($this->fileName)) { + throw new Exception($this->fileName . ' does not exist.'); + } + + $this->thumbnailDir = 'w' . $this->thumbnailParams['width'] . ',h' . $this->thumbnailParams['height'] . + ',f' . $this->thumbnailParams['fit'] . ',q' . $this->thumbnailParams['quality']; + + try { + $image = $this->getExistingThumbnail(); + + if (!$image) { + switch ($this->thumbnailParams['fit']) { + case 'contain': + $image = $this->contain(); + break; + case 'crop': + default: + $image = $this->crop(); + } + } + + $result['mimeType'] = $this->getThumbnailMimeType($this->thumbnailDir, $this->fileName); + $result['last_modified'] = $this->getThumbnailLastModified($this->thumbnailDir, $this->fileName); + $result['file'] = $image; + $result['filename_download'] = $this->fileNameDownload; + return $result; + } + catch (Exception $e) { + throw $e; + } + } + + /** + * validate params and file extensions and return it + * + * @param string $thumbnailUrlPath + * @param array $params + * @throws Exception + * @return array + */ + public function validateThumbnailParams($params) + { + // If the user provided the key param + $usesKey = isset($params['key']); + + // The user provided whitelist. If this is empty, any combination of the params is allowed + $userWhitelist = json_decode(ArrayUtils::get($this->getConfig(), 'asset_whitelist'), true); + + // The system native thumbnails that can't be changed + $systemWhitelist = json_decode(ArrayUtils::get($this->getConfig(), 'asset_whitelist_system'), true); + + // If the whitelist is set and therefore mandatory + $whitelistEnabled = empty($userWhitelist) == false; + + // All available sizes in the system + $allSizes = $whitelistEnabled ? array_merge($systemWhitelist, $userWhitelist) : $systemWhitelist; + + if ($usesKey) { + // Retrieve the sizes by the key that's passed + $exists = false; + + foreach($allSizes as $key => $value) { + if ($value['key'] == $params['key']) { + $exists = true; + $params = [ + 'w'=> $value['width'], + 'h' => $value['height'], + 'q' => $value['quality'], + 'f' => $value['fit'], + 'key' => $params['key'] + ]; + } + } + + if ($exists == false) { + throw new Exception(sprintf("Key doesn't exist.")); + } + + $this->thumbnailParams['key']= filter_var($params['key'], FILTER_SANITIZE_STRING); + } + + // We require all the params to be there when the key param isn't used + if ($usesKey == false) { + $this->validate( + [ + 'w' => isset($params['w']) ? $params['w'] : '', + 'h' => isset($params['h']) ? $params['h'] : '', + 'q' => isset($params['q']) ? $params['q'] : '', + 'f' => isset($params['f']) ? $params['f'] : '' + ], + [ + 'w' => 'required|numeric', + 'h' => 'required|numeric', + 'q' => 'required|numeric', + 'f' => 'required' + ] + ); + } + + // If the user didn't provide a key, and the whitelist items are required, + // verify if the passed keys match one of the predefined items + + if (!$usesKey && $whitelistEnabled) { + $exists = false; + + foreach($allSizes as $key => $value) { + if ( + $value['width'] == $params['w'] && + $value['height'] == $params['h'] && + $value['fit'] == $params['f'] && + $value['quality'] == $params['q'] + ) { + $exists = true; + } + } + + if (!$exists) { + throw new Exception(sprintf("The params don't match the asset whitelist.")); + } + } + + $this->thumbnailParams['fit']= filter_var($params['f'], FILTER_SANITIZE_STRING); + $this->thumbnailParams['height']= filter_var($params['h'], FILTER_SANITIZE_STRING); + $this->thumbnailParams['quality']= filter_var($params['q'], FILTER_SANITIZE_STRING); + $this->thumbnailParams['width']= filter_var($params['w'], FILTER_SANITIZE_STRING); + + $ext = pathinfo($this->fileName, PATHINFO_EXTENSION); + $name = pathinfo($this->fileName, PATHINFO_FILENAME); + + if (!$this->isSupportedFileExtension($ext)) { + throw new Exception('Invalid file extension.'); + } + + $this->thumbnailParams['format'] = strtolower(ArrayUtils::get($params, 'format') ?: $ext); + + if (!$this->isSupportedFileExtension($this->thumbnailParams['format'])) { + throw new Exception('Invalid file format.'); + } + + if ( + $this->thumbnailParams['format'] !== NULL && + strtolower($ext) !== $this->thumbnailParams['format'] && + !( + ($this->thumbnailParams['format'] == 'jpeg' || $this->thumbnailParams['format'] == 'jpg') && + (strtolower($ext) == 'jpeg' || strtolower($ext) == 'jpg') + ) + ) { + $this->thumbnailParams['thumbnailFileName'] = $name . '.' .$this->thumbnailParams['format']; + } else { + $this->thumbnailParams['thumbnailFileName'] = $this->fileName; + } + } + + /** + * Check if given file extension is supported + * + * @param int $ext + * @return boolean + */ + public function isSupportedFileExtension($ext) + { + return in_array(strtolower($ext), $this->getSupportedFileExtensions()); + } + + /** + * Return supported image file types + * + * @return array + */ + public function getSupportedFileExtensions() + { + return Thumbnail::getFormatsSupported(); + } + + /** + * Merge file and thumbnailer config settings and return + * + * @throws Exception + * @return array + */ + public function getConfig() + { + return $this->config; + } + + /** + * Return thumbnail as data + * + * @throws Exception + * @return string|null + */ + public function getExistingThumbnail() + { + try { + if( $this->filesystemThumb->exists($this->thumbnailDir . '/' . $this->thumbnailParams['thumbnailFileName']) ) { + $img = $this->filesystemThumb->read($this->thumbnailDir . '/' . $this->thumbnailParams['thumbnailFileName']); + } + return isset($img) && $img ? $img : null; + } + catch (Exception $e) { + throw $e; + } + } + + /** + * Replace PDF files with a JPG thumbnail + * @throws Exception + * @return string image content + */ + public function load () { + $content = $this->filesystem->read($this->fileName); + $ext = pathinfo($this->fileName, PATHINFO_EXTENSION); + if (Thumbnail::isNonImageFormatSupported($ext)) { + $content = Thumbnail::createImageFromNonImage($content); + } + return Image::make($content); + } + + /** + * Create thumbnail from image and `contain` + * http://image.intervention.io/api/resize + * https://css-tricks.com/almanac/properties/o/object-fit/ + * + * @throws Exception + * @return string + */ + public function contain() + { + try { + $img = $this->load(); + $img->resize($this->thumbnailParams['width'], $this->thumbnailParams['height'], function ($constraint) { + $constraint->aspectRatio(); + }); + $encodedImg = (string) $img->encode($this->thumbnailParams['format'], ($this->thumbnailParams['quality'] ? $this->thumbnailParams['quality'] : null)); + $this->filesystemThumb->write($this->thumbnailDir . '/' . $this->thumbnailParams['thumbnailFileName'], $encodedImg); + + return $encodedImg; + } + catch (Exception $e) { + throw $e; + } + } + + /** + * Create thumbnail from image and `crop` + * http://image.intervention.io/api/fit + * https://css-tricks.com/almanac/properties/o/object-fit/ + * + * @throws Exception + * @return string + */ + public function crop() + { + try { + $img = $this->load(); + $img->fit($this->thumbnailParams['width'],$this->thumbnailParams['height'], function($constraint){}); + $encodedImg = (string) $img->encode($this->thumbnailParams['format'], ($this->thumbnailParams['quality'] ? $this->thumbnailParams['quality'] : null)); + $this->filesystemThumb->write($this->thumbnailDir . '/' . $this->thumbnailParams['thumbnailFileName'], $encodedImg); + + return $encodedImg; + } + catch (Exception $e) { + throw $e; + } + } + + public function getDefaultThumbnail() + { + $basePath=$this->container->get('path_base'); + $filePath = ArrayUtils::get($this->config, 'thumbnail_not_found_location'); + if (is_string($filePath) && !empty($filePath) && $filePath[0] !== '/') { + $filePath = $basePath . '/' . $filePath; + } + + if (file_exists($filePath)) { + $result['mimeType'] = image_type_to_mime_type(exif_imagetype($filePath)); + $result['file']=file_get_contents($filePath); + $filename = pathinfo($filePath); + $result['filename']= $filename['basename']; + return $result; + } else { + return http_response_code(404); + } + } + + public function getThumbnailMimeType($path, $fileName) + { + try { + if($this->filesystemThumb->exists($path . '/' . $fileName) ) { + if(strtolower(pathinfo($fileName, PATHINFO_EXTENSION)) == 'webp') { + return 'image/webp'; + } + $img = Image::make($this->filesystemThumb->read($path. '/' . $fileName)); + return $img->mime(); + } + return 'application/octet-stream'; + } + + catch (Exception $e) { + throw $e; + } + } + + public function getThumbnailLastModified($path, $fileName) + { + try { + $lastModified = $this->filesystemThumb->getAdapter()->getTimestamp($path . '/' . $fileName); + $lastModified = new DateTimeUtils(date('c', $lastModified)); + return $lastModified->toRFC2616Format(); + } + + catch (Exception $e) { + throw $e; + } + } +} diff --git a/src/core/Directus/Services/FilesServices.php b/src/core/Directus/Services/FilesServices.php index ef0ffaff4a..654f20ecfa 100644 --- a/src/core/Directus/Services/FilesServices.php +++ b/src/core/Directus/Services/FilesServices.php @@ -11,8 +11,9 @@ use function Directus\get_directus_setting; use Directus\Util\ArrayUtils; use Directus\Util\DateTimeUtils; -use Directus\Filesystem\Files; use Directus\Validator\Exception\InvalidRequestException; +use function Directus\get_random_string; +use Directus\Application\Application; class FilesServices extends AbstractService { @@ -32,31 +33,34 @@ public function create(array $data, array $params = []) $this->enforceCreatePermissions($this->collection, $data, $params); $tableGateway = $this->createTableGateway($this->collection); - // These values are going to be set in a hook - // These fields should be ignore on validation as these values are automatically filled - // These values are set here to avoid validation - // FIXME: These values shouldn't not be validated $data['uploaded_by'] = $this->getAcl()->getUserId(); $data['uploaded_on'] = DateTimeUtils::now()->toString(); $validationConstraints = $this->createConstraintFor($this->collection); + // Do not validate filename when uploading files using URL // The filename will be generate automatically if not defined if (is_a_url(ArrayUtils::get($data, 'data'))) { - unset($validationConstraints['filename']); + unset($validationConstraints['filename_disk']); + unset($validationConstraints['filename_download']); } + $this->validate($data, array_merge(['data' => 'required'], $validationConstraints)); + $files = $this->container->get('files'); $result=$files->getFileSizeType($data['data']); if(get_directus_setting('file_mimetype_whitelist') != null){ validate_file($result['mimeType'],'mimeTypes'); } + if(get_directus_setting('file_max_size') != null){ validate_file($result['size'],'maxSize'); } - $newFile = $tableGateway->createRecord($data, $this->getCRUDParams($params)); + $recordData = $this->getSaveData($data, false); + + $newFile = $tableGateway->createRecord($recordData, $this->getCRUDParams($params)); return $tableGateway->wrapData( \Directus\append_storage_information($newFile->toArray()), @@ -65,6 +69,53 @@ public function create(array $data, array $params = []) ); } + public function getSaveData($data, $isUpdate){ + $dataInfo = []; + $files = $this->container->get('files'); + + if (array_key_exists('data', $data) && is_a_url($data['data'])) { + $dataInfo = $files->getLink($data['data']); + // Set the URL payload data + $data['data'] = ArrayUtils::get($dataInfo, 'data'); + $data['filename_disk'] = ArrayUtils::get($dataInfo, 'filename'); + $data['filename_download'] = ArrayUtils::get($dataInfo, 'filename'); + } else if (array_key_exists('data', $data) && !is_object($data['data'])) { + $dataInfo = $files->getDataInfo($data['data']); + } + + $type = ArrayUtils::get($dataInfo, 'type', ArrayUtils::get($data, 'type')); + + if (strpos($type, 'embed/') === 0) { + $recordData = $files->saveEmbedData(array_merge($dataInfo, ArrayUtils::pick($data, ['filename_disk']))); + } else { + $newFileContents = array_key_exists('data', $data) ? $data['data'] : null; + $recordData = $files->saveData($newFileContents, $data['filename_disk'], $isUpdate); + } + + // NOTE: Use the user input title, tags, description and location when exists. + $recordData = ArrayUtils::defaults($recordData, ArrayUtils::pick($data, [ + 'title', + 'tags', + 'description', + 'location', + ])); + + if(!$isUpdate){ + $recordData['private_hash'] = get_random_string(); + } + + return ArrayUtils::omit(array_merge($data,$recordData),['data','html']); + } + + protected function findByPrivateHash($hash) + { + $result = $this->createTableGateway(SchemaManager::COLLECTION_FILES, false)->fetchAll(function (Select $select) use ($hash) { + $select->columns(['filename_disk']); + $select->where(['private_hash' => $hash]); + })->current()->toArray(); + return $result; + } + public function find($id, array $params = []) { $tableGateway = $this->createTableGateway($this->collection); @@ -83,7 +134,7 @@ public function findByIds($id, array $params = []) public function update($id, array $data, array $params = []) { $this->enforceUpdatePermissions($this->collection, $data, $params); - + $this->checkItemExists($this->collection, $id); $this->validatePayload($this->collection, array_keys($data), $data, $params); @@ -91,7 +142,7 @@ public function update($id, array $data, array $params = []) if(isset($data['data'])){ $result=$files->getFileSizeType($data['data']); - + if(get_directus_setting('file_mimetype_whitelist') != null){ validate_file($result['mimeType'],'mimeTypes'); } @@ -99,12 +150,40 @@ public function update($id, array $data, array $params = []) validate_file($result['size'],'maxSize'); } } - + $tableGateway = $this->createTableGateway($this->collection); - $newFile = $tableGateway->updateRecord($id, $data, $this->getCRUDParams($params)); + + $currentItem = $tableGateway->getOneData($id); + $currentFileName = ArrayUtils::get($currentItem, 'filename_disk'); + + if (array_key_exists('filename_disk', $data) && $data['filename_disk'] !== $currentFileName) { + $oldFilePath = $currentFileName; + $newFilePath = $data['filename_disk']; + + try { + $this->container->get('filesystem')->getAdapter()->rename($oldFilePath, $newFilePath); + } catch(Exception $e) { + throw new InvalidRequestException($e); + } + } + + // getSaveData requires filename_disk to be set, in order for Filesystem/Files to read the file info + $fileName = $currentFileName; + + // If the user provided their own filename, the file has been renamed above. In that case, pass on + // the new filename + if (array_key_exists('filename_disk', $data)) { + $fileName = $data['filename_disk']; + } + + $data['filename_disk'] = $fileName; + + $recordData = $this->getSaveData($data, true); + + $newFile = $tableGateway->updateRecord($id, $recordData, $this->getCRUDParams($params)); return $tableGateway->wrapData( - \Directus\append_storage_information($newFile->toArray()), + \Directus\append_storage_information([$newFile->toArray()]), true, ArrayUtils::get($params, 'meta') ); @@ -124,7 +203,7 @@ public function delete($id, array $params = []) $files->delete($file); // Delete file record - return $tableGateway->deleteRecord($id); + return $tableGateway->deleteRecord($id,$this->getCRUDParams($params)); } public function findAll(array $params = []) @@ -134,75 +213,6 @@ public function findAll(array $params = []) return $this->getItemsAndSetResponseCacheTags($tableGateway, $params); } - public function createFolder(array $data, array $params = []) - { - $collection = 'directus_folders'; - $this->enforceCreatePermissions($collection, $data, $params); - $this->validatePayload($collection, null, $data, $params); - - $foldersTableGateway = $this->createTableGateway($collection); - - $newFolder = $foldersTableGateway->createRecord($data, $this->getCRUDParams($params)); - - return $foldersTableGateway->wrapData( - $newFolder->toArray(), - true, - ArrayUtils::get($params, 'meta') - ); - } - - public function findFolder($id, array $params = []) - { - $foldersTableGateway = $this->createTableGateway('directus_folders'); - $params['id'] = $id; - - return $this->getItemsAndSetResponseCacheTags($foldersTableGateway, $params); - } - - public function findFolderByIds($id, array $params = []) - { - $foldersTableGateway = $this->createTableGateway('directus_folders'); - - return $this->getItemsByIdsAndSetResponseCacheTags($foldersTableGateway, $id, $params); - } - - public function updateFolder($id, array $data, array $params = []) - { - $collectionName = 'directus_folders'; - $this->enforceUpdatePermissions($collectionName, $data, $params); - $this->checkItemExists($collectionName, $id); - - $foldersTableGateway = $this->createTableGateway('directus_folders'); - $group = $foldersTableGateway->updateRecord($id, $data, $this->getCRUDParams($params)); - - return $foldersTableGateway->wrapData( - $group->toArray(), - true, - ArrayUtils::get($params, 'meta') - ); - } - - public function findAllFolders(array $params = []) - { - $foldersTableGateway = $this->createTableGateway('directus_folders'); - - return $this->getItemsAndSetResponseCacheTags($foldersTableGateway, $params); - } - - public function deleteFolder($id, array $params = []) - { - $this->enforcePermissions('directus_folders', [], $params); - - $foldersTableGateway = $this->createTableGateway('directus_folders'); - // NOTE: check if item exists - // TODO: As noted in other places make a light function to check for it - $this->getItemsAndSetResponseCacheTags($foldersTableGateway, [ - 'id' => $id - ]); - - return $foldersTableGateway->deleteRecord($id, $this->getCRUDParams($params)); - } - /** * @param array $items * @param array $params diff --git a/src/core/Directus/Services/FoldersService.php b/src/core/Directus/Services/FoldersService.php new file mode 100644 index 0000000000..1dafe6b009 --- /dev/null +++ b/src/core/Directus/Services/FoldersService.php @@ -0,0 +1,89 @@ +collection = SchemaManager::COLLECTION_FOLDERS; + } + + public function createFolder(array $data, array $params = []) + { + $this->enforceCreatePermissions($this->$collection, $data, $params); + $this->validatePayload($this->$collection, null, $data, $params); + + $foldersTableGateway = $this->createTableGateway($this->$collection); + + $newFolder = $foldersTableGateway->createRecord($data, $this->getCRUDParams($params)); + + return $foldersTableGateway->wrapData( + $newFolder->toArray(), + true, + ArrayUtils::get($params, 'meta') + ); + } + + public function findFolder($id, array $params = []) + { + $foldersTableGateway = $this->createTableGateway('directus_folders'); + $params['id'] = $id; + + return $this->getItemsAndSetResponseCacheTags($foldersTableGateway, $params); + } + + public function findFolderByIds($id, array $params = []) + { + $foldersTableGateway = $this->createTableGateway('directus_folders'); + + return $this->getItemsByIdsAndSetResponseCacheTags($foldersTableGateway, $id, $params); + } + + public function updateFolder($id, array $data, array $params = []) + { + $this->enforceUpdatePermissions($this->collection, $data, $params); + $this->checkItemExists($this->collection, $id); + + $foldersTableGateway = $this->createTableGateway('directus_folders'); + $group = $foldersTableGateway->updateRecord($id, $data, $this->getCRUDParams($params)); + + return $foldersTableGateway->wrapData( + $group->toArray(), + true, + ArrayUtils::get($params, 'meta') + ); + } + + public function findAllFolders(array $params = []) + { + $foldersTableGateway = $this->createTableGateway('directus_folders'); + + return $this->getItemsAndSetResponseCacheTags($foldersTableGateway, $params); + } + + public function deleteFolder($id, array $params = []) + { + $this->enforcePermissions($this->collection, [], $params); + + $foldersTableGateway = $this->createTableGateway('directus_folders'); + // NOTE: check if item exists + // TODO: As noted in other places make a light function to check for it + $this->getItemsAndSetResponseCacheTags($foldersTableGateway, [ + 'id' => $id + ]); + + return $foldersTableGateway->deleteRecord($id, $this->getCRUDParams($params)); + } +} diff --git a/src/core/Directus/Services/PagesService.php b/src/core/Directus/Services/ModulesService.php similarity index 67% rename from src/core/Directus/Services/PagesService.php rename to src/core/Directus/Services/ModulesService.php index af3f94d6d4..b0d10489cc 100644 --- a/src/core/Directus/Services/PagesService.php +++ b/src/core/Directus/Services/ModulesService.php @@ -4,7 +4,7 @@ use Directus\Application\Container; -class PagesService extends AbstractExtensionsController +class ModulesService extends AbstractExtensionsController { public function __construct(Container $container) { @@ -12,8 +12,8 @@ public function __construct(Container $container) $basePath = $this->container->get('path_base'); $this->paths = [ - $basePath . '/public/extensions/core/pages', - $basePath . '/public/extensions/custom/pages', + $basePath . '/public/extensions/core/modules', + $basePath . '/public/extensions/custom/modules', ]; } diff --git a/src/core/Directus/Services/ProjectService.php b/src/core/Directus/Services/ProjectService.php index 38b17b7e4e..9148314a33 100644 --- a/src/core/Directus/Services/ProjectService.php +++ b/src/core/Directus/Services/ProjectService.php @@ -14,6 +14,7 @@ use Directus\Exception\UnprocessableEntityException; use Directus\Util\ArrayUtils; use Directus\Util\Installation\InstallerUtils; +use Directus\Util\StringUtils; class ProjectService extends AbstractService { @@ -61,10 +62,13 @@ public function create(array $data) // If the first installtion is executing then add the api.json file to store the password. // For every installation after the first one, user must pass that same password to create the next project. - $scannedDirectory = \Directus\scan_config_folder(); + $basePath = \Directus\get_app_base_path(); + $scannedDirectory = \Directus\scan_folder($basePath.'/config'); - $superadminFilePath = \Directus\get_app_base_path().'/config/__api.json'; - if(empty($scannedDirectory)){ + $projectNames = $scannedDirectory; + + $superadminFilePath = $basePath.'/config/__api.json'; + if(empty($projectNames)){ $configStub = InstallerUtils::createJsonFileContent($data); file_put_contents($superadminFilePath, $configStub); }else{ @@ -123,7 +127,7 @@ public function create(array $data) public function delete(Request $request) { $data = $request->getQueryParams(); - + $this->validate($data,[ 'super_admin_token' => 'required', ]); diff --git a/src/core/Directus/Services/ServerService.php b/src/core/Directus/Services/ServerService.php index 5b61d3b52d..33b2d2122d 100644 --- a/src/core/Directus/Services/ServerService.php +++ b/src/core/Directus/Services/ServerService.php @@ -6,6 +6,7 @@ use Directus\Exception\UnauthorizedException; use function Directus\get_project_info; use Directus\Services\UsersService; +use Directus\Util\StringUtils; class ServerService extends AbstractService { @@ -67,10 +68,15 @@ public function findAllInfo($global = true, $configuration = null) */ public function validateServerInfo($data) { - $scannedDirectory = \Directus\scan_config_folder(); - - $superadminFilePath = \Directus\get_app_base_path().'/config/__api.json'; - if(!empty($scannedDirectory)){ + $basePath = \Directus\get_app_base_path(); + + $scannedDirectory = \Directus\scan_folder($basePath.'/config'); + + $projectNames = $scannedDirectory; + + $superadminFilePath = $basePath.'/config/__api.json'; + + if(!empty($projectNames)){ $this->validate($data, [ 'super_admin_token' => 'required' ]); diff --git a/src/core/Directus/Services/SettingsService.php b/src/core/Directus/Services/SettingsService.php index 331c639350..b76946eabb 100644 --- a/src/core/Directus/Services/SettingsService.php +++ b/src/core/Directus/Services/SettingsService.php @@ -3,7 +3,9 @@ namespace Directus\Services; use Directus\Application\Container; +use function Directus\get_directus_setting; use Directus\Database\Schema\SchemaManager; +use Directus\Exception\UnprocessableEntityException; class SettingsService extends AbstractService { diff --git a/src/core/Directus/Services/TablesService.php b/src/core/Directus/Services/TablesService.php index 266b86ba10..79dd9e4dd5 100644 --- a/src/core/Directus/Services/TablesService.php +++ b/src/core/Directus/Services/TablesService.php @@ -746,23 +746,35 @@ public function dropColumn($collectionName, $fieldName, array $params = []) throw new UnprocessableEntityException('Cannot delete the last field'); } - if (!$columnObject->isAlias()) { + if ($columnObject->isAlias() === false) { if (!$this->dropColumnSchema($collectionName, $fieldName)) { throw new ErrorException('Error deleting the field'); } } - if ($columnObject->hasRelationship() && !in_array($columnObject->getType(),DataTypes::getUsersType())) { - $this->removeColumnRelationship($columnObject,$params); + if ( + $columnObject->hasRelationship() && + // Don't remove relational columns for native relationships (users / files) + DataTypes::isUsersType($columnObject->getType()) === false && + DataTypes::isFilesType($columnObject->getType()) === false + ) { + $this->removeColumnRelationship($columnObject, $params); } if ($columnObject->isManaged()) { /** * Remove O2M field if M2O interface deleted as O2M will only work if M2O exist */ - if($columnObject->isManyToOne() && !in_array($columnObject->getType(),DataTypes::getUsersType())){ + + if( + $columnObject->isManyToOne() && + // Don't remove relational columns for native relationships (users / files) + DataTypes::isUsersType($columnObject->getType()) === false && + DataTypes::isFilesType($columnObject->getType()) === false + ) { $this->removeRelatedColumnInfo($columnObject); } + $this->removeColumnInfo($collectionName, $fieldName); } } diff --git a/src/core/Directus/Services/UsersService.php b/src/core/Directus/Services/UsersService.php index e83f6d5489..503634a714 100644 --- a/src/core/Directus/Services/UsersService.php +++ b/src/core/Directus/Services/UsersService.php @@ -25,6 +25,7 @@ use Zend\Db\Sql\Select; use function Directus\get_directus_setting; use Directus\Validator\Exception\InvalidRequestException; +use Zend\Db\TableGateway\TableGateway; class UsersService extends AbstractService { @@ -380,22 +381,26 @@ protected function isLastAdmin($id) public function has2FAEnforced($id) { try { - $result = $this->createTableGateway(SchemaManager::COLLECTION_ROLES, false)->fetchAll(function (Select $select) use ($id) { - $select->columns(['enforce_2fa']); - $select->where(['ur.id' => $id]); - $select->join( - ['ur' => SchemaManager::COLLECTION_USERS], - sprintf('ur.role = %s.id', SchemaManager::COLLECTION_ROLES), - [ - 'role' - ] - ); - - }); - - $enforce_2fa = $result->current()['enforce_2fa']; - - if ($enforce_2fa == null || $enforce_2fa == 0) { + $dbConnection = $this->container->get('database'); + $tableGateway = new TableGateway(SchemaManager::COLLECTION_ROLES, $dbConnection); + $select = new Select(['r' => SchemaManager::COLLECTION_ROLES]); + $select->columns(['enforce_2fa']); + + $subSelect = new Select(['ur' => 'directus_users']); + $subSelect->where->equalTo('ur.id', $id); + $subSelect->limit(1); + + $select->join( + ['ur' => $subSelect], + 'r.id = ur.role', + [ + 'role' + ] + ); + + $result = $tableGateway->selectWith($select)->current()['enforce_2fa']; + + if (empty($result)) { return false; } else { return true; diff --git a/src/core/Directus/Util/DateTimeUtils.php b/src/core/Directus/Util/DateTimeUtils.php index 522547362f..7c72ad7116 100644 --- a/src/core/Directus/Util/DateTimeUtils.php +++ b/src/core/Directus/Util/DateTimeUtils.php @@ -107,18 +107,6 @@ public static function nowInUTC() return static::now('UTC'); } - public static function nowInTimezone() - { - $projectName = \Directus\get_api_project_from_request(); - if(!is_null($projectName)){ - $config = get_project_config($projectName); - return static::now($config->get('app.timezone')); - } else { - // If there's no project config (f.e. when creating projects), default to UTC - return static::now('UTC'); - } - } - /** * Creates a new DateTimeUtils instance from a \DateTime instance * @@ -293,6 +281,9 @@ public function toISO8601Format() return $this->format('c'); } + public function toRFC2616Format() { + return $this->format('D, d M Y H:i:s T'); + } /** * Returns a string format of the datetime * diff --git a/src/core/Directus/Util/Installation/InstallerUtils.php b/src/core/Directus/Util/Installation/InstallerUtils.php index fd0de044cd..d27da2f91d 100644 --- a/src/core/Directus/Util/Installation/InstallerUtils.php +++ b/src/core/Directus/Util/Installation/InstallerUtils.php @@ -31,6 +31,7 @@ class InstallerUtils { const MIGRATION_CONFIGURATION_PATH = '/migrations/migrations.php'; + const MIGRATION_UPGRADE_CONFIGURATION_PATH = '/migrations/migrations.upgrades.php'; /** * Add the upgraded migrations into directus_migration on a fresh installation of project. * Upgraded migrations may contain the same queries which is in db [Original] migrations. @@ -39,9 +40,20 @@ class InstallerUtils * * @return boolean */ - public static function addUpgradeMigrations() + public static function addUpgradeMigrations($basePath = null, $projectName = null) { - $dbConnection = Application::getInstance()->fromContainer('database'); + + if ( + static::isUsingFiles() == false || + (!is_null($basePath) && !is_null($projectName)) + ) { + $app = static::createApp($basePath, $projectName); + $dbConnection = $app->getContainer()->get('database'); + } else { + $basePath = \Directus\get_app_base_path(); + $dbConnection = Application::getInstance()->fromContainer('database'); + } + $migrationsTableGateway = new TableGateway(SchemaManager::COLLECTION_MIGRATIONS, $dbConnection); $select = new Select($migrationsTableGateway->table); @@ -49,17 +61,18 @@ public static function addUpgradeMigrations() $result = $migrationsTableGateway->selectWith($select)->toArray(); $alreadyStoredMigrations = array_column($result, 'version'); - $ignoreableFiles = ['..', '.']; - $scannedDirectory = array_values(array_diff(scandir(\Directus\get_app_base_path().'/migrations/upgrades/schemas'), $ignoreableFiles)); + $ignoreableFiles = ['..', '.', '.DS_Store']; + $scannedDirectory = array_values(array_diff(scandir($basePath.'/migrations/upgrades'), $ignoreableFiles)); foreach($scannedDirectory as $fileName){ $data = []; - $fileNameObject = explode("_",$fileName,2); - $migrationName = explode(".",str_replace(' ', '',ucwords(str_replace('_', ' ', $fileNameObject[1]))),2); + $fileNameObject = explode("_", $fileName, 2); + $migrationName = explode(".", str_replace(' ', '', ucwords(str_replace('_', ' ', $fileNameObject[1]))), 2); + $data = [ 'version' => $fileNameObject[0], 'migration_name' => $migrationName[0], - 'start_time' => DateTimeUtils::nowInTimezone()->toString(), - 'end_time' => DateTimeUtils::nowInTimezone()->toString() + 'start_time' => DateTimeUtils::nowInUTC()->toString(), + 'end_time' => DateTimeUtils::nowInUTC()->toString() ]; if(!in_array($data['version'],$alreadyStoredMigrations) && !is_null($data['version']) && !is_null($data['migration_name'])){ @@ -165,6 +178,8 @@ public static function replacePlaceholderValues($content, $data) * * @param string $path * @param array $data + * The JSON that the user POSTed to the create-project endpoint + * Required in here are the database credentials * @param bool $force */ public static function ensureCanCreateTables($path, array $data, $force = false) @@ -227,7 +242,9 @@ public static function runMigrationAndSeeder(Config $config) { $manager = new Manager($config, new StringInput(''), new NullOutput()); $manager->migrate('development'); - $manager->seed('development'); + if(!empty($config['paths']['seeds'])){ + $manager->seed('development'); + } } /** @@ -705,14 +722,11 @@ private static function createConfigFile($path, array $data, $force = false) * * @return Config */ - private static function getMigrationConfig($basePath, $projectName = null, $migrationName = null) + private static function getMigrationConfig($basePath, $projectName = null, $migrationName = 'install') { static::ensureConfigFileExists($basePath, $projectName); static::ensureMigrationFileExists($basePath); - if ($migrationName === null) { - $migrationName = 'db'; - } $configPath = static::createConfigPath($basePath, $projectName); $migrationPath = $basePath . '/migrations/' . $migrationName; @@ -729,9 +743,8 @@ private static function getMigrationConfig($basePath, $projectName = null, $migr ArrayUtils::rename($apiConfig, 'socket', 'unix_socket'); $apiConfig['charset'] = ArrayUtils::get($apiConfig, 'database.charset', 'utf8mb4'); - $configArray = require $basePath.self::MIGRATION_CONFIGURATION_PATH ; - $configArray['paths']['migrations'] = $migrationPath . '/schemas'; - $configArray['paths']['seeds'] = $migrationPath . '/seeds'; + $configArray = $migrationName == "install" ? require $basePath.self::MIGRATION_CONFIGURATION_PATH :require $basePath.self::MIGRATION_UPGRADE_CONFIGURATION_PATH; + $configArray['paths']['migrations'] = $migrationPath; $configArray['environments']['development'] = $apiConfig; return new Config($configArray); @@ -957,8 +970,6 @@ private static function createConfigData(array $data) 'db_password' => null, 'db_socket' => '', 'mail_from' => 'admin@example.com', - 'feedback_token' => sha1(gmdate('U') . StringUtils::randomString(32, false)), - 'feedback_login' => true, 'timezone' => get_default_timezone(), 'logs_path' => __DIR__ . '/../../../../../logs', 'cache' => [ @@ -969,7 +980,7 @@ private static function createConfigData(array $data) 'adapter' => 'local', 'root' => 'public/uploads/{{project}}/originals', 'root_url' => '/uploads/{{project}}/originals', - 'thumb_root' => 'public/uploads/{{project}}/thumbnails', + 'thumb_root' => 'public/uploads/{{project}}/generated', ], 'mail' => [ 'transport' => 'sendmail', diff --git a/src/core/Directus/Util/Installation/stubs/config.stub b/src/core/Directus/Util/Installation/stubs/config.stub index 6613eca69e..84efcb06f1 100644 --- a/src/core/Directus/Util/Installation/stubs/config.stub +++ b/src/core/Directus/Util/Installation/stubs/config.stub @@ -1,17 +1,6 @@ [ - 'env' => 'production', - 'timezone' => '{{timezone}}', - ], - - 'settings' => [ - 'logger' => [ - 'path' => '{{logs_path}}', - ], - ], - 'database' => [ 'type' => '{{db_type}}', 'host' => '{{db_host}}', @@ -21,61 +10,34 @@ return [ 'password' => '{{db_password}}', 'engine' => 'InnoDB', 'charset' => 'utf8mb4', - // When using unix socket to connect to the database the host attribute should be removed - // 'socket' => '/var/lib/mysql/mysql.sock', + // Remove 'host' above when using sockets 'socket' => '{{db_socket}}', ], - 'cache' => [ - 'enabled' => {{cache.enabled}}, - 'response_ttl' => {{cache.response_ttl}}, // seconds - 'pool' => [ - {{optional(cache.adapter)}}, - {{optional(cache.path)}}, - {{optional(cache.host)}}, - {{optional(cache.port)}}, - ], - // 'pool' => [ - // 'adapter' => 'apc' - // ], - // 'pool' => [ - // 'adapter' => 'apcu' - // ], - // 'pool' => [ - // 'adapter' => 'filesystem', - // 'path' => 'cache', // relative to the api directory - // ], - // 'pool' => [ - // 'adapter' => 'memcached', - // //'url' => 'localhost:11211;localhost:11212' - // 'host' => 'localhost', - // 'port' => 11211 - // ], - // 'pool' => [ - // 'adapter' => 'memcache', - // 'url' => 'localhost:11211;localhost:11212' - // //'host' => 'localhost', - // //'port' => 11211 - // ], - // 'pool' => [ - // 'adapter' => 'redis', - // 'host' => 'localhost', - // 'port' => 6379 - // ], + 'cors' => [ + 'enabled' => {{cors.enabled}}, + 'origin' => {{cors.origin}}, + 'methods' => {{cors.methods}}, + 'headers' => {{cors.headers}}, + 'exposed_headers' => {{cors.exposed_headers}}, + 'max_age' => {{cors.max_age}}, + 'credentials' => {{cors.credentials}}, + ], + + 'rate_limit' => [ + 'enabled' => {{rate_limit.enabled}}, + 'limit' => {{rate_limit.limit}}, + 'interval' => {{rate_limit.interval}}, + {{optional(rate_limit.adapter)}}, + {{optional(rate_limit.host)}}, + {{optional(rate_limit.port)}}, + {{optional(rate_limit.timeout)}}, ], 'storage' => [ 'adapter' => '{{storage.adapter}}', - // The storage root is the directus root directory. - // All path are relative to the storage root when the path is not starting with a forward slash. - // By default the uploads directory is located at the directus public root - // An absolute path can be used as alternative. 'root' => '{{storage.root}}', - // This is the url where all the media will be pointing to - // here is where Directus will assume all assets will be accessed - // Ex: (yourdomain)/uploads/_/originals 'root_url' => '{{storage.root_url}}', - // Same as "root", but for the thumbnails 'thumb_root' => '{{storage.thumb_root}}', {{optional(storage.key)}}, {{optional(storage.secret)}}, @@ -83,9 +45,7 @@ return [ {{optional(storage.version)}}, {{optional(storage.bucket)}}, {{optional(storage.options)}}, - // Set custom S3 endpoint {{optional(storage.endpoint)}}, - // Use an internal proxy for downloading all files {{optional(storage.proxy_downloads)}}, ], @@ -102,24 +62,27 @@ return [ ], ], - 'cors' => [ - 'enabled' => {{cors.enabled}}, - 'origin' => {{cors.origin}}, - 'methods' => {{cors.methods}}, - 'headers' => {{cors.headers}}, - 'exposed_headers' => {{cors.exposed_headers}}, - 'max_age' => {{cors.max_age}}, // in seconds - 'credentials' => {{cors.credentials}}, + 'cache' => [ + 'enabled' => {{cache.enabled}}, + 'response_ttl' => {{cache.response_ttl}}, + 'pool' => [ + {{optional(cache.adapter)}}, + {{optional(cache.path)}}, + {{optional(cache.host)}}, + {{optional(cache.port)}}, + ], ], - 'rate_limit' => [ - 'enabled' => {{rate_limit.enabled}}, - 'limit' => {{rate_limit.limit}}, // number of request - 'interval' => {{rate_limit.interval}}, // seconds - {{optional(rate_limit.adapter)}}, - {{optional(rate_limit.host)}}, - {{optional(rate_limit.port)}}, - {{optional(rate_limit.timeout)}}, + 'auth' => [ + 'secret_key' => '{{auth.secret}}', + 'public_key' => '{{auth.public}}', + 'social_providers' => [ + {{optional(auth.social_providers.okta)}}, + {{optional(auth.social_providers.github)}}, + {{optional(auth.social_providers.facebook)}}, + {{optional(auth.social_providers.google)}}, + {{optional(auth.social_providers.twitter)}}, + ], ], 'hooks' => [ @@ -127,43 +90,11 @@ return [ 'filters' => [], ], - 'feedback' => [ - 'token' => '{{feedback_token}}', - 'login' => {{feedback_login}} - ], - - // These tables will not be loaded in the directus schema 'tableBlacklist' => [], - 'auth' => [ - 'secret_key' => '{{auth.secret}}', - 'public_key' => '{{auth.public}}', - {{optional(auth.social_providers)}}, - // 'okta' => [ - // 'client_id' => '', - // 'client_secret' => '', - // 'base_url' => 'https://dev-000000.oktapreview.com/oauth2/default' - // ], - // 'github' => [ - // 'client_id' => '', - // 'client_secret' => '' - // ], - // 'facebook' => [ - // 'client_id' => '', - // 'client_secret' => '', - // 'graph_api_version' => 'v2.8', - // ], - // 'google' => [ - // 'client_id' => '', - // 'client_secret' => '', - // 'hosted_domain' => '*', - // // Uses OpenIDConnect to fetch the email instead of using the Google+ API - // // Disabling the OIDC Mode, requires you to enable the Google+ API otherwise it will fail - // 'use_oidc_mode' => true, - // ], - // 'twitter' => [ - // 'identifier' => '', - // 'secret' => '' - // ] + 'env' => 'production', + + 'logger' => [ + 'path' => '{{logs_path}}', ], ]; diff --git a/src/core/Directus/Util/MimeTypeUtils.php b/src/core/Directus/Util/MimeTypeUtils.php index 4b7027e56e..d438c5a684 100644 --- a/src/core/Directus/Util/MimeTypeUtils.php +++ b/src/core/Directus/Util/MimeTypeUtils.php @@ -2365,7 +2365,7 @@ class MimeTypeUtils 'pcx' => ['image/vnd.zbrush.pcx', 'image/x-pcx'], 'pdb' => ['application/vnd.palm', 'application/x-aportisdoc', 'application/x-palm-database'], 'pdc' => ['application/x-aportisdoc'], - 'pdf' => ['application/acrobat', 'application/nappdf', 'application/pdf', 'application/x-pdf', 'image/pdf'], + 'pdf' => ['application/pdf', 'application/acrobat', 'application/nappdf', 'application/x-pdf', 'image/pdf'], 'pdf.bz2' => ['application/x-bzpdf'], 'pdf.gz' => ['application/x-gzpdf'], 'pdf.lz' => ['application/x-lzpdf'], diff --git a/src/endpoints/Assets.php b/src/endpoints/Assets.php new file mode 100644 index 0000000000..d6938a1918 --- /dev/null +++ b/src/endpoints/Assets.php @@ -0,0 +1,38 @@ +container); + $fileId = $request->getAttribute('id'); + + $asset = $service->getAsset( + $fileId, + $request->getQueryParams() + ); + + if(isset($asset['file']) && $asset['mimeType']) + { + $response->setHeader('Content-type',$asset['mimeType']); + $response->setHeader('Content-Disposition','filename='.$asset['filename_download']); + $response->setHeader('Last-Modified',$asset['last_modified']); + + $body = $response->getBody(); + $body->write($asset['file']); + + return $response; + } + else + { + return $response->withStatus(404); + } + } +} diff --git a/src/endpoints/Auth.php b/src/endpoints/Auth.php index a0c5804873..deef2bfcce 100644 --- a/src/endpoints/Auth.php +++ b/src/endpoints/Auth.php @@ -76,7 +76,7 @@ public function authenticate(Request $request, Response $response) default : $this->storeJwtSession($responseData['data']); } - unset($responseData['data']['user']); + $responseData['data']['user'] = ArrayUtils::omit($responseData['data']['user'], [ 'password' , 'token', 'email_notifications', 'last_access_on', 'last_page']); } $responseData['data'] = !empty($responseData['data']) ? $responseData['data'] : null; @@ -125,7 +125,7 @@ public function storeCookieSession($request,$response,$data){ $userSessionObject = $userSessionService->find(['token' => $accessToken]); $sessionToken = $userSessionObject['token']; }else{ - + $userSession = $userSessionService->create([ 'user' => $data['user']['id'], 'token' => $data['user']['token'], @@ -417,8 +417,14 @@ public function ssoServiceCallback(Request $request, Response $response) $urlParams['attributes'] = $e->getAttributes(); } - $urlParams['code'] = ($e instanceof \Directus\Exception\Exception) ? $e->getErrorCode() : 0; + $urlParams['code'] = ($e instanceof \Directus\Exception\Exception) ? $e->getErrorCode() : -1; $urlParams['error'] = true; + + // Log error to the error file if it's not coming from Directus. This allows the user to debug + // errors coming from the service provider + if ($e instanceof \Directus\Exception\Exception === false) { + $this->container->get('logger')->error($e); + } } diff --git a/src/endpoints/Fields.php b/src/endpoints/Fields.php index d05ee406c7..14819c4858 100644 --- a/src/endpoints/Fields.php +++ b/src/endpoints/Fields.php @@ -169,11 +169,11 @@ public function all(Request $request, Response $response) */ public function delete(Request $request, Response $response) { - + $service = new TablesService($this->container); $field = $service->getFieldObject($request->getAttribute('collection'),$request->getAttribute('field')); - + $service->deleteField( $request->getAttribute('collection'), $request->getAttribute('field'), @@ -191,7 +191,6 @@ public function delete(Request $request, Response $response) $collectionsPermission = $permissionService->findAll($filter); $permissionId = array_column($collectionsPermission['data'], 'id'); $permissionService->batchDeleteWithIds($permissionId); - } return $this->responseWithData($request, $response, []); diff --git a/src/endpoints/Files.php b/src/endpoints/Files.php index 212b5398ed..69f595351e 100644 --- a/src/endpoints/Files.php +++ b/src/endpoints/Files.php @@ -25,22 +25,12 @@ class Files extends Route public function __invoke(Application $app) { $app->post('', [$this, 'create']); - $app->get('/{id:' . regex_numeric_ids() . '}', [$this, 'read']); - $app->patch('/{id:' . regex_numeric_ids() . '}', [$this, 'update']); + $app->get('/{id}', [$this, 'read']); + $app->patch('/{id}', [$this, 'update']); $app->patch('', [$this, 'update']); - $app->delete('/{id:' . regex_numeric_ids() . '}', [$this, 'delete']); + $app->delete('/{id}', [$this, 'delete']); $app->get('', [$this, 'all']); - // Folders - $controller = $this; - $app->group('/folders', function () use ($controller) { - $this->post('', [$controller, 'createFolder']); - $this->get('/{id:[0-9]+}', [$controller, 'readFolder']); - $this->patch('/{id:[0-9]+}', [$controller, 'updateFolder']); - $this->delete('/{id:[0-9]+}', [$controller, 'deleteFolder']); - $this->get('', [$controller, 'allFolder']); - }); - // Revisions $app->get('/{id}/revisions', [$this, 'fileRevisions']); $app->get('/{id}/revisions/{offset}', [$this, 'oneFileRevision']); @@ -74,7 +64,8 @@ public function create(Request $request, Response $response) // TODO: the file already exists move it to the upload path location $payload = array_merge([ - 'filename' => $uploadedFile->getClientFilename(), + 'filename_disk' => $uploadedFile->getClientFilename(), + 'filename_download' => $uploadedFile->getClientFilename(), 'data' => $uploadedFile, ], $payload); } @@ -130,7 +121,6 @@ public function update(Request $request, Response $response) $request->getParsedBody(), $request->getQueryParams() ); - return $this->responseWithData($request, $response, $responseData); } @@ -164,93 +154,6 @@ public function all(Request $request, Response $response) return $this->responseWithData($request, $response, $responseData); } - /** - * @param Request $request - * @param Response $response - * - * @return Response - */ - public function createFolder(Request $request, Response $response) - { - $this->validateRequestPayload($request); - $service = new FilesServices($this->container); - $responseData = $service->createFolder( - $request->getParsedBody(), - $request->getQueryParams() - ); - - return $this->responseWithData($request, $response, $responseData); - } - - /** - * @param Request $request - * @param Response $response - * - * @return Response - */ - public function readFolder(Request $request, Response $response) - { - $service = new FilesServices($this->container); - $responseData = $service->findFolderByIds( - $request->getAttribute('id'), - ArrayUtils::pick($request->getQueryParams(), ['fields', 'meta']) - ); - - return $this->responseWithData($request, $response, $responseData); - } - - /** - * @param Request $request - * @param Response $response - * - * @return Response - */ - public function updateFolder(Request $request, Response $response) - { - $this->validateRequestPayload($request); - $service = new FilesServices($this->container); - $responseData = $service->updateFolder( - $request->getAttribute('id'), - $request->getParsedBody(), - $request->getQueryParams() - ); - - return $this->responseWithData($request, $response, $responseData); - } - - /** - * @param Request $request - * @param Response $response - * - * @return Response - */ - public function allFolder(Request $request, Response $response) - { - $service = new FilesServices($this->container); - $responseData = $service->findAllFolders( - $request->getQueryParams() - ); - - return $this->responseWithData($request, $response, $responseData); - } - - /** - * @param Request $request - * @param Response $response - * - * @return Response - */ - public function deleteFolder(Request $request, Response $response) - { - $service = new FilesServices($this->container); - $service->deleteFolder( - $request->getAttribute('id'), - $request->getQueryParams() - ); - - return $this->responseWithData($request, $response, []); - } - /** * @param Request $request * @param Response $response diff --git a/src/endpoints/Folders.php b/src/endpoints/Folders.php new file mode 100644 index 0000000000..545894f6c2 --- /dev/null +++ b/src/endpoints/Folders.php @@ -0,0 +1,114 @@ +get('', [$this, 'allFolders']); + $app->post('', [$this, 'createFolder']); + $app->get('/{id:[0-9]+}', [$this, 'readFolder']); + $app->patch('/{id:[0-9]+}', [$this, 'updateFolder']); + $app->delete('/{id:[0-9]+}', [$this, 'deleteFolder']); + } + + /** + * @param Request $request + * @param Response $response + * + * @return Response + */ + public function allFolders(Request $request, Response $response) + { + $service = new FoldersService($this->container); + $responseData = $service->findAllFolders( + $request->getQueryParams() + ); + + return $this->responseWithData($request, $response, $responseData); + } + + /** + * @param Request $request + * @param Response $response + * + * @return Response + */ + public function createFolder(Request $request, Response $response) + { + $this->validateRequestPayload($request); + $service = new FoldersService($this->container); + $responseData = $service->createFolder( + $request->getParsedBody(), + $request->getQueryParams() + ); + + return $this->responseWithData($request, $response, $responseData); + } + + /** + * @param Request $request + * @param Response $response + * + * @return Response + */ + public function readFolder(Request $request, Response $response) + { + $service = new FoldersService($this->container); + $responseData = $service->findFolderByIds( + $request->getAttribute('id'), + ArrayUtils::pick($request->getQueryParams(), ['fields', 'meta']) + ); + + return $this->responseWithData($request, $response, $responseData); + } + + /** + * @param Request $request + * @param Response $response + * + * @return Response + */ + public function updateFolder(Request $request, Response $response) + { + $this->validateRequestPayload($request); + $service = new FoldersService($this->container); + $responseData = $service->updateFolder( + $request->getAttribute('id'), + $request->getParsedBody(), + $request->getQueryParams() + ); + + return $this->responseWithData($request, $response, $responseData); + } + + + /** + * @param Request $request + * @param Response $response + * + * @return Response + */ + public function deleteFolder(Request $request, Response $response) + { + $service = new FoldersService($this->container); + $service->deleteFolder( + $request->getAttribute('id'), + $request->getQueryParams() + ); + + return $this->responseWithData($request, $response, []); + } +} diff --git a/src/endpoints/Pages.php b/src/endpoints/Modules.php similarity index 81% rename from src/endpoints/Pages.php rename to src/endpoints/Modules.php index 8c11da8e78..26dbb0b964 100644 --- a/src/endpoints/Pages.php +++ b/src/endpoints/Modules.php @@ -6,9 +6,9 @@ use Directus\Application\Http\Request; use Directus\Application\Http\Response; use Directus\Application\Route; -use Directus\Services\PagesService; +use Directus\Services\ModulesService; -class Pages extends Route +class Modules extends Route { /** * @param Application $app @@ -20,7 +20,7 @@ public function __invoke(Application $app) public function all(Request $request, Response $response) { - $service = new PagesService($this->container); + $service = new ModulesService($this->container); $responseData = $service->findAll(); return $this->responseWithData($request, $response, $responseData); diff --git a/src/endpoints/ProjectHome.php b/src/endpoints/ProjectHome.php index e28ca79e32..2b013bb653 100644 --- a/src/endpoints/ProjectHome.php +++ b/src/endpoints/ProjectHome.php @@ -25,7 +25,7 @@ public function __invoke(Request $request, Response $response) ] ]; } - + return $this->responseWithData($request, $response, $responseData); } } diff --git a/src/endpoints/Server.php b/src/endpoints/Server.php index 564880066b..8a2f0753bc 100644 --- a/src/endpoints/Server.php +++ b/src/endpoints/Server.php @@ -21,9 +21,7 @@ public function __invoke(Application $app) \Directus\create_ping_route($app); $app->get('/projects', [$this, 'projects']); $app->post('/projects', \Directus\Api\Routes\ProjectsCreate::class); - $app->delete('/projects/{name}', \Directus\Api\Routes\ProjectsDelete::class) - ->add(new TableGatewayMiddleware($this->container)); - + $app->delete('/projects/{name}', \Directus\Api\Routes\ProjectsDelete::class); $app->get('/info', [$this, 'getInfo']); } @@ -34,16 +32,27 @@ public function __invoke(Application $app) */ public function projects(Request $request, Response $response) { - $scannedDirectory = \Directus\scan_config_folder(); + // When using Directus in Docker (or any other service that relies on environment variables), always use `_` for + // the project key + if (getenv("DIRECTUS_USE_ENV") === "1") { + $projectNames[] = "_"; + } else { + $basePath = \Directus\get_app_base_path(); + $scannedDirectory = \Directus\scan_folder($basePath.'/config'); + + $configFiles = $scannedDirectory; + + if (empty($configFiles)) { + throw new NotInstalledException('This Directus instance has not been configured. Install via the Directus App (eg: /admin) or read more about configuration at: https://docs.directus.io/getting-started/installation.html#configure'); + } - $projectNames = []; - if(empty($scannedDirectory)){ - throw new NotInstalledException('This Directus instance has not been configured. Install via the Directus App (eg: /admin) or read more about configuration at: https://docs.directus.io/getting-started/installation.html#configure'); - }else{ - foreach($scannedDirectory as $fileName){ - if(!StringUtils::startsWith($fileName, 'private.')){ - $fileObject = explode(".",$fileName); - $projectNames[] = $fileObject[0]; + // We're re-filtering the list of projects again before returning them. This time we'll fetch out the private + // config files. We want to filter out the disabled ones (`_`) so we can correctly return the "No projects installed" + // warning above. + $projectNames = []; + foreach($configFiles as $fileName){ + if (!StringUtils::startsWith($fileName, 'private.')) { + $projectNames[] = explode('.', $fileName)[0]; } } } @@ -81,7 +90,6 @@ public function getInfo(Request $request, Response $response) 'curl' => extension_loaded("curl"), 'gd' => extension_loaded("gd"), 'fileinfo' => extension_loaded("fileinfo"), - 'libapache2_mod_php' => extension_loaded("libapache2-mod-php"), 'mbstring' => extension_loaded("mbstring"), 'json' => extension_loaded("json"), ], diff --git a/src/endpoints/Settings.php b/src/endpoints/Settings.php index 8447753010..0f0f116ec4 100644 --- a/src/endpoints/Settings.php +++ b/src/endpoints/Settings.php @@ -10,6 +10,7 @@ use Directus\Services\FilesServices; use Directus\Util\ArrayUtils; use function Directus\regex_numeric_ids; +use function Directus\get_directus_setting; class Settings extends Route { @@ -35,19 +36,19 @@ public function __invoke(Application $app) */ public function create(Request $request, Response $response) { + $service = new SettingsService($this->container); $this->validateRequestPayload($request); $payload = $request->getParsedBody(); if (isset($payload[0]) && is_array($payload[0])) { return $this->batch($request, $response); } - $service = new SettingsService($this->container); $fieldData = $service->findAllFields( $request->getQueryParams() ); $inputData = $this->getInterfaceBasedInput($request, $payload['key'], $fieldData); - + $responseData = $service->create( $inputData, $request->getQueryParams() @@ -231,6 +232,7 @@ public function update(Request $request, Response $response) } $inputData = $request->getParsedBody(); + $service = new SettingsService($this->container); /** @@ -241,7 +243,6 @@ public function update(Request $request, Response $response) $request->getAttribute('id'), $request->getQueryParams() ); - /** * Get the interface based input * diff --git a/src/helpers/all.php b/src/helpers/all.php index b52639b9c4..c797e441fa 100644 --- a/src/helpers/all.php +++ b/src/helpers/all.php @@ -154,6 +154,21 @@ function get_url($path = '') } } +if (!function_exists('get_base_path')) { + /** + * Get Directus' base path + * + * If Directus is running in a folder, this will return the names of the folder + * that Directus is running in, f.e. /directus/public/ + * + * @return string + */ + function get_base_path() + { + return create_uri_from_global()->getBasePath() . '/'; + } +} + if (!function_exists('create_request_from_global')) { /** * Create a Request object from global variables @@ -473,7 +488,7 @@ function get_reserved_endpoint_names() return [ 'server', 'interfaces', - 'pages', + 'modules', 'layouts', 'types', 'projects' @@ -609,7 +624,7 @@ function register_extensions_hooks(Application $app) register_hooks_list( $app, - get_custom_hooks('public/extensions/core/pages', true) + get_custom_hooks('public/extensions/core/modules', true) ); register_hooks_list( @@ -755,15 +770,7 @@ function register_filter_hook(\Directus\Hook\Emitter $emitter, $name, $listener, */ function get_default_timezone(Application $app = null) { - if ($app == null) { - $app = Application::getInstance(); - } - - if (!$app || !($timezone = $app->getConfig()->get('app.timezone'))) { - $timezone = date_default_timezone_get(); - } - - return $timezone; + return date_default_timezone_get(); } } diff --git a/src/helpers/app.php b/src/helpers/app.php index 78ba2cf056..ffa943ce36 100644 --- a/src/helpers/app.php +++ b/src/helpers/app.php @@ -77,7 +77,7 @@ function get_project_config($name, $basePath = null) } $configFilePath = InstallerUtils::createConfigPath($basePath, $name); - + if (isset($configs[$configFilePath])) { return $configs[$configFilePath]; } @@ -116,22 +116,25 @@ function get_app_base_path() } } -if (!function_exists('scan_config_folder')) { +if (!function_exists('scan_folder')) { /** - * Scan config folder and return the php files (Project Configurations) + * Scan folder and return the php files (Project Configurations) * * @return string */ - function scan_config_folder() + function scan_folder($folder) { $projectNames = []; - $ignoreableFiles = ['api_sample.php','.DS_Store','..', '.']; - $scannedDirectory = array_values(array_diff(scandir(get_app_base_path().'/config'), $ignoreableFiles)); - if(!empty($scannedDirectory)){ - foreach($scannedDirectory as $fileName){ - $fileObject = explode(".",$fileName); - if(end($fileObject) == "php" ){ - $projectNames[] = implode(".",$fileObject); + $ignoreableFiles = ['.DS_Store','..', '.']; + $scannedDirectory = array_values(array_diff(scandir($folder), $ignoreableFiles)); + if (!empty($scannedDirectory)) { + foreach ($scannedDirectory as $fileName) { + $fileObject = explode(".", $fileName); + + if (end($fileObject) == "php") { + if (strlen($fileObject[0]) == 1 || StringUtils::startsWith($fileName, '_') === false) { + $projectNames[] = implode(".", $fileObject); + } } } } diff --git a/src/helpers/file.php b/src/helpers/file.php index e102056f87..016e8d746a 100644 --- a/src/helpers/file.php +++ b/src/helpers/file.php @@ -129,21 +129,33 @@ function append_storage_information(array $rows) foreach ($rows as &$row) { $data = []; + $ext = pathinfo($row['filename_disk'], PATHINFO_EXTENSION); if ($proxyDownloads) { - $data['url'] = get_proxy_path($row['filename']); + $data['url'] = get_proxy_path($row['filename_disk']); $data['full_url'] = get_url($data['url']); } else { - $data['url'] = $data['full_url'] = $fileRootUrl . '/' . $row['filename']; - - // Add Full url - if ($isLocalStorageAdapter && !$hasFileRootUrlHost) { - $data['full_url'] = get_url($data['url']); + if(isset($row['private_hash'])){ + $data['url'] = $data['full_url'] = $fileRootUrl . '/' . $row['filename_disk']; + // Add Full url + if (!$hasFileRootUrlHost) { + $data['full_url'] = get_url($data['url']); + } } + } - // Add Thumbnails - $data['thumbnails'] = get_thumbnails($row); + // Add thumbnails if the asset is an image + $search = 'image'; + if ( + array_key_exists('type', $row) && + substr($row['type'], 0, strlen($search)) === $search && + $row['type'] !== 'image/svg+xml' // SVGs aren't manipulatable bitmaps + ) { + $data['thumbnails'] = get_thumbnails($row); + } else { + $data['thumbnails'] = null; + } // Add embed content /** @var \Directus\Embed\EmbedManager $embedManager */ @@ -192,45 +204,36 @@ function add_default_thumbnail_dimensions(array &$list) function get_thumbnails(array $row) { - $filename = $row['filename']; + $filename = $row['filename_disk']; $type = array_get($row, 'type'); $thumbnailFilenameParts = explode('.', $filename); $thumbnailExtension = array_pop($thumbnailFilenameParts); - $thumbnailDimensions = array_filter( - explode(',', get_directus_setting('thumbnail_dimensions')) - ); + + $systemThumb = json_decode(get_directus_setting('asset_whitelist_system'),true); + $whitelistThumb = json_decode(get_directus_setting('asset_whitelist'),true); + $thumbnailWhitelist = !empty($whitelistThumb) ? array_merge($systemThumb, $whitelistThumb) : $systemThumb; $fileExtension = MimeTypeUtils::getFromMimeType($type); + if (!in_array($fileExtension, Thumbnail::getFormatsSupported()) && strpos($type, 'embed/') !== 0) { return null; } - // Add default size - add_default_thumbnail_dimensions($thumbnailDimensions); - $thumbnails = []; - foreach (array_unique($thumbnailDimensions) as $dimension) { + foreach ($thumbnailWhitelist as $thumbnail) { if (Thumbnail::isNonImageFormatSupported($thumbnailExtension)) { $thumbnailExtension = Thumbnail::defaultFormat(); } - if (!is_string($dimension)) { - continue; - } - - $size = explode('x', $dimension); - - if (count($size) == 2) { - $thumbnailUrl = get_thumbnail_url($filename, $size[0], $size[1]); - $thumbnailRelativeUrl = get_thumbnail_path($filename, $size[0], $size[1]); - $thumbnails[] = [ - 'url' => $thumbnailUrl, - 'relative_url' => $thumbnailRelativeUrl, - 'dimension' => $dimension, - 'width' => (int) $size[0], - 'height' => (int) $size[1], - ]; - } + $thumbnailUrl = get_thumbnail_url($row['private_hash'],$thumbnail); + $thumbnailRelativeUrl = get_thumbnail_path($row['private_hash'], $thumbnail, true); + $thumbnails[] = [ + 'url' => $thumbnailUrl, + 'relative_url' => $thumbnailRelativeUrl, + 'dimension' => $thumbnail['width'].'x'.$thumbnail['height'], + 'width' => (int) $thumbnail['width'], + 'height' => (int) $thumbnail['height'], + ]; } return $thumbnails; @@ -241,17 +244,13 @@ function get_thumbnails(array $row) /** * Returns a url for the given file pointing to the thumbnailer * - * @param string $name - * @param int $width - * @param int $height - * @param string $mode - * @param string $quality + * @param array $thumbnail * * @return string */ - function get_thumbnail_url($name, $width, $height, $mode = 'crop', $quality = 'good') + function get_thumbnail_url($name,$thumbnail) { - return get_url(get_thumbnail_path($name, $width, $height, $mode, $quality)); + return get_url(get_thumbnail_path($name, $thumbnail)); } } @@ -259,28 +258,25 @@ function get_thumbnail_url($name, $width, $height, $mode = 'crop', $quality = 'g /** * Returns a relative url for the given file pointing to the thumbnailer * - * @param string $name - * @param int $width - * @param int $height - * @param string $mode - * @param string $quality + * @param array $thumbnail * * @return string */ - function get_thumbnail_path($name, $width, $height, $mode = 'crop', $quality = 'good') + function get_thumbnail_path($name, $thumbnail, $addBasePath = false) { + $path = ''; + $projectName = get_api_project_from_request(); + $paramsString = '?key=' . $thumbnail['key']; - // env/width/height/mode/quality/name - return sprintf( - '/thumbnail/%s/%d/%d/%s/%s/%s', - $projectName, - $width, - $height, - $mode, - $quality, - $name - ); + $path = $projectName . '/assets/' . $name . $paramsString; + + if ($addBasePath === true) { + $basePath = get_base_path(); + $path = $basePath . $path; + } + + return $path; } } @@ -452,3 +448,27 @@ function validate_file_size($value, $options) } } } + +if (!function_exists('get_file_root_url')) { + /** + * Get File Root URL + * + * + * @return string + */ + function get_file_root_url() + { + $container = Application::getInstance()->getContainer(); + $config = $container->get('config'); + return $config->get('storage.root_url'); + } +} + +if (!function_exists('get_random_string')) { + function get_random_string($limit = 16) + { + return substr(base_convert(sha1(uniqid(mt_rand())), 16, 36), 0,$limit); + } +} + + diff --git a/src/helpers/settings.php b/src/helpers/settings.php index ec77c9618c..45e5e5e7c5 100644 --- a/src/helpers/settings.php +++ b/src/helpers/settings.php @@ -89,11 +89,8 @@ function get_directus_files_settings() function get_directus_thumbnail_settings() { return get_directus_settings_by_keys([ - 'thumbnail_dimensions', - 'thumbnail_not_found_location', - 'thumbnail_quality_tags', - 'thumbnail_actions', - 'thumbnail_cache_ttl', + 'asset_whitelist', + 'asset_whitelist_system' ]); } } @@ -156,22 +153,55 @@ function get_trusted_proxies() */ function get_project_info() { - $settings = get_directus_settings_by_keys(['project_name', 'project_logo','project_color','project_foreground','project_background','default_locale', 'telemetry']); - $settings['project_logo'] = array_get($settings, 'project_logo') ? get_project_logo_data(array_get($settings, 'project_logo')) : null; - $settings['project_foreground'] = array_get($settings, 'project_foreground') ? get_project_logo_data(array_get($settings, 'project_foreground')) : null; - $settings['project_background'] = array_get($settings, 'project_background') ? get_project_logo_data(array_get($settings, 'project_background')) : null; - $settings['telemetry'] = array_get($settings, 'telemetry') && $settings['telemetry'] ? true : false; + $settings = get_directus_settings_by_keys([ + 'project_name', + 'project_logo', + 'project_color', + 'project_foreground', + 'project_background', + 'project_public_note', + 'default_locale', + 'telemetry' + ]); + + // TODO: + // Typecasting of these values should be done the same way as in the `/settings` endpoint. + + $settings['project_logo'] = array_get($settings, 'project_logo') + ? get_file_data(array_get($settings, 'project_logo')) + : null; + + $settings['project_foreground'] = + array_get($settings, 'project_foreground') + ? get_file_data(array_get($settings, 'project_foreground')) + : null; + + $settings['project_background'] = + array_get($settings, 'project_background') + ? get_file_data(array_get($settings, 'project_background')) + : null; + + $settings['project_public_note'] = + array_get($settings, 'project_public_note') + ? array_get($settings, 'project_public_note') + : null; + + $settings['telemetry'] = + array_get($settings, 'telemetry') && $settings['telemetry'] + ? true + : false; + return $settings; } } -if (!function_exists('get_project_logo_data')) { +if (!function_exists('get_file_data')) { /** * @param int $id * * @return array */ - function get_project_logo_data($id) + function get_file_data($id) { /** @var RelationalTableGateway $table */ $table = TableGatewayFactory::create('directus_files', ['acl' => false]); diff --git a/src/schema.sql b/src/schema.sql index dfe72e0a17..cd613a8705 100644 --- a/src/schema.sql +++ b/src/schema.sql @@ -1,61 +1,40 @@ -/* -SQLyog Ultimate v12.09 (64 bit) -MySQL - 5.7.21-log : Database - directus-live -********************************************************************* -*/ - - +-- ------------------------------------------------------------- +-- TablePlus 2.11.2(278) +-- +-- https://tableplus.com/ +-- +-- Database: db +-- Generation Time: 2019-12-05 09:39:38.1110 +-- ------------------------------------------------------------- + + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; /*!40101 SET NAMES utf8 */; - -/*!40101 SET SQL_MODE=''*/; - +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; -/*Table structure for table `directus_activity` */ - -DROP TABLE IF EXISTS `directus_activity`; CREATE TABLE `directus_activity` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `action` varchar(45) NOT NULL, - `action_by` int(11) unsigned NOT NULL DEFAULT '0', + `action_by` int(11) unsigned NOT NULL DEFAULT 0, `action_on` datetime NOT NULL, `ip` varchar(50) NOT NULL, `user_agent` varchar(255) NOT NULL, `collection` varchar(64) NOT NULL, `item` varchar(255) NOT NULL, `edited_on` datetime DEFAULT NULL, - `comment` text CHARACTER SET utf8mb4, + `comment` text CHARACTER SET utf8mb4 DEFAULT NULL, `comment_deleted_on` datetime DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; - -/*Data for the table `directus_activity` */ - -insert into `directus_activity`(`id`,`action`,`action_by`,`action_on`,`ip`,`user_agent`,`collection`,`item`,`edited_on`,`comment`,`comment_deleted_on`) values (1,'authenticate',1,'2019-06-22 07:28:21','::1','Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36','directus_users','1',NULL,NULL,NULL); - -/*Table structure for table `directus_activity_seen` */ - -DROP TABLE IF EXISTS `directus_activity_seen`; - -CREATE TABLE `directus_activity_seen` ( - `id` int(11) unsigned NOT NULL AUTO_INCREMENT, - `activity` int(11) unsigned NOT NULL, - `user` int(11) unsigned NOT NULL DEFAULT '0', - `seen_on` datetime DEFAULT NULL, - `archived` tinyint(1) unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*Data for the table `directus_activity_seen` */ - -/*Table structure for table `directus_collection_presets` */ - -DROP TABLE IF EXISTS `directus_collection_presets`; - CREATE TABLE `directus_collection_presets` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL, @@ -63,99 +42,73 @@ CREATE TABLE `directus_collection_presets` ( `role` int(11) unsigned DEFAULT NULL, `collection` varchar(64) NOT NULL, `search_query` varchar(100) DEFAULT NULL, - `filters` text, + `filters` text DEFAULT NULL, `view_type` varchar(100) NOT NULL DEFAULT 'tabular', - `view_query` text, - `view_options` text, - `translation` text, + `view_query` text DEFAULT NULL, + `view_options` text DEFAULT NULL, + `translation` text DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `idx_user_collection_title` (`user`,`collection`,`title`) -) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; - -/*Data for the table `directus_collection_presets` */ - -insert into `directus_collection_presets`(`id`,`title`,`user`,`role`,`collection`,`search_query`,`filters`,`view_type`,`view_query`,`view_options`,`translation`) values (1,NULL,NULL,NULL,'directus_activity',NULL,NULL,'timeline','{\"timeline\":{\"sort\":\"-action_on\"}}','{\"timeline\":{\"date\":\"action_on\",\"title\":\"{{ action_by.first_name }} {{ action_by.last_name }} ({{ action }})\",\"content\":\"action_by\",\"color\":\"action\"}}',NULL),(2,NULL,NULL,NULL,'directus_files',NULL,NULL,'cards',NULL,'{\"cards\":{\"title\":\"title\",\"subtitle\":\"type\",\"content\":\"description\",\"src\":\"data\"}}',NULL),(3,NULL,NULL,NULL,'directus_users',NULL,NULL,'cards',NULL,'{\"cards\":{\"title\":\"first_name\",\"subtitle\":\"last_name\",\"content\":\"title\",\"src\":\"avatar\",\"icon\":\"person\"}}',NULL); - -/*Table structure for table `directus_collections` */ - -DROP TABLE IF EXISTS `directus_collections`; +) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; CREATE TABLE `directus_collections` ( `collection` varchar(64) NOT NULL, - `managed` tinyint(1) unsigned NOT NULL DEFAULT '1', - `hidden` tinyint(1) unsigned NOT NULL DEFAULT '0', - `single` tinyint(1) unsigned NOT NULL DEFAULT '0', + `managed` tinyint(1) unsigned NOT NULL DEFAULT 1, + `hidden` tinyint(1) unsigned NOT NULL DEFAULT 0, + `single` tinyint(1) unsigned NOT NULL DEFAULT 0, `icon` varchar(30) DEFAULT NULL, `note` varchar(255) DEFAULT NULL, - `translation` text, + `translation` text DEFAULT NULL, PRIMARY KEY (`collection`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*Data for the table `directus_collections` */ - -/*Table structure for table `directus_fields` */ - -DROP TABLE IF EXISTS `directus_fields`; - CREATE TABLE `directus_fields` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `collection` varchar(64) NOT NULL, `field` varchar(64) NOT NULL, `type` varchar(64) NOT NULL, `interface` varchar(64) DEFAULT NULL, - `options` text, - `locked` tinyint(1) unsigned NOT NULL DEFAULT '0', + `options` text DEFAULT NULL, + `locked` tinyint(1) unsigned NOT NULL DEFAULT 0, `validation` varchar(255) DEFAULT NULL, - `required` tinyint(1) unsigned NOT NULL DEFAULT '0', - `readonly` tinyint(1) unsigned NOT NULL DEFAULT '0', - `hidden_detail` tinyint(1) unsigned NOT NULL DEFAULT '0', - `hidden_browse` tinyint(1) unsigned NOT NULL DEFAULT '0', + `required` tinyint(1) unsigned NOT NULL DEFAULT 0, + `readonly` tinyint(1) unsigned NOT NULL DEFAULT 0, + `hidden_detail` tinyint(1) unsigned NOT NULL DEFAULT 0, + `hidden_browse` tinyint(1) unsigned NOT NULL DEFAULT 0, `sort` int(11) unsigned DEFAULT NULL, - `width` varchar(30) DEFAULT NULL, + `width` varchar(50) DEFAULT 'full', `group` int(11) unsigned DEFAULT NULL, `note` varchar(1024) DEFAULT NULL, - `translation` text, + `translation` text DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `idx_collection_field` (`collection`,`field`) -) ENGINE=InnoDB AUTO_INCREMENT=145 DEFAULT CHARSET=utf8; - -/*Data for the table `directus_fields` */ - -insert into `directus_fields`(`id`,`collection`,`field`,`type`,`interface`,`options`,`locked`,`validation`,`required`,`readonly`,`hidden_detail`,`hidden_browse`,`sort`,`width`,`group`,`note`,`translation`) values (1,'directus_activity','id','integer','primary-key',NULL,1,NULL,1,1,1,0,NULL,'full',NULL,NULL,NULL),(2,'directus_activity','action','string','activity-icon',NULL,1,NULL,0,1,0,0,1,'full',NULL,NULL,NULL),(3,'directus_activity','collection','string','collections',NULL,1,NULL,0,1,0,0,2,'half',NULL,NULL,NULL),(4,'directus_activity','item','string','text-input',NULL,1,NULL,0,1,0,0,3,'half',NULL,NULL,NULL),(5,'directus_activity','action_by','integer','user',NULL,1,NULL,0,1,0,0,4,'half',NULL,NULL,NULL),(6,'directus_activity','action_on','datetime','datetime','{\"showRelative\":true}',1,NULL,0,1,0,0,5,'half',NULL,NULL,NULL),(7,'directus_activity','edited_on','datetime','datetime','{\"showRelative\":true}',1,NULL,0,1,0,0,6,'half',NULL,NULL,NULL),(8,'directus_activity','comment_deleted_on','datetime','datetime','{\"showRelative\":true}',1,NULL,0,1,0,0,7,'half',NULL,NULL,NULL),(9,'directus_activity','ip','string','text-input',NULL,1,NULL,0,1,0,0,8,'half',NULL,NULL,NULL),(10,'directus_activity','user_agent','string','text-input',NULL,1,NULL,0,1,0,0,9,'half',NULL,NULL,NULL),(11,'directus_activity','comment','string','textarea',NULL,1,NULL,0,1,0,0,10,'full',NULL,NULL,NULL),(12,'directus_collection_presets','id','integer','primary-key',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(13,'directus_collection_presets','title','string','text-input',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(14,'directus_collection_presets','user','integer','user',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(15,'directus_collection_presets','role','m2o','many-to-one',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(16,'directus_collection_presets','collection','m2o','many-to-one',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(17,'directus_collection_presets','search_query','string','text-input',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(18,'directus_collection_presets','filters','json','json',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(19,'directus_collection_presets','view_options','json','json',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(20,'directus_collection_presets','view_type','string','text-input',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(21,'directus_collection_presets','view_query','json','json',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(22,'directus_collections','fields','o2m','one-to-many',NULL,1,NULL,0,0,1,1,1,'full',NULL,NULL,NULL),(23,'directus_collections','collection','string','primary-key',NULL,1,NULL,1,1,0,0,2,'half',NULL,NULL,NULL),(24,'directus_collections','note','string','text-input',NULL,1,NULL,0,0,0,0,3,'half',NULL,'An internal description.',NULL),(25,'directus_collections','managed','boolean','toggle',NULL,1,NULL,0,0,1,0,4,'half',NULL,'[Learn More](https://docs.directus.io/guides/collections.html#managing-collections).',NULL),(26,'directus_collections','hidden','boolean','toggle',NULL,1,NULL,0,0,0,0,5,'half',NULL,'[Learn More](https://docs.directus.io/guides/collections.html#hidden).',NULL),(27,'directus_collections','single','boolean','toggle',NULL,1,NULL,0,0,0,0,6,'half',NULL,'[Learn More](https://docs.directus.io/guides/collections.html#single).',NULL),(28,'directus_collections','translation','json','json',NULL,1,NULL,0,0,1,0,7,'full',NULL,NULL,NULL),(29,'directus_collections','icon','string','icon',NULL,1,NULL,0,0,0,0,8,'full',NULL,'The icon shown in the App\'s navigation sidebar.',NULL),(30,'directus_fields','id','integer','primary-key',NULL,1,NULL,1,0,1,0,NULL,'full',NULL,NULL,NULL),(31,'directus_fields','collection','m2o','many-to-one',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(32,'directus_fields','field','string','text-input',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(33,'directus_fields','type','string','primary-key',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(34,'directus_fields','interface','string','primary-key',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(35,'directus_fields','options','json','json',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(36,'directus_fields','locked','boolean','toggle',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(37,'directus_fields','translation','json','json',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(38,'directus_fields','readonly','boolean','toggle',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(39,'directus_fields','validation','string','text-input',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(40,'directus_fields','required','boolean','toggle',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(41,'directus_fields','sort','sort','sort',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(42,'directus_fields','note','string','text-input',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(43,'directus_fields','hidden_detail','boolean','toggle',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(44,'directus_fields','hidden_browse','boolean','toggle',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(45,'directus_fields','width','integer','numeric',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(46,'directus_fields','group','m2o','many-to-one',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(47,'directus_files','data','alias','file',NULL,1,NULL,0,0,1,0,0,'full',NULL,NULL,NULL),(48,'directus_files','id','integer','primary-key',NULL,1,NULL,1,0,1,0,1,'full',NULL,NULL,NULL),(49,'directus_files','preview','alias','file-preview',NULL,1,NULL,0,0,0,0,2,'full',NULL,NULL,NULL),(50,'directus_files','title','string','text-input','{\"placeholder\":\"Enter a descriptive title...\",\"iconRight\":\"title\"}',1,NULL,0,0,0,0,3,'half',NULL,NULL,NULL),(51,'directus_files','filename','string','text-input','{\"placeholder\":\"Enter a unique file name...\",\"iconRight\":\"insert_drive_file\"}',1,NULL,1,1,0,0,4,'half',NULL,NULL,NULL),(52,'directus_files','tags','array','tags',NULL,0,NULL,0,0,0,0,5,'half',NULL,NULL,NULL),(53,'directus_files','location','string','text-input','{\"placeholder\":\"Enter a location...\",\"iconRight\":\"place\"}',0,NULL,0,0,0,0,6,'half',NULL,NULL,NULL),(54,'directus_files','description','string','wysiwyg','{\"placeholder\":\"Enter a caption or description...\"}',0,NULL,0,0,0,0,7,'full',NULL,NULL,NULL),(55,'directus_files','width','integer','numeric','{\"iconRight\":\"straighten\"}',1,NULL,0,1,0,0,10,'half',NULL,NULL,NULL),(56,'directus_files','height','integer','numeric','{\"iconRight\":\"straighten\"}',1,NULL,0,1,0,0,11,'half',NULL,NULL,NULL),(57,'directus_files','duration','integer','numeric','{\"iconRight\":\"timer\"}',1,NULL,0,1,0,0,12,'half',NULL,NULL,NULL),(58,'directus_files','filesize','integer','file-size','{\"iconRight\":\"storage\"}',1,NULL,0,1,0,0,13,'half',NULL,NULL,NULL),(59,'directus_files','uploaded_on','datetime','datetime','{\"iconRight\":\"today\"}',1,NULL,1,1,0,0,8,'half',NULL,NULL,NULL),(60,'directus_files','uploaded_by','integer','user',NULL,1,NULL,1,1,0,0,9,'half',NULL,NULL,NULL),(61,'directus_files','metadata','json','json',NULL,1,NULL,0,0,0,0,14,'full',NULL,NULL,NULL),(62,'directus_files','type','string','text-input',NULL,1,NULL,0,1,1,0,NULL,'full',NULL,NULL,NULL),(63,'directus_files','charset','string','text-input',NULL,1,NULL,0,1,1,1,NULL,'full',NULL,NULL,NULL),(64,'directus_files','embed','string','text-input',NULL,1,NULL,0,1,1,0,NULL,'full',NULL,NULL,NULL),(65,'directus_files','folder','m2o','many-to-one',NULL,1,NULL,0,0,1,0,NULL,'full',NULL,NULL,NULL),(66,'directus_files','storage','string','text-input',NULL,1,NULL,0,0,1,1,NULL,'full',NULL,NULL,NULL),(67,'directus_files','checksum','string','text-input',NULL,1,NULL,0,1,1,1,NULL,'full',NULL,NULL,NULL),(68,'directus_folders','id','integer','primary-key',NULL,1,NULL,1,0,1,0,NULL,'full',NULL,NULL,NULL),(69,'directus_folders','name','string','text-input',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(70,'directus_folders','parent_folder','m2o','many-to-one',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(71,'directus_permissions','id','integer','primary-key',NULL,1,NULL,1,0,1,0,NULL,'full',NULL,NULL,NULL),(72,'directus_permissions','collection','m2o','many-to-one',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(73,'directus_permissions','role','m2o','many-to-one',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(74,'directus_permissions','status','string','text-input',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(75,'directus_permissions','create','string','text-input',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(76,'directus_permissions','read','string','text-input',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(77,'directus_permissions','update','string','text-input',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(78,'directus_permissions','delete','string','primary-key',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(79,'directus_permissions','comment','string','text-input',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(80,'directus_permissions','explain','string','text-input',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(81,'directus_permissions','status_blacklist','array','tags',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(82,'directus_permissions','read_field_blacklist','array','tags',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(83,'directus_permissions','write_field_blacklist','array','tags',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(84,'directus_relations','id','integer','primary-key',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(85,'directus_relations','collection_many','string','collections',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(86,'directus_relations','field_many','string','text-input',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(87,'directus_relations','collection_one','string','collections',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(88,'directus_relations','field_one','string','text-input',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(89,'directus_relations','junction_field','string','text-input',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(90,'directus_revisions','id','integer','primary-key',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(91,'directus_revisions','activity','m2o','many-to-one',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(92,'directus_revisions','collection','m2o','many-to-one',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(93,'directus_revisions','item','string','text-input',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(94,'directus_revisions','data','json','json',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(95,'directus_revisions','delta','json','json',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(96,'directus_revisions','parent_item','string','text-input',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(97,'directus_revisions','parent_collection','string','collections',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(98,'directus_revisions','parent_changed','boolean','toggle',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(99,'directus_roles','id','integer','primary-key',NULL,1,NULL,1,0,1,0,NULL,'full',NULL,NULL,NULL),(100,'directus_roles','external_id','string','text-input',NULL,1,NULL,0,1,1,1,NULL,'full',NULL,NULL,NULL),(101,'directus_roles','name','string','text-input',NULL,1,NULL,1,0,0,0,1,'half',NULL,NULL,NULL),(102,'directus_roles','description','string','text-input',NULL,1,NULL,0,0,0,0,2,'half',NULL,NULL,NULL),(103,'directus_roles','ip_whitelist','string','textarea',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(104,'directus_roles','nav_blacklist','string','textarea',NULL,1,NULL,0,0,1,1,NULL,'full',NULL,NULL,NULL),(105,'directus_roles','users','o2m','many-to-many','{\"fields\":\"first_name,last_name\"}',1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(106,'directus_roles','nav_override','json','json','[\r\n {\r\n \"title\": \"$t:collections\",\r\n \"include\": \"collections\"\r\n },\r\n {\r\n \"title\": \"$t:bookmarks\",\r\n \"include\": \"bookmarks\"\r\n },\r\n {\r\n \"title\": \"$t:extensions\",\r\n \"include\": \"extensions\"\r\n },\r\n {\r\n \"title\": \"Custom Links\",\r\n \"links\": [\r\n {\r\n \"name\": \"RANGER Studio\",\r\n \"path\": \"https://rangerstudio.com\",\r\n \"icon\": \"star\"\r\n },\r\n {\r\n \"name\": \"Movies\",\r\n \"path\": \"/collections/movies\"\r\n }\r\n ]\r\n }\r\n ]',1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(107,'directus_settings','project_name','string','text-input',NULL,1,NULL,1,0,0,0,1,'half-space',NULL,NULL,NULL),(108,'directus_settings','color','string','color-palette',NULL,1,NULL,0,0,0,0,2,'half',NULL,'How many minutes before an idle user is signed out.',NULL),(109,'directus_settings','logo','file','file',NULL,1,NULL,0,0,0,0,3,'half',NULL,'Your brand\'s logo.',NULL),(110,'directus_settings','app_url','string','text-input',NULL,1,NULL,1,0,0,0,4,'half-space',NULL,'The URL where your app is hosted. The API will use this to direct your users to the correct login page.',NULL),(111,'directus_settings','default_limit','integer','numeric',NULL,1,NULL,1,0,0,0,5,'half',NULL,'How many minutes before an idle user is signed out.',NULL),(112,'directus_settings','sort_null_last','boolean','toggle',NULL,1,NULL,0,0,0,0,6,'half-space',NULL,'Put items with `null` for the value last when sorting.',NULL),(113,'directus_settings','auto_sign_out','integer','numeric',NULL,1,NULL,1,0,0,0,7,'half',NULL,'How many minutes before an idle user is signed out.',NULL),(114,'directus_settings','youtube_api','string','text-input',NULL,1,NULL,0,0,0,0,8,'half',NULL,'When provided, this allows more information to be collected for YouTube embeds.',NULL),(115,'directus_settings','thumbnail_dimensions','array','tags',NULL,1,NULL,0,0,0,0,9,'full',NULL,'Allowed dimensions for thumbnails.',NULL),(116,'directus_settings','thumbnail_quality_tags','json','json',NULL,1,NULL,0,0,0,0,10,'half',NULL,'Allowed quality for thumbnails.',NULL),(117,'directus_settings','thumbnail_actions','json','json',NULL,1,NULL,0,0,0,0,11,'half',NULL,'Defines how the thumbnail will be generated based on the requested dimensions.',NULL),(118,'directus_settings','thumbnail_cache_ttl','integer','numeric',NULL,1,NULL,1,0,0,0,12,'half',NULL,'`max-age` HTTP header of the thumbnail.',NULL),(119,'directus_settings','thumbnail_not_found_location','string','text-input',NULL,1,NULL,0,0,0,0,13,'half',NULL,'This image will be used when trying to generate a thumbnail with invalid options or an error happens on the server when creating the image.',NULL),(120,'directus_users','id','integer','primary-key',NULL,1,NULL,1,0,1,0,1,'full',NULL,NULL,NULL),(121,'directus_users','status','status','status','{\"status_mapping\":{\"draft\":{\"name\":\"Draft\",\"text_color\":\"white\",\"background_color\":\"light-gray\",\"listing_subdued\":false,\"listing_badge\":true,\"soft_delete\":false},\"invited\":{\"name\":\"Invited\",\"text_color\":\"white\",\"background_color\":\"light-gray\",\"listing_subdued\":false,\"listing_badge\":true,\"soft_delete\":false},\"active\":{\"name\":\"Active\",\"text_color\":\"white\",\"background_color\":\"success\",\"listing_subdued\":false,\"listing_badge\":false,\"soft_delete\":false},\"suspended\":{\"name\":\"Suspended\",\"text_color\":\"white\",\"background_color\":\"light-gray\",\"listing_subdued\":false,\"listing_badge\":true,\"soft_delete\":false},\"deleted\":{\"name\":\"Deleted\",\"text_color\":\"white\",\"background_color\":\"danger\",\"listing_subdued\":false,\"listing_badge\":true,\"soft_delete\":true}}}',1,NULL,1,0,0,0,2,'full',NULL,NULL,NULL),(122,'directus_users','first_name','string','text-input','{\"placeholder\":\"Enter your give name...\"}',1,NULL,1,0,0,0,3,'half',NULL,NULL,NULL),(123,'directus_users','last_name','string','text-input','{\"placeholder\":\"Enter your surname...\"}',1,NULL,1,0,0,0,4,'half',NULL,NULL,NULL),(124,'directus_users','email','string','text-input','{\"placeholder\":\"Enter your email address...\"}',1,'$email',1,0,0,0,5,'half',NULL,NULL,NULL),(125,'directus_users','email_notifications','boolean','toggle',NULL,1,NULL,0,0,0,0,6,'half',NULL,NULL,NULL),(126,'directus_users','password','hash','password',NULL,1,NULL,1,0,0,0,7,'half',NULL,NULL,NULL),(127,'directus_users','roles','o2m','user-roles',NULL,1,NULL,1,0,0,0,8,'half',NULL,NULL,NULL),(128,'directus_users','company','string','text-input','{\"placeholder\":\"Enter your company or organization name...\"}',0,NULL,0,0,0,0,9,'half',NULL,NULL,NULL),(129,'directus_users','title','string','text-input','{\"placeholder\":\"Enter your title or role...\"}',0,NULL,0,0,0,0,10,'half',NULL,NULL,NULL),(130,'directus_users','timezone','string','dropdown','{\"choices\":{\"Pacific\\/Midway\":\"(UTC-11:00) Midway Island\",\"Pacific\\/Samoa\":\"(UTC-11:00) Samoa\",\"Pacific\\/Honolulu\":\"(UTC-10:00) Hawaii\",\"US\\/Alaska\":\"(UTC-09:00) Alaska\",\"America\\/Los_Angeles\":\"(UTC-08:00) Pacific Time (US & Canada)\",\"America\\/Tijuana\":\"(UTC-08:00) Tijuana\",\"US\\/Arizona\":\"(UTC-07:00) Arizona\",\"America\\/Chihuahua\":\"(UTC-07:00) Chihuahua\",\"America\\/Mexico\\/La_Paz\":\"(UTC-07:00) La Paz\",\"America\\/Mazatlan\":\"(UTC-07:00) Mazatlan\",\"US\\/Mountain\":\"(UTC-07:00) Mountain Time (US & Canada)\",\"America\\/Managua\":\"(UTC-06:00) Central America\",\"US\\/Central\":\"(UTC-06:00) Central Time (US & Canada)\",\"America\\/Guadalajara\":\"(UTC-06:00) Guadalajara\",\"America\\/Mexico_City\":\"(UTC-06:00) Mexico City\",\"America\\/Monterrey\":\"(UTC-06:00) Monterrey\",\"Canada\\/Saskatchewan\":\"(UTC-06:00) Saskatchewan\",\"America\\/Bogota\":\"(UTC-05:00) Bogota\",\"US\\/Eastern\":\"(UTC-05:00) Eastern Time (US & Canada)\",\"US\\/East-Indiana\":\"(UTC-05:00) Indiana (East)\",\"America\\/Lima\":\"(UTC-05:00) Lima\",\"America\\/Quito\":\"(UTC-05:00) Quito\",\"Canada\\/Atlantic\":\"(UTC-04:00) Atlantic Time (Canada)\",\"America\\/New_York\":\"(UTC-04:00) New York\",\"America\\/Caracas\":\"(UTC-04:30) Caracas\",\"America\\/La_Paz\":\"(UTC-04:00) La Paz\",\"America\\/Santiago\":\"(UTC-04:00) Santiago\",\"America\\/Santo_Domingo\":\"(UTC-04:00) Santo Domingo\",\"Canada\\/Newfoundland\":\"(UTC-03:30) Newfoundland\",\"America\\/Sao_Paulo\":\"(UTC-03:00) Brasilia\",\"America\\/Argentina\\/Buenos_Aires\":\"(UTC-03:00) Buenos Aires\",\"America\\/Argentina\\/GeorgeTown\":\"(UTC-03:00) Georgetown\",\"America\\/Godthab\":\"(UTC-03:00) Greenland\",\"America\\/Noronha\":\"(UTC-02:00) Mid-Atlantic\",\"Atlantic\\/Azores\":\"(UTC-01:00) Azores\",\"Atlantic\\/Cape_Verde\":\"(UTC-01:00) Cape Verde Is.\",\"Africa\\/Casablanca\":\"(UTC+00:00) Casablanca\",\"Europe\\/Edinburgh\":\"(UTC+00:00) Edinburgh\",\"Etc\\/Greenwich\":\"(UTC+00:00) Greenwich Mean Time : Dublin\",\"Europe\\/Lisbon\":\"(UTC+00:00) Lisbon\",\"Europe\\/London\":\"(UTC+00:00) London\",\"Africa\\/Monrovia\":\"(UTC+00:00) Monrovia\",\"UTC\":\"(UTC+00:00) UTC\",\"Europe\\/Amsterdam\":\"(UTC+01:00) Amsterdam\",\"Europe\\/Belgrade\":\"(UTC+01:00) Belgrade\",\"Europe\\/Berlin\":\"(UTC+01:00) Berlin\",\"Europe\\/Bern\":\"(UTC+01:00) Bern\",\"Europe\\/Bratislava\":\"(UTC+01:00) Bratislava\",\"Europe\\/Brussels\":\"(UTC+01:00) Brussels\",\"Europe\\/Budapest\":\"(UTC+01:00) Budapest\",\"Europe\\/Copenhagen\":\"(UTC+01:00) Copenhagen\",\"Europe\\/Ljubljana\":\"(UTC+01:00) Ljubljana\",\"Europe\\/Madrid\":\"(UTC+01:00) Madrid\",\"Europe\\/Paris\":\"(UTC+01:00) Paris\",\"Europe\\/Prague\":\"(UTC+01:00) Prague\",\"Europe\\/Rome\":\"(UTC+01:00) Rome\",\"Europe\\/Sarajevo\":\"(UTC+01:00) Sarajevo\",\"Europe\\/Skopje\":\"(UTC+01:00) Skopje\",\"Europe\\/Stockholm\":\"(UTC+01:00) Stockholm\",\"Europe\\/Vienna\":\"(UTC+01:00) Vienna\",\"Europe\\/Warsaw\":\"(UTC+01:00) Warsaw\",\"Africa\\/Lagos\":\"(UTC+01:00) West Central Africa\",\"Europe\\/Zagreb\":\"(UTC+01:00) Zagreb\",\"Europe\\/Athens\":\"(UTC+02:00) Athens\",\"Europe\\/Bucharest\":\"(UTC+02:00) Bucharest\",\"Africa\\/Cairo\":\"(UTC+02:00) Cairo\",\"Africa\\/Harare\":\"(UTC+02:00) Harare\",\"Europe\\/Helsinki\":\"(UTC+02:00) Helsinki\",\"Europe\\/Istanbul\":\"(UTC+02:00) Istanbul\",\"Asia\\/Jerusalem\":\"(UTC+02:00) Jerusalem\",\"Europe\\/Kyiv\":\"(UTC+02:00) Kyiv\",\"Africa\\/Johannesburg\":\"(UTC+02:00) Pretoria\",\"Europe\\/Riga\":\"(UTC+02:00) Riga\",\"Europe\\/Sofia\":\"(UTC+02:00) Sofia\",\"Europe\\/Tallinn\":\"(UTC+02:00) Tallinn\",\"Europe\\/Vilnius\":\"(UTC+02:00) Vilnius\",\"Asia\\/Baghdad\":\"(UTC+03:00) Baghdad\",\"Asia\\/Kuwait\":\"(UTC+03:00) Kuwait\",\"Europe\\/Minsk\":\"(UTC+03:00) Minsk\",\"Africa\\/Nairobi\":\"(UTC+03:00) Nairobi\",\"Asia\\/Riyadh\":\"(UTC+03:00) Riyadh\",\"Europe\\/Volgograd\":\"(UTC+03:00) Volgograd\",\"Asia\\/Tehran\":\"(UTC+03:30) Tehran\",\"Asia\\/Abu_Dhabi\":\"(UTC+04:00) Abu Dhabi\",\"Asia\\/Baku\":\"(UTC+04:00) Baku\",\"Europe\\/Moscow\":\"(UTC+04:00) Moscow\",\"Asia\\/Muscat\":\"(UTC+04:00) Muscat\",\"Europe\\/St_Petersburg\":\"(UTC+04:00) St. Petersburg\",\"Asia\\/Tbilisi\":\"(UTC+04:00) Tbilisi\",\"Asia\\/Yerevan\":\"(UTC+04:00) Yerevan\",\"Asia\\/Kabul\":\"(UTC+04:30) Kabul\",\"Asia\\/Islamabad\":\"(UTC+05:00) Islamabad\",\"Asia\\/Karachi\":\"(UTC+05:00) Karachi\",\"Asia\\/Tashkent\":\"(UTC+05:00) Tashkent\",\"Asia\\/Calcutta\":\"(UTC+05:30) Chennai\",\"Asia\\/Kolkata\":\"(UTC+05:30) Kolkata\",\"Asia\\/Mumbai\":\"(UTC+05:30) Mumbai\",\"Asia\\/New_Delhi\":\"(UTC+05:30) New Delhi\",\"Asia\\/Sri_Jayawardenepura\":\"(UTC+05:30) Sri Jayawardenepura\",\"Asia\\/Katmandu\":\"(UTC+05:45) Kathmandu\",\"Asia\\/Almaty\":\"(UTC+06:00) Almaty\",\"Asia\\/Astana\":\"(UTC+06:00) Astana\",\"Asia\\/Dhaka\":\"(UTC+06:00) Dhaka\",\"Asia\\/Yekaterinburg\":\"(UTC+06:00) Ekaterinburg\",\"Asia\\/Rangoon\":\"(UTC+06:30) Rangoon\",\"Asia\\/Bangkok\":\"(UTC+07:00) Bangkok\",\"Asia\\/Hanoi\":\"(UTC+07:00) Hanoi\",\"Asia\\/Jakarta\":\"(UTC+07:00) Jakarta\",\"Asia\\/Novosibirsk\":\"(UTC+07:00) Novosibirsk\",\"Asia\\/Beijing\":\"(UTC+08:00) Beijing\",\"Asia\\/Chongqing\":\"(UTC+08:00) Chongqing\",\"Asia\\/Hong_Kong\":\"(UTC+08:00) Hong Kong\",\"Asia\\/Krasnoyarsk\":\"(UTC+08:00) Krasnoyarsk\",\"Asia\\/Kuala_Lumpur\":\"(UTC+08:00) Kuala Lumpur\",\"Australia\\/Perth\":\"(UTC+08:00) Perth\",\"Asia\\/Singapore\":\"(UTC+08:00) Singapore\",\"Asia\\/Taipei\":\"(UTC+08:00) Taipei\",\"Asia\\/Ulan_Bator\":\"(UTC+08:00) Ulaan Bataar\",\"Asia\\/Urumqi\":\"(UTC+08:00) Urumqi\",\"Asia\\/Irkutsk\":\"(UTC+09:00) Irkutsk\",\"Asia\\/Osaka\":\"(UTC+09:00) Osaka\",\"Asia\\/Sapporo\":\"(UTC+09:00) Sapporo\",\"Asia\\/Seoul\":\"(UTC+09:00) Seoul\",\"Asia\\/Tokyo\":\"(UTC+09:00) Tokyo\",\"Australia\\/Adelaide\":\"(UTC+09:30) Adelaide\",\"Australia\\/Darwin\":\"(UTC+09:30) Darwin\",\"Australia\\/Brisbane\":\"(UTC+10:00) Brisbane\",\"Australia\\/Canberra\":\"(UTC+10:00) Canberra\",\"Pacific\\/Guam\":\"(UTC+10:00) Guam\",\"Australia\\/Hobart\":\"(UTC+10:00) Hobart\",\"Australia\\/Melbourne\":\"(UTC+10:00) Melbourne\",\"Pacific\\/Port_Moresby\":\"(UTC+10:00) Port Moresby\",\"Australia\\/Sydney\":\"(UTC+10:00) Sydney\",\"Asia\\/Yakutsk\":\"(UTC+10:00) Yakutsk\",\"Asia\\/Vladivostok\":\"(UTC+11:00) Vladivostok\",\"Pacific\\/Auckland\":\"(UTC+12:00) Auckland\",\"Pacific\\/Fiji\":\"(UTC+12:00) Fiji\",\"Pacific\\/Kwajalein\":\"(UTC+12:00) International Date Line West\",\"Asia\\/Kamchatka\":\"(UTC+12:00) Kamchatka\",\"Asia\\/Magadan\":\"(UTC+12:00) Magadan\",\"Pacific\\/Marshall_Is\":\"(UTC+12:00) Marshall Is.\",\"Asia\\/New_Caledonia\":\"(UTC+12:00) New Caledonia\",\"Asia\\/Solomon_Is\":\"(UTC+12:00) Solomon Is.\",\"Pacific\\/Wellington\":\"(UTC+12:00) Wellington\",\"Pacific\\/Tongatapu\":\"(UTC+13:00) Nuku\'alofa\"},\"placeholder\":\"Choose a timezone...\"}',1,NULL,1,0,0,0,11,'half',NULL,NULL,NULL),(131,'directus_users','locale','string','language','{\"limit\":true}',1,NULL,1,0,0,0,12,'half',NULL,NULL,NULL),(132,'directus_users','locale_options','json','json',NULL,1,NULL,0,0,1,1,13,'full',NULL,NULL,NULL),(133,'directus_users','token','string','text-input',NULL,1,NULL,0,0,1,1,14,'full',NULL,NULL,NULL),(134,'directus_users','last_login','datetime','datetime',NULL,1,NULL,0,1,0,0,15,'half',NULL,NULL,NULL),(135,'directus_users','last_access_on','datetime','datetime',NULL,1,NULL,0,1,1,0,16,'half',NULL,NULL,NULL),(136,'directus_users','last_page','string','text-input',NULL,1,NULL,0,1,1,1,17,'half',NULL,NULL,NULL),(137,'directus_users','avatar','file','file',NULL,1,NULL,0,0,0,0,18,'full',NULL,NULL,NULL),(138,'directus_users','invite_token','string','text-input',NULL,1,NULL,0,0,1,1,NULL,'full',NULL,NULL,NULL),(139,'directus_users','invite_accepted','boolean','toggle',NULL,1,NULL,0,0,1,1,NULL,'full',NULL,NULL,NULL),(140,'directus_users','last_ip','string','text-input',NULL,1,NULL,0,1,1,0,NULL,'full',NULL,NULL,NULL),(141,'directus_users','external_id','string','text-input',NULL,1,NULL,0,1,1,0,NULL,'full',NULL,NULL,NULL),(142,'directus_user_roles','id','integer','primary-key',NULL,1,NULL,1,0,1,0,NULL,'full',NULL,NULL,NULL),(143,'directus_user_roles','user','m2o','many-to-one',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL),(144,'directus_user_roles','role','m2o','many-to-one',NULL,1,NULL,0,0,0,0,NULL,'full',NULL,NULL,NULL); - -/*Table structure for table `directus_files` */ - -DROP TABLE IF EXISTS `directus_files`; +) ENGINE=InnoDB AUTO_INCREMENT=167 DEFAULT CHARSET=utf8; CREATE TABLE `directus_files` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `storage` varchar(50) NOT NULL DEFAULT 'local', - `filename` varchar(255) NOT NULL, + `private_hash` varchar(16) DEFAULT NULL, + `filename_disk` varchar(255) NOT NULL, + `filename_download` varchar(255) NOT NULL, `title` varchar(255) DEFAULT NULL, `type` varchar(255) DEFAULT NULL, `uploaded_by` int(11) unsigned NOT NULL, `uploaded_on` datetime NOT NULL, `charset` varchar(50) DEFAULT NULL, - `filesize` int(11) unsigned NOT NULL DEFAULT '0', + `filesize` int(11) unsigned NOT NULL DEFAULT 0, `width` int(11) unsigned DEFAULT NULL, `height` int(11) unsigned DEFAULT NULL, `duration` int(11) DEFAULT NULL, `embed` varchar(200) DEFAULT NULL, `folder` int(11) unsigned DEFAULT NULL, - `description` text, + `description` text DEFAULT NULL, `location` varchar(200) DEFAULT NULL, `tags` varchar(255) DEFAULT NULL, `checksum` varchar(32) DEFAULT NULL, - `metadata` text, + `metadata` text DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*Data for the table `directus_files` */ - -/*Table structure for table `directus_folders` */ - -DROP TABLE IF EXISTS `directus_folders`; - CREATE TABLE `directus_folders` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(191) CHARACTER SET utf8mb4 NOT NULL, @@ -164,29 +117,15 @@ CREATE TABLE `directus_folders` ( UNIQUE KEY `idx_name_parent_folder` (`name`,`parent_folder`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*Data for the table `directus_folders` */ - -/*Table structure for table `directus_migrations` */ - -DROP TABLE IF EXISTS `directus_migrations`; - CREATE TABLE `directus_migrations` ( `version` bigint(20) NOT NULL, `migration_name` varchar(100) DEFAULT NULL, `start_time` timestamp NULL DEFAULT NULL, `end_time` timestamp NULL DEFAULT NULL, - `breakpoint` tinyint(1) NOT NULL DEFAULT '0', + `breakpoint` tinyint(1) NOT NULL DEFAULT 0, PRIMARY KEY (`version`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*Data for the table `directus_migrations` */ - -insert into `directus_migrations`(`version`,`migration_name`,`start_time`,`end_time`,`breakpoint`) values (20180220023138,'CreateActivityTable','2019-06-22 07:28:11','2019-06-22 07:28:11',0),(20180220023144,'CreateActivitySeenTable','2019-06-22 07:28:11','2019-06-22 07:28:11',0),(20180220023152,'CreateCollectionsPresetsTable','2019-06-22 07:28:11','2019-06-22 07:28:11',0),(20180220023157,'CreateCollectionsTable','2019-06-22 07:28:11','2019-06-22 07:28:11',0),(20180220023202,'CreateFieldsTable','2019-06-22 07:28:11','2019-06-22 07:28:11',0),(20180220023208,'CreateFilesTable','2019-06-22 07:28:11','2019-06-22 07:28:11',0),(20180220023213,'CreateFoldersTable','2019-06-22 07:28:11','2019-06-22 07:28:11',0),(20180220023217,'CreateRolesTable','2019-06-22 07:28:11','2019-06-22 07:28:11',0),(20180220023226,'CreatePermissionsTable','2019-06-22 07:28:11','2019-06-22 07:28:11',0),(20180220023232,'CreateRelationsTable','2019-06-22 07:28:11','2019-06-22 07:28:11',0),(20180220023238,'CreateRevisionsTable','2019-06-22 07:28:11','2019-06-22 07:28:11',0),(20180220023243,'CreateSettingsTable','2019-06-22 07:28:11','2019-06-22 07:28:11',0),(20180220023248,'CreateUsersTable','2019-06-22 07:28:11','2019-06-22 07:28:11',0),(20180426173310,'CreateUserRoles','2019-06-22 07:28:11','2019-06-22 07:28:12',0),(20181022175715,'Upgrade070003','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20181102153600,'TimezoneChoices','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20181105165224,'Upgrade070006','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20181122195602,'LocaleInterface','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20181123171520,'RemoveScope','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20181210204720,'AddTrustedProxiesSettingField','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20181222023800,'AddProjectUrlSettingField','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20181227042755,'IncreaseUsersLastPageLength','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20190104155309,'AddUsersEmailValidation','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20190111193724,'AddAppUrlSettingField','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20190111212736,'AddMissingSettingsField','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20190118181424,'AddRolesUsersField','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20190130215921,'AddFilesChecksumField','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20190318173400,'AddNavOverride','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20190412122400,'SetPasswordTypeToHash','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20190412123000,'UseFileInterface','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20190412145800,'SetO2MOptionsRoles','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20190415125300,'SetWidth','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20190419154400,'SettingsFields','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20190419161200,'CollectionNotes','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20190419173000,'MiscRequiredFields','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20190422131600,'UseJson','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20190426156200,'SetNullableValueFieldSettingsTable','2019-06-22 07:28:32','2019-06-22 07:28:32',0),(20190520094300,'UseTimeline','2019-06-22 07:28:32','2019-06-22 07:28:32',0); - -/*Table structure for table `directus_permissions` */ - -DROP TABLE IF EXISTS `directus_permissions`; - CREATE TABLE `directus_permissions` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `collection` varchar(64) NOT NULL, @@ -204,12 +143,6 @@ CREATE TABLE `directus_permissions` ( PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*Data for the table `directus_permissions` */ - -/*Table structure for table `directus_relations` */ - -DROP TABLE IF EXISTS `directus_relations`; - CREATE TABLE `directus_relations` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `collection_many` varchar(64) NOT NULL, @@ -218,15 +151,7 @@ CREATE TABLE `directus_relations` ( `field_one` varchar(64) DEFAULT NULL, `junction_field` varchar(64) DEFAULT NULL, PRIMARY KEY (`id`) -) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8; - -/*Data for the table `directus_relations` */ - -insert into `directus_relations`(`id`,`collection_many`,`field_many`,`collection_one`,`field_one`,`junction_field`) values (1,'directus_activity','action_by','directus_users',NULL,NULL),(2,'directus_activity_seen','user','directus_users',NULL,NULL),(3,'directus_activity_seen','activity','directus_activity',NULL,NULL),(4,'directus_collections_presets','user','directus_users',NULL,NULL),(5,'directus_collections_presets','group','directus_groups',NULL,NULL),(6,'directus_files','uploaded_by','directus_users',NULL,NULL),(7,'directus_files','folder','directus_folders',NULL,NULL),(8,'directus_folders','parent_folder','directus_folders',NULL,NULL),(9,'directus_permissions','group','directus_groups',NULL,NULL),(10,'directus_revisions','activity','directus_activity',NULL,NULL),(11,'directus_user_roles','user','directus_users','roles','role'),(12,'directus_user_roles','role','directus_roles','users','user'),(13,'directus_users','avatar','directus_files',NULL,NULL),(14,'directus_fields','collection','directus_collections','fields',NULL); - -/*Table structure for table `directus_revisions` */ - -DROP TABLE IF EXISTS `directus_revisions`; +) ENGINE=InnoDB AUTO_INCREMENT=12 DEFAULT CHARSET=utf8; CREATE TABLE `directus_revisions` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, @@ -234,101 +159,364 @@ CREATE TABLE `directus_revisions` ( `collection` varchar(64) NOT NULL, `item` varchar(255) NOT NULL, `data` longtext NOT NULL, - `delta` longtext, + `delta` longtext DEFAULT NULL, `parent_collection` varchar(64) DEFAULT NULL, `parent_item` varchar(255) DEFAULT NULL, - `parent_changed` tinyint(1) unsigned DEFAULT '0', + `parent_changed` tinyint(1) unsigned DEFAULT 0, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -/*Data for the table `directus_revisions` */ - -/*Table structure for table `directus_roles` */ - -DROP TABLE IF EXISTS `directus_roles`; - CREATE TABLE `directus_roles` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(100) NOT NULL, `description` varchar(500) DEFAULT NULL, - `ip_whitelist` text, - `nav_blacklist` text, + `ip_whitelist` text DEFAULT NULL, `external_id` varchar(255) DEFAULT NULL, - `nav_override` text, + `module_listing` text DEFAULT NULL, + `collection_listing` text DEFAULT NULL, + `enforce_2fa` tinyint(1) DEFAULT 0, PRIMARY KEY (`id`), UNIQUE KEY `idx_group_name` (`name`), UNIQUE KEY `idx_roles_external_id` (`external_id`) ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; -/*Data for the table `directus_roles` */ - -insert into `directus_roles`(`id`,`name`,`description`,`ip_whitelist`,`nav_blacklist`,`external_id`,`nav_override`) values (1,'Administrator','Admins have access to all managed data within the system by default',NULL,NULL,NULL,NULL),(2,'Public','This sets the data that is publicly available through the API without a token',NULL,NULL,NULL,NULL); - -/*Table structure for table `directus_settings` */ - -DROP TABLE IF EXISTS `directus_settings`; - CREATE TABLE `directus_settings` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `key` varchar(64) NOT NULL, - `value` text, + `value` text NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `idx_key` (`key`) -) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8; +) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8; -/*Data for the table `directus_settings` */ - -insert into `directus_settings`(`id`,`key`,`value`) values (1,'logo',''),(2,'color','darkest-gray'),(3,'default_limit','200'),(4,'sort_null_last','1'),(5,'auto_sign_out','60'),(6,'youtube_api_key',''),(7,'trusted_proxies',''),(8,'thumbnail_dimensions','200x200'),(9,'thumbnail_quality_tags','{\"poor\": 25, \"good\": 50, \"better\": 75, \"best\": 100}'),(10,'thumbnail_actions','{\"contain\":{\"options\":{\"resizeCanvas\":false,\"position\":\"center\",\"resizeRelative\":false,\"canvasBackground\":\"ccc\"}},\"crop\":{\"options\":{\"position\":\"center\"}}}'),(11,'thumbnail_cache_ttl','86400'),(12,'thumbnail_not_found_location',''),(13,'project_name','Directus'),(14,'app_url',''),(15,'project_url',''); - -/*Table structure for table `directus_user_roles` */ - -DROP TABLE IF EXISTS `directus_user_roles`; - -CREATE TABLE `directus_user_roles` ( +CREATE TABLE `directus_user_sessions` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `user` int(11) unsigned DEFAULT NULL, - `role` int(11) unsigned DEFAULT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `idx_user_role` (`user`,`role`) -) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; - -/*Data for the table `directus_user_roles` */ - -insert into `directus_user_roles`(`id`,`user`,`role`) values (1,1,1); - -/*Table structure for table `directus_users` */ - -DROP TABLE IF EXISTS `directus_users`; + `token_type` varchar(255) DEFAULT NULL, + `token` varchar(520) DEFAULT NULL, + `ip_address` varchar(255) DEFAULT NULL, + `user_agent` text DEFAULT NULL, + `created_on` datetime DEFAULT NULL, + `token_expired_at` datetime DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `directus_users` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `status` varchar(16) NOT NULL DEFAULT 'draft', + `role` int(11) DEFAULT NULL, `first_name` varchar(50) DEFAULT NULL, `last_name` varchar(50) DEFAULT NULL, `email` varchar(128) NOT NULL, `password` varchar(255) DEFAULT NULL, `token` varchar(255) DEFAULT NULL, - `timezone` varchar(32) NOT NULL DEFAULT 'UTC', - `locale` varchar(8) DEFAULT 'en-US', - `locale_options` text, + `timezone` varchar(32) NOT NULL DEFAULT 'America/New_York', + `locale` varchar(8) DEFAULT NULL, + `locale_options` text DEFAULT NULL, `avatar` int(11) unsigned DEFAULT NULL, `company` varchar(191) DEFAULT NULL, `title` varchar(191) DEFAULT NULL, - `email_notifications` int(1) NOT NULL DEFAULT '1', + `email_notifications` int(1) NOT NULL DEFAULT 1, `last_access_on` datetime DEFAULT NULL, `last_page` varchar(192) DEFAULT NULL, `external_id` varchar(255) DEFAULT NULL, + `theme` varchar(100) DEFAULT 'auto', + `2fa_secret` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `idx_users_email` (`email`), UNIQUE KEY `idx_users_token` (`token`), UNIQUE KEY `idx_users_external_id` (`external_id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8; -/*Data for the table `directus_users` */ - -insert into `directus_users`(`id`,`status`,`first_name`,`last_name`,`email`,`password`,`token`,`timezone`,`locale`,`locale_options`,`avatar`,`company`,`title`,`email_notifications`,`last_access_on`,`last_page`,`external_id`) values (1,'active','Admin','User','admin@example.com','$2y$12$WzyHxXSOPzSKtDeOQek2P.mlHyYTslUUzZim9nG8aqUMS8ekhGI3m',NULL,'UTC','en-US',NULL,NULL,NULL,NULL,1,'2019-06-22 07:28:29','/settings',NULL); +CREATE TABLE `directus_webhooks` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `status` varchar(16) NOT NULL DEFAULT 'inactive', + `http_action` varchar(255) DEFAULT NULL, + `url` varchar(510) DEFAULT NULL, + `collection` varchar(255) DEFAULT NULL, + `directus_action` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +INSERT INTO `directus_collection_presets` (`id`, `title`, `user`, `role`, `collection`, `search_query`, `filters`, `view_type`, `view_query`, `view_options`, `translation`) VALUES ('1', NULL, NULL, NULL, 'directus_activity', NULL, NULL, 'timeline', '{\"timeline\":{\"sort\":\"-action_on\"}}', '{\"timeline\":{\"date\":\"action_on\",\"title\":\"{{ action }} by {{ action_by.first_name }} {{ action_by.last_name }} (#{{ item }})\",\"content\":\"collection\",\"color\":\"action\"}}', NULL), +('2', NULL, NULL, NULL, 'directus_files', NULL, NULL, 'cards', NULL, '{\"cards\":{\"title\":\"title\",\"subtitle\":\"type\",\"content\":\"description\",\"src\":\"data\"}}', NULL), +('3', NULL, NULL, NULL, 'directus_users', NULL, NULL, 'cards', NULL, '{\"cards\":{\"title\":\"first_name\",\"subtitle\":\"last_name\",\"content\":\"title\",\"src\":\"avatar\",\"icon\":\"person\"}}', NULL), +('4', NULL, NULL, NULL, 'directus_webhooks', NULL, NULL, 'tabular', '{\"tabular\":{\"fields\":\"status,http_action,url,collection,directus_action\"}}', '{\"tabular\":{\"widths\":{\"status\":32,\"http_action\":72,\"url\":200,\"collection\":200,\"directus_action\":200}}}', NULL); + +INSERT INTO `directus_fields` (`id`, `collection`, `field`, `type`, `interface`, `options`, `locked`, `validation`, `required`, `readonly`, `hidden_detail`, `hidden_browse`, `sort`, `width`, `group`, `note`, `translation`) VALUES ('1', 'directus_fields', 'id', 'integer', 'primary-key', NULL, '1', NULL, '1', '0', '1', '0', NULL, 'full', NULL, NULL, NULL), +('2', 'directus_fields', 'collection', 'm2o', 'many-to-one', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('3', 'directus_fields', 'field', 'string', 'text-input', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('4', 'directus_fields', 'type', 'string', 'primary-key', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('5', 'directus_fields', 'interface', 'string', 'primary-key', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('6', 'directus_fields', 'options', 'json', 'json', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('7', 'directus_fields', 'locked', 'boolean', 'toggle', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('8', 'directus_fields', 'translation', 'json', 'repeater', '{\n \"fields\": [\n {\n \"field\": \"locale\",\n \"type\": \"string\",\n \"interface\": \"language\",\n \"options\": {\n \"limit\": true\n },\n \"width\": \"half\"\n },\n {\n \"field\": \"translation\",\n \"type\": \"string\",\n \"interface\": \"text-input\",\n \"width\": \"half\"\n }\n ]\n }', '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('9', 'directus_fields', 'readonly', 'boolean', 'toggle', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('10', 'directus_fields', 'validation', 'string', 'text-input', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('11', 'directus_fields', 'required', 'boolean', 'toggle', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('12', 'directus_fields', 'sort', 'sort', 'sort', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('13', 'directus_fields', 'note', 'string', 'text-input', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('14', 'directus_fields', 'hidden_detail', 'boolean', 'toggle', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('15', 'directus_fields', 'hidden_browse', 'boolean', 'toggle', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('16', 'directus_fields', 'width', 'integer', 'numeric', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('17', 'directus_fields', 'group', 'm2o', 'many-to-one', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('18', 'directus_activity', 'id', 'integer', 'primary-key', NULL, '1', NULL, '1', '1', '1', '0', NULL, 'full', NULL, NULL, NULL), +('19', 'directus_activity', 'action', 'string', 'text-input', '{\"iconRight\":\"change_history\"}', '1', NULL, '0', '1', '0', '0', '1', 'full', NULL, NULL, NULL), +('20', 'directus_activity', 'collection', 'string', 'collections', '{\"iconRight\":\"list_alt\",\"include_system\":true}', '1', NULL, '0', '1', '0', '0', '2', 'half', NULL, NULL, NULL), +('21', 'directus_activity', 'item', 'string', 'text-input', '{\"iconRight\":\"link\"}', '1', NULL, '0', '1', '0', '0', '3', 'half', NULL, NULL, NULL), +('22', 'directus_activity', 'action_by', 'integer', 'user', '{\"iconRight\":\"account_circle\"}', '1', NULL, '0', '1', '0', '0', '4', 'half', NULL, NULL, NULL), +('23', 'directus_activity', 'action_on', 'datetime', 'datetime', '{\"showRelative\":true,\"iconRight\":\"calendar_today\"}', '1', NULL, '0', '1', '0', '0', '5', 'half', NULL, NULL, NULL), +('24', 'directus_activity', 'edited_on', 'datetime', 'datetime', '{\"showRelative\":true,\"iconRight\":\"edit\"}', '1', NULL, '0', '1', '0', '0', '6', 'half', NULL, NULL, NULL), +('25', 'directus_activity', 'comment_deleted_on', 'datetime', 'datetime', '{\"showRelative\":true,\"iconRight\":\"delete_outline\"}', '1', NULL, '0', '1', '0', '0', '7', 'half', NULL, NULL, NULL), +('26', 'directus_activity', 'ip', 'string', 'text-input', '{\"iconRight\":\"my_location\"}', '1', NULL, '0', '1', '0', '0', '8', 'half', NULL, NULL, NULL), +('27', 'directus_activity', 'user_agent', 'string', 'text-input', '{\"iconRight\":\"devices_other\"}', '1', NULL, '0', '1', '0', '0', '9', 'half', NULL, NULL, NULL), +('28', 'directus_activity', 'comment', 'string', 'textarea', NULL, '1', NULL, '0', '1', '0', '0', '10', 'full', NULL, NULL, NULL), +('29', 'directus_collection_presets', 'id', 'integer', 'primary-key', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('30', 'directus_collection_presets', 'title', 'string', 'text-input', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('31', 'directus_collection_presets', 'user', 'integer', 'user', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('32', 'directus_collection_presets', 'role', 'm2o', 'many-to-one', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('33', 'directus_collection_presets', 'collection', 'm2o', 'many-to-one', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('34', 'directus_collection_presets', 'search_query', 'string', 'text-input', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('35', 'directus_collection_presets', 'filters', 'json', 'json', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('36', 'directus_collection_presets', 'view_options', 'json', 'json', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('37', 'directus_collection_presets', 'view_type', 'string', 'text-input', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('38', 'directus_collection_presets', 'view_query', 'json', 'json', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('39', 'directus_collections', 'fields', 'o2m', 'one-to-many', NULL, '1', NULL, '0', '0', '1', '1', '1', 'full', NULL, NULL, NULL), +('40', 'directus_collections', 'collection', 'string', 'primary-key', NULL, '1', NULL, '1', '1', '0', '0', '2', 'half', NULL, NULL, NULL), +('41', 'directus_collections', 'note', 'string', 'text-input', NULL, '1', NULL, '0', '0', '0', '0', '3', 'half', NULL, 'An internal description.', NULL), +('42', 'directus_collections', 'managed', 'boolean', 'toggle', NULL, '1', NULL, '0', '0', '1', '0', '4', 'half', NULL, '[Learn More](https://docs.directus.io/guides/collections.html#managing-collections).', NULL), +('43', 'directus_collections', 'hidden', 'boolean', 'toggle', NULL, '1', NULL, '0', '0', '0', '0', '5', 'half', NULL, '[Learn More](https://docs.directus.io/guides/collections.html#hidden).', NULL), +('44', 'directus_collections', 'single', 'boolean', 'toggle', NULL, '1', NULL, '0', '0', '0', '0', '6', 'half', NULL, '[Learn More](https://docs.directus.io/guides/collections.html#single).', NULL), +('45', 'directus_collections', 'translation', 'json', 'repeater', '{\n \"fields\": [\n {\n \"field\": \"locale\",\n \"type\": \"string\",\n \"interface\": \"language\",\n \"options\": {\n \"limit\": true\n },\n \"width\": \"half\"\n },\n {\n \"field\": \"translation\",\n \"type\": \"string\",\n \"interface\": \"text-input\",\n \"width\": \"half\"\n }\n ]\n }', '1', NULL, '0', '0', '0', '0', '7', 'full', NULL, NULL, NULL), +('46', 'directus_collections', 'icon', 'string', 'icon', NULL, '1', NULL, '0', '0', '0', '0', '8', 'full', NULL, 'The icon shown in the App\'s navigation sidebar.', NULL), +('47', 'directus_files', 'preview', 'alias', 'file-preview', NULL, '1', NULL, '0', '0', '0', '0', '1', 'full', NULL, NULL, NULL), +('48', 'directus_files', 'title', 'string', 'text-input', '{\"placeholder\":\"Enter a descriptive title...\",\"iconRight\":\"title\"}', '1', NULL, '0', '0', '0', '0', '2', 'full', NULL, NULL, NULL), +('49', 'directus_files', 'tags', 'array', 'tags', '{\"placeholder\":\"Enter a keyword then hit enter...\"}', '1', NULL, '0', '0', '0', '0', '3', 'half', NULL, NULL, NULL), +('50', 'directus_files', 'location', 'string', 'text-input', '{\"placeholder\":\"Enter a location...\",\"iconRight\":\"place\"}', '1', NULL, '0', '0', '0', '0', '4', 'half', NULL, NULL, NULL), +('51', 'directus_files', 'description', 'string', 'wysiwyg', '{\"toolbar\":[\"bold\",\"italic\",\"underline\",\"link\",\"code\"]}', '1', NULL, '0', '0', '0', '0', '5', 'full', NULL, NULL, NULL), +('52', 'directus_files', 'filename_download', 'string', 'text-input', '{\"monospace\":true,\"iconRight\":\"get_app\"}', '1', NULL, '0', '0', '0', '0', '6', 'full', NULL, NULL, NULL), +('53', 'directus_files', 'filename_disk', 'string', 'text-input', '{\"placeholder\":\"Enter a unique file name...\",\"iconRight\":\"insert_drive_file\"}', '1', NULL, '0', '0', '0', '0', '7', 'full', NULL, NULL, NULL), +('54', 'directus_files', 'private_hash', 'string', 'slug', '{\"iconRight\":\"lock\"}', '1', NULL, '0', '0', '0', '0', '8', 'half', NULL, NULL, NULL), +('55', 'directus_files', 'checksum', 'string', 'text-input', '{\"iconRight\":\"check\",\"monospace\":true}', '1', NULL, '0', '1', '0', '0', '9', 'half', NULL, NULL, NULL), +('56', 'directus_files', 'uploaded_on', 'datetime', 'datetime', '{\"iconRight\":\"today\"}', '1', NULL, '1', '1', '0', '0', '10', 'half', NULL, NULL, NULL), +('57', 'directus_files', 'uploaded_by', 'user_created', 'user-created', NULL, '1', NULL, '1', '1', '0', '0', '11', 'half', NULL, NULL, NULL), +('58', 'directus_files', 'width', 'integer', 'numeric', '{\"iconRight\":\"straighten\"}', '1', NULL, '0', '1', '0', '0', '12', 'half', NULL, NULL, NULL), +('59', 'directus_files', 'height', 'integer', 'numeric', '{\"iconRight\":\"straighten\"}', '1', NULL, '0', '1', '0', '0', '13', 'half', NULL, NULL, NULL), +('60', 'directus_files', 'duration', 'integer', 'numeric', '{\"iconRight\":\"timer\"}', '1', NULL, '0', '1', '0', '0', '14', 'half', NULL, NULL, NULL), +('61', 'directus_files', 'filesize', 'integer', 'file-size', '{\"iconRight\":\"storage\"}', '1', NULL, '0', '1', '0', '0', '15', 'half', NULL, NULL, NULL), +('62', 'directus_files', 'metadata', 'json', 'key-value', '{\"keyInterface\":\"text-input\",\"keyDataType\":\"string\",\"keyOptions\":{\"monospace\":true,\"placeholder\":\"Key\"},\"valueInterface\":\"text-input\",\"valueDataType\":\"string\",\"valueOptions\":{\"monospace\":true,\"placeholder\":\"Value\"}}', '1', NULL, '0', '0', '0', '0', '15', 'full', NULL, NULL, NULL), +('63', 'directus_files', 'data', 'alias', 'file', NULL, '1', NULL, '0', '0', '1', '0', NULL, 'full', NULL, NULL, NULL), +('64', 'directus_files', 'id', 'integer', 'primary-key', NULL, '1', NULL, '1', '0', '1', '0', NULL, 'full', NULL, NULL, NULL), +('65', 'directus_files', 'type', 'string', 'text-input', NULL, '1', NULL, '0', '1', '1', '0', NULL, 'full', NULL, NULL, NULL), +('66', 'directus_files', 'charset', 'string', 'text-input', NULL, '1', NULL, '0', '1', '1', '1', NULL, 'full', NULL, NULL, NULL), +('67', 'directus_files', 'embed', 'string', 'text-input', NULL, '1', NULL, '0', '1', '1', '0', NULL, 'full', NULL, NULL, NULL), +('68', 'directus_files', 'folder', 'm2o', 'many-to-one', NULL, '1', NULL, '0', '0', '1', '0', NULL, 'full', NULL, NULL, NULL), +('69', 'directus_files', 'storage', 'string', 'text-input', NULL, '1', NULL, '0', '0', '1', '1', NULL, 'full', NULL, NULL, NULL), +('70', 'directus_folders', 'id', 'integer', 'primary-key', NULL, '1', NULL, '1', '0', '1', '0', NULL, 'full', NULL, NULL, NULL), +('71', 'directus_folders', 'name', 'string', 'text-input', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('72', 'directus_folders', 'parent_folder', 'm2o', 'many-to-one', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('73', 'directus_roles', 'id', 'integer', 'primary-key', NULL, '1', NULL, '1', '0', '1', '0', NULL, 'full', NULL, NULL, NULL), +('74', 'directus_roles', 'external_id', 'string', 'text-input', NULL, '1', NULL, '0', '1', '1', '1', NULL, 'full', NULL, NULL, NULL), +('75', 'directus_roles', 'name', 'string', 'text-input', NULL, '1', NULL, '1', '0', '0', '0', '1', 'half', NULL, NULL, NULL), +('76', 'directus_roles', 'description', 'string', 'text-input', NULL, '1', NULL, '0', '0', '0', '0', '2', 'half', NULL, NULL, NULL), +('77', 'directus_roles', 'ip_whitelist', 'array', 'tags', '{\"\":\"Add an IP address...\"}', '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('78', 'directus_roles', 'enforce_2fa', 'boolean', 'toggle', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('79', 'directus_roles', 'users', 'o2m', 'one-to-many', '{\"fields\":\"first_name,last_name\"}', '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('80', 'directus_roles', 'module_listing', 'json', 'repeater', '{\n \"fields\": [\n {\n \"field\": \"name\",\n \"interface\": \"text-input\",\n \"type\": \"string\",\n \"width\": \"half\"\n },\n {\n \"field\": \"link\",\n \"interface\": \"text-input\",\n \"type\": \"string\",\n \"width\": \"half\"\n },\n {\n \"field\": \"icon\",\n \"interface\": \"icon\",\n \"type\": \"string\",\n \"width\": \"full\"\n }\n ]\n }', '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('81', 'directus_roles', 'collection_listing', 'json', 'repeater', '{\n \"fields\": [\n {\n \"field\": \"groups\",\n \"width\": \"full\",\n \"interface\": \"repeater\",\n \"type\": \"JSON\",\n \"options\": {\n \"template\": \"{{ label }}\",\n \"fields\": [\n {\n \"field\": \"label\",\n \"interface\": \"text-input\",\n \"type\": \"string\"\n },\n {\n \"field\": \"value\",\n \"interface\": \"text-input\",\n \"type\": \"string\"\n },\n {\n \"field\": \"icon\",\n \"width\": \"full\",\n \"interface\": \"icon\",\n \"type\": \"string\"\n }\n ]\n }\n }\n ]\n }', '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('82', 'directus_permissions', 'id', 'integer', 'primary-key', NULL, '1', NULL, '1', '0', '1', '0', NULL, 'full', NULL, NULL, NULL), +('83', 'directus_permissions', 'collection', 'm2o', 'many-to-one', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('84', 'directus_permissions', 'role', 'm2o', 'many-to-one', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('85', 'directus_permissions', 'status', 'string', 'text-input', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('86', 'directus_permissions', 'create', 'string', 'text-input', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('87', 'directus_permissions', 'read', 'string', 'text-input', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('88', 'directus_permissions', 'update', 'string', 'text-input', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('89', 'directus_permissions', 'delete', 'string', 'primary-key', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('90', 'directus_permissions', 'comment', 'string', 'text-input', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('91', 'directus_permissions', 'explain', 'string', 'text-input', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('92', 'directus_permissions', 'status_blacklist', 'array', 'tags', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('93', 'directus_permissions', 'read_field_blacklist', 'array', 'tags', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('94', 'directus_permissions', 'write_field_blacklist', 'array', 'tags', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('95', 'directus_relations', 'id', 'integer', 'primary-key', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('96', 'directus_relations', 'collection_many', 'string', 'collections', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('97', 'directus_relations', 'field_many', 'string', 'text-input', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('98', 'directus_relations', 'collection_one', 'string', 'collections', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('99', 'directus_relations', 'field_one', 'string', 'text-input', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('100', 'directus_relations', 'junction_field', 'string', 'text-input', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('101', 'directus_revisions', 'id', 'integer', 'primary-key', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('102', 'directus_revisions', 'activity', 'm2o', 'many-to-one', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('103', 'directus_revisions', 'collection', 'm2o', 'many-to-one', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('104', 'directus_revisions', 'item', 'string', 'text-input', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('105', 'directus_revisions', 'data', 'json', 'json', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('106', 'directus_revisions', 'delta', 'json', 'json', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('107', 'directus_revisions', 'parent_item', 'string', 'text-input', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('108', 'directus_revisions', 'parent_collection', 'string', 'collections', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('109', 'directus_revisions', 'parent_changed', 'boolean', 'toggle', NULL, '1', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('110', 'directus_settings', 'project_name', 'string', 'text-input', '{\"iconRight\":\"title\"}', '1', NULL, '1', '0', '0', '0', '1', 'half', NULL, 'Logo in the top-left of the App (40x40)', NULL), +('111', 'directus_settings', 'project_url', 'string', 'text-input', '{\"iconRight\":\"link\"}', '1', NULL, '0', '0', '0', '0', '2', 'half', NULL, 'External link for the App\'s top-left logo', NULL), +('112', 'directus_settings', 'project_logo', 'file', 'file', NULL, '1', NULL, '0', '0', '0', '0', '3', 'half', NULL, 'A 40x40 brand logo, ideally a white SVG/PNG', NULL), +('113', 'directus_settings', 'project_color', 'string', 'color', NULL, '1', NULL, '0', '0', '0', '0', '4', 'half', NULL, 'Color for login background and App\'s logo', NULL), +('114', 'directus_settings', 'project_foreground', 'file', 'file', NULL, '1', NULL, '0', '0', '0', '0', '5', 'half', NULL, 'Centered image (eg: logo) for the login page', NULL), +('115', 'directus_settings', 'project_background', 'file', 'file', NULL, '1', NULL, '0', '0', '0', '0', '6', 'half', NULL, 'Full-screen background for the login page', NULL), +('116', 'directus_settings', 'default_locale', 'string', 'language', '{\"limit\":true}', '1', NULL, '0', '0', '0', '0', '7', 'half', NULL, 'Default locale for Directus Users', NULL), +('117', 'directus_settings', 'telemetry', 'boolean', 'toggle', NULL, '1', NULL, '0', '0', '0', '0', '8', 'half', NULL, 'Learn More', NULL), +('118', 'directus_settings', 'data_divider', 'alias', 'divider', '{\"style\":\"large\",\"title\":\"Data\",\"hr\":true}', '1', NULL, '0', '0', '0', '1', '10', 'full', NULL, NULL, NULL), +('119', 'directus_settings', 'default_limit', 'integer', 'numeric', '{\"iconRight\":\"keyboard_tab\"}', '1', NULL, '1', '0', '0', '0', '11', 'half', NULL, 'Default item count in API and App responses', NULL), +('120', 'directus_settings', 'sort_null_last', 'boolean', 'toggle', NULL, '1', NULL, '0', '0', '0', '0', '12', 'half', NULL, 'NULL values are sorted last', NULL), +('121', 'directus_settings', 'security_divider', 'alias', 'divider', '{\"style\":\"large\",\"title\":\"Security\",\"hr\":true}', '1', NULL, '0', '0', '0', '1', '20', 'full', NULL, NULL, NULL), +('122', 'directus_settings', 'auto_sign_out', 'integer', 'numeric', '{\"iconRight\":\"timer\"}', '1', NULL, '1', '0', '0', '0', '22', 'half', NULL, 'Minutes before idle users are signed out', NULL), +('123', 'directus_settings', 'login_attempts_allowed', 'integer', 'numeric', '{\"iconRight\":\"lock\"}', '1', NULL, '0', '0', '0', '0', '23', 'half', NULL, 'Failed login attempts before suspending users', NULL), +('124', 'directus_settings', 'password_policy', 'string', 'dropdown', '{\"choices\":{\"\":\"None\",\"\\/^.{8,}$\\/\":\"Weak\",\"\\/(?=^.{8,}$)(?=.*\\\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[!@#$%^&*()_+}{\';\'?>.<,])(?!.*\\\\s).*$\\/\":\"Strong\"}}', '1', NULL, '0', '0', '0', '0', '24', 'half', NULL, 'Weak: Minimum length 8; Strong: 1 small-case letter, 1 capital letter, 1 digit, 1 special character and the length should be minimum 8', NULL), +('125', 'directus_settings', 'files_divider', 'alias', 'divider', '{\"style\":\"large\",\"title\":\"Files & Thumbnails\",\"hr\":true}', '1', NULL, '0', '0', '0', '1', '30', 'full', NULL, NULL, NULL), +('126', 'directus_settings', 'file_naming', 'string', 'dropdown', '{\"choices\":{\"uuid\":\"UUID (Obfuscated)\",\"file_name\":\"File Name (Readable)\"}}', '1', NULL, '0', '0', '0', '0', '31', 'half', NULL, 'File-system naming convention for uploads', NULL), +('127', 'directus_settings', 'file_max_size', 'string', 'text-input', '{\"placeholder\":\"eg: 4MB\",\"iconRight\":\"storage\"}', '1', NULL, '0', '0', '0', '0', '32', 'half', NULL, NULL, NULL), +('128', 'directus_settings', 'file_mimetype_whitelist', 'array', 'tags', '{\"placeholder\":\"Enter a file mimetype then hit enter (eg: image\\/jpeg)\"}', '1', NULL, '0', '0', '0', '0', '33', 'full', NULL, NULL, NULL), +('129', 'directus_settings', 'asset_whitelist', 'json', 'repeater', '{\"template\":\"{{key}}\",\"fields\":[{\"field\":\"key\",\"interface\":\"slug\",\"width\":\"half\",\"type\":\"string\",\"required\":true},{\"field\":\"fit\",\"interface\":\"dropdown\",\"width\":\"half\",\"type\":\"string\",\"options\":{\"choices\":{\"crop\":\"Crop (forces exact size)\",\"contain\":\"Contain (preserve aspect ratio)\"}},\"required\":true},{\"field\":\"width\",\"interface\":\"numeric\",\"width\":\"half\",\"type\":\"integer\",\"required\":true},{\"field\":\"height\",\"interface\":\"numeric\",\"width\":\"half\",\"type\":\"integer\",\"required\":true},{\"field\":\"quality\",\"interface\":\"slider\",\"width\":\"full\",\"type\":\"integer\",\"default\":80,\"options\":{\"min\":0,\"max\":100,\"step\":1},\"required\":true}]}', '0', NULL, '0', '0', '0', '0', '34', 'full', NULL, 'Defines how the thumbnail will be generated based on the requested params.', NULL), +('130', 'directus_settings', 'asset_whitelist_system', 'json', 'json', NULL, '0', NULL, '0', '1', '1', '1', '35', 'half', NULL, NULL, NULL), +('131', 'directus_settings', 'youtube_api_key', 'string', 'text-input', '{\"iconRight\":\"videocam\"}', '1', NULL, '0', '0', '0', '0', '36', 'full', NULL, 'Allows fetching more YouTube Embed info', NULL), +('132', 'directus_users', 'id', 'integer', 'primary-key', NULL, '1', NULL, '1', '0', '1', '0', '1', 'full', NULL, NULL, NULL), +('133', 'directus_users', 'status', 'status', 'status', '{\"status_mapping\":{\"draft\":{\"name\":\"Draft\",\"text_color\":\"white\",\"background_color\":\"light-gray\",\"listing_subdued\":false,\"listing_badge\":true,\"soft_delete\":false},\"invited\":{\"name\":\"Invited\",\"text_color\":\"white\",\"background_color\":\"light-gray\",\"listing_subdued\":false,\"listing_badge\":true,\"soft_delete\":false},\"active\":{\"name\":\"Active\",\"text_color\":\"white\",\"background_color\":\"success\",\"listing_subdued\":false,\"listing_badge\":false,\"soft_delete\":false},\"suspended\":{\"name\":\"Suspended\",\"text_color\":\"white\",\"background_color\":\"light-gray\",\"listing_subdued\":false,\"listing_badge\":true,\"soft_delete\":false},\"deleted\":{\"name\":\"Deleted\",\"text_color\":\"white\",\"background_color\":\"danger\",\"listing_subdued\":false,\"listing_badge\":true,\"soft_delete\":true}}}', '1', NULL, '1', '0', '0', '0', '2', 'full', NULL, NULL, NULL), +('134', 'directus_users', 'first_name', 'string', 'text-input', '{\"iconRight\":\"account_circle\"}', '1', NULL, '1', '0', '0', '0', '3', 'half', NULL, NULL, NULL), +('135', 'directus_users', 'last_name', 'string', 'text-input', '{\"iconRight\":\"account_circle\"}', '1', NULL, '1', '0', '0', '0', '4', 'half', NULL, NULL, NULL), +('136', 'directus_users', 'email', 'string', 'text-input', '{\"iconRight\":\"alternate_email\"}', '1', '$email', '1', '0', '0', '0', '5', 'half', NULL, NULL, NULL), +('137', 'directus_users', 'email_notifications', 'boolean', 'toggle', NULL, '1', NULL, '0', '0', '0', '0', '6', 'half', NULL, NULL, NULL), +('138', 'directus_users', 'password', 'hash', 'password', NULL, '1', NULL, '1', '0', '0', '0', '7', 'half', NULL, NULL, NULL), +('139', 'directus_users', 'role', 'm2o', 'user-roles', NULL, '1', NULL, '1', '0', '0', '0', '8', 'half', NULL, NULL, NULL), +('140', 'directus_users', 'company', 'string', 'text-input', '{\"iconRight\":\"location_city\"}', '0', NULL, '0', '0', '0', '0', '9', 'half', NULL, NULL, NULL), +('141', 'directus_users', 'title', 'string', 'text-input', '{\"iconRight\":\"text_fields\"}', '0', NULL, '0', '0', '0', '0', '10', 'half', NULL, NULL, NULL), +('142', 'directus_users', 'timezone', 'string', 'dropdown', '{\"choices\":{\"Pacific\\/Midway\":\"(UTC-11:00) Midway Island\",\"Pacific\\/Samoa\":\"(UTC-11:00) Samoa\",\"Pacific\\/Honolulu\":\"(UTC-10:00) Hawaii\",\"US\\/Alaska\":\"(UTC-09:00) Alaska\",\"America\\/Los_Angeles\":\"(UTC-08:00) Pacific Time (US & Canada)\",\"America\\/Tijuana\":\"(UTC-08:00) Tijuana\",\"US\\/Arizona\":\"(UTC-07:00) Arizona\",\"America\\/Chihuahua\":\"(UTC-07:00) Chihuahua\",\"America\\/Mexico\\/La_Paz\":\"(UTC-07:00) La Paz\",\"America\\/Mazatlan\":\"(UTC-07:00) Mazatlan\",\"US\\/Mountain\":\"(UTC-07:00) Mountain Time (US & Canada)\",\"America\\/Managua\":\"(UTC-06:00) Central America\",\"US\\/Central\":\"(UTC-06:00) Central Time (US & Canada)\",\"America\\/Guadalajara\":\"(UTC-06:00) Guadalajara\",\"America\\/Mexico_City\":\"(UTC-06:00) Mexico City\",\"America\\/Monterrey\":\"(UTC-06:00) Monterrey\",\"Canada\\/Saskatchewan\":\"(UTC-06:00) Saskatchewan\",\"America\\/Bogota\":\"(UTC-05:00) Bogota\",\"US\\/Eastern\":\"(UTC-05:00) Eastern Time (US & Canada)\",\"US\\/East-Indiana\":\"(UTC-05:00) Indiana (East)\",\"America\\/Lima\":\"(UTC-05:00) Lima\",\"America\\/Quito\":\"(UTC-05:00) Quito\",\"Canada\\/Atlantic\":\"(UTC-04:00) Atlantic Time (Canada)\",\"America\\/New_York\":\"(UTC-04:00) New York\",\"America\\/Caracas\":\"(UTC-04:30) Caracas\",\"America\\/La_Paz\":\"(UTC-04:00) La Paz\",\"America\\/Santiago\":\"(UTC-04:00) Santiago\",\"America\\/Santo_Domingo\":\"(UTC-04:00) Santo Domingo\",\"Canada\\/Newfoundland\":\"(UTC-03:30) Newfoundland\",\"America\\/Sao_Paulo\":\"(UTC-03:00) Brasilia\",\"America\\/Argentina\\/Buenos_Aires\":\"(UTC-03:00) Buenos Aires\",\"America\\/Argentina\\/GeorgeTown\":\"(UTC-03:00) Georgetown\",\"America\\/Godthab\":\"(UTC-03:00) Greenland\",\"America\\/Noronha\":\"(UTC-02:00) Mid-Atlantic\",\"Atlantic\\/Azores\":\"(UTC-01:00) Azores\",\"Atlantic\\/Cape_Verde\":\"(UTC-01:00) Cape Verde Is.\",\"Africa\\/Casablanca\":\"(UTC+00:00) Casablanca\",\"Europe\\/Edinburgh\":\"(UTC+00:00) Edinburgh\",\"Etc\\/Greenwich\":\"(UTC+00:00) Greenwich Mean Time : Dublin\",\"Europe\\/Lisbon\":\"(UTC+00:00) Lisbon\",\"Europe\\/London\":\"(UTC+00:00) London\",\"Africa\\/Monrovia\":\"(UTC+00:00) Monrovia\",\"UTC\":\"(UTC+00:00) UTC\",\"Europe\\/Amsterdam\":\"(UTC+01:00) Amsterdam\",\"Europe\\/Belgrade\":\"(UTC+01:00) Belgrade\",\"Europe\\/Berlin\":\"(UTC+01:00) Berlin\",\"Europe\\/Bern\":\"(UTC+01:00) Bern\",\"Europe\\/Bratislava\":\"(UTC+01:00) Bratislava\",\"Europe\\/Brussels\":\"(UTC+01:00) Brussels\",\"Europe\\/Budapest\":\"(UTC+01:00) Budapest\",\"Europe\\/Copenhagen\":\"(UTC+01:00) Copenhagen\",\"Europe\\/Ljubljana\":\"(UTC+01:00) Ljubljana\",\"Europe\\/Madrid\":\"(UTC+01:00) Madrid\",\"Europe\\/Paris\":\"(UTC+01:00) Paris\",\"Europe\\/Prague\":\"(UTC+01:00) Prague\",\"Europe\\/Rome\":\"(UTC+01:00) Rome\",\"Europe\\/Sarajevo\":\"(UTC+01:00) Sarajevo\",\"Europe\\/Skopje\":\"(UTC+01:00) Skopje\",\"Europe\\/Stockholm\":\"(UTC+01:00) Stockholm\",\"Europe\\/Vienna\":\"(UTC+01:00) Vienna\",\"Europe\\/Warsaw\":\"(UTC+01:00) Warsaw\",\"Africa\\/Lagos\":\"(UTC+01:00) West Central Africa\",\"Europe\\/Zagreb\":\"(UTC+01:00) Zagreb\",\"Europe\\/Athens\":\"(UTC+02:00) Athens\",\"Europe\\/Bucharest\":\"(UTC+02:00) Bucharest\",\"Africa\\/Cairo\":\"(UTC+02:00) Cairo\",\"Africa\\/Harare\":\"(UTC+02:00) Harare\",\"Europe\\/Helsinki\":\"(UTC+02:00) Helsinki\",\"Europe\\/Istanbul\":\"(UTC+02:00) Istanbul\",\"Asia\\/Jerusalem\":\"(UTC+02:00) Jerusalem\",\"Europe\\/Kyiv\":\"(UTC+02:00) Kyiv\",\"Africa\\/Johannesburg\":\"(UTC+02:00) Pretoria\",\"Europe\\/Riga\":\"(UTC+02:00) Riga\",\"Europe\\/Sofia\":\"(UTC+02:00) Sofia\",\"Europe\\/Tallinn\":\"(UTC+02:00) Tallinn\",\"Europe\\/Vilnius\":\"(UTC+02:00) Vilnius\",\"Asia\\/Baghdad\":\"(UTC+03:00) Baghdad\",\"Asia\\/Kuwait\":\"(UTC+03:00) Kuwait\",\"Europe\\/Minsk\":\"(UTC+03:00) Minsk\",\"Africa\\/Nairobi\":\"(UTC+03:00) Nairobi\",\"Asia\\/Riyadh\":\"(UTC+03:00) Riyadh\",\"Europe\\/Volgograd\":\"(UTC+03:00) Volgograd\",\"Asia\\/Tehran\":\"(UTC+03:30) Tehran\",\"Asia\\/Abu_Dhabi\":\"(UTC+04:00) Abu Dhabi\",\"Asia\\/Baku\":\"(UTC+04:00) Baku\",\"Europe\\/Moscow\":\"(UTC+04:00) Moscow\",\"Asia\\/Muscat\":\"(UTC+04:00) Muscat\",\"Europe\\/St_Petersburg\":\"(UTC+04:00) St. Petersburg\",\"Asia\\/Tbilisi\":\"(UTC+04:00) Tbilisi\",\"Asia\\/Yerevan\":\"(UTC+04:00) Yerevan\",\"Asia\\/Kabul\":\"(UTC+04:30) Kabul\",\"Asia\\/Islamabad\":\"(UTC+05:00) Islamabad\",\"Asia\\/Karachi\":\"(UTC+05:00) Karachi\",\"Asia\\/Tashkent\":\"(UTC+05:00) Tashkent\",\"Asia\\/Calcutta\":\"(UTC+05:30) Chennai\",\"Asia\\/Kolkata\":\"(UTC+05:30) Kolkata\",\"Asia\\/Mumbai\":\"(UTC+05:30) Mumbai\",\"Asia\\/New_Delhi\":\"(UTC+05:30) New Delhi\",\"Asia\\/Sri_Jayawardenepura\":\"(UTC+05:30) Sri Jayawardenepura\",\"Asia\\/Katmandu\":\"(UTC+05:45) Kathmandu\",\"Asia\\/Almaty\":\"(UTC+06:00) Almaty\",\"Asia\\/Astana\":\"(UTC+06:00) Astana\",\"Asia\\/Dhaka\":\"(UTC+06:00) Dhaka\",\"Asia\\/Yekaterinburg\":\"(UTC+06:00) Ekaterinburg\",\"Asia\\/Rangoon\":\"(UTC+06:30) Rangoon\",\"Asia\\/Bangkok\":\"(UTC+07:00) Bangkok\",\"Asia\\/Hanoi\":\"(UTC+07:00) Hanoi\",\"Asia\\/Jakarta\":\"(UTC+07:00) Jakarta\",\"Asia\\/Novosibirsk\":\"(UTC+07:00) Novosibirsk\",\"Asia\\/Beijing\":\"(UTC+08:00) Beijing\",\"Asia\\/Chongqing\":\"(UTC+08:00) Chongqing\",\"Asia\\/Hong_Kong\":\"(UTC+08:00) Hong Kong\",\"Asia\\/Krasnoyarsk\":\"(UTC+08:00) Krasnoyarsk\",\"Asia\\/Kuala_Lumpur\":\"(UTC+08:00) Kuala Lumpur\",\"Australia\\/Perth\":\"(UTC+08:00) Perth\",\"Asia\\/Singapore\":\"(UTC+08:00) Singapore\",\"Asia\\/Taipei\":\"(UTC+08:00) Taipei\",\"Asia\\/Ulan_Bator\":\"(UTC+08:00) Ulaan Bataar\",\"Asia\\/Urumqi\":\"(UTC+08:00) Urumqi\",\"Asia\\/Irkutsk\":\"(UTC+09:00) Irkutsk\",\"Asia\\/Osaka\":\"(UTC+09:00) Osaka\",\"Asia\\/Sapporo\":\"(UTC+09:00) Sapporo\",\"Asia\\/Seoul\":\"(UTC+09:00) Seoul\",\"Asia\\/Tokyo\":\"(UTC+09:00) Tokyo\",\"Australia\\/Adelaide\":\"(UTC+09:30) Adelaide\",\"Australia\\/Darwin\":\"(UTC+09:30) Darwin\",\"Australia\\/Brisbane\":\"(UTC+10:00) Brisbane\",\"Australia\\/Canberra\":\"(UTC+10:00) Canberra\",\"Pacific\\/Guam\":\"(UTC+10:00) Guam\",\"Australia\\/Hobart\":\"(UTC+10:00) Hobart\",\"Australia\\/Melbourne\":\"(UTC+10:00) Melbourne\",\"Pacific\\/Port_Moresby\":\"(UTC+10:00) Port Moresby\",\"Australia\\/Sydney\":\"(UTC+10:00) Sydney\",\"Asia\\/Yakutsk\":\"(UTC+10:00) Yakutsk\",\"Asia\\/Vladivostok\":\"(UTC+11:00) Vladivostok\",\"Pacific\\/Auckland\":\"(UTC+12:00) Auckland\",\"Pacific\\/Fiji\":\"(UTC+12:00) Fiji\",\"Pacific\\/Kwajalein\":\"(UTC+12:00) International Date Line West\",\"Asia\\/Kamchatka\":\"(UTC+12:00) Kamchatka\",\"Asia\\/Magadan\":\"(UTC+12:00) Magadan\",\"Pacific\\/Marshall_Is\":\"(UTC+12:00) Marshall Is.\",\"Asia\\/New_Caledonia\":\"(UTC+12:00) New Caledonia\",\"Asia\\/Solomon_Is\":\"(UTC+12:00) Solomon Is.\",\"Pacific\\/Wellington\":\"(UTC+12:00) Wellington\",\"Pacific\\/Tongatapu\":\"(UTC+13:00) Nuku\'alofa\"},\"placeholder\":\"Choose a timezone...\"}', '1', NULL, '1', '0', '0', '0', '11', 'half', NULL, NULL, NULL), +('143', 'directus_users', 'locale', 'string', 'language', '{\"limit\":true}', '1', NULL, '0', '0', '0', '0', '12', 'half', NULL, NULL, NULL), +('144', 'directus_users', 'avatar', 'file', 'file', NULL, '1', NULL, '0', '0', '0', '0', '13', 'full', NULL, NULL, NULL), +('145', 'directus_users', 'theme', 'string', 'radio-buttons', '{\"format\":true,\"choices\":{\"auto\":\"Auto\",\"light\":\"Light\",\"dark\":\"Dark\"}}', '1', NULL, '0', '0', '0', '0', '14', 'full', NULL, NULL, NULL), +('146', 'directus_users', '2fa_secret', 'string', '2fa-secret', NULL, '1', NULL, '0', '1', '0', '0', '15', 'full', NULL, NULL, NULL), +('147', 'directus_users', 'locale_options', 'json', 'json', NULL, '1', NULL, '0', '0', '1', '1', '16', 'full', NULL, NULL, NULL), +('148', 'directus_users', 'token', 'string', 'text-input', NULL, '1', NULL, '0', '0', '1', '1', '17', 'full', NULL, NULL, NULL), +('149', 'directus_users', 'last_access_on', 'datetime', 'datetime', NULL, '1', NULL, '0', '1', '1', '0', '18', 'full', NULL, NULL, NULL), +('150', 'directus_users', 'last_page', 'string', 'text-input', NULL, '1', NULL, '0', '1', '1', '1', '19', 'full', NULL, NULL, NULL), +('151', 'directus_users', 'external_id', 'string', 'text-input', NULL, '1', NULL, '0', '1', '20', '0', NULL, 'full', NULL, NULL, NULL), +('152', 'directus_user_sessions', 'id', 'integer', 'primary-key', NULL, '1', NULL, '1', '0', '1', '0', NULL, 'full', NULL, NULL, NULL), +('153', 'directus_user_sessions', 'user', 'user', 'user', NULL, '0', NULL, '1', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('154', 'directus_user_sessions', 'token_type', 'string', 'text-input', NULL, '0', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('155', 'directus_user_sessions', 'token', 'string', 'text-input', NULL, '0', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('156', 'directus_user_sessions', 'ip_address', 'string', 'text-input', NULL, '0', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('157', 'directus_user_sessions', 'user_agent', 'string', 'text-input', NULL, '0', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('158', 'directus_user_sessions', 'created_on', 'datetime', 'datetime', NULL, '0', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('159', 'directus_user_sessions', 'token_expired_at', 'datetime', 'datetime', NULL, '0', NULL, '0', '0', '0', '0', NULL, 'full', NULL, NULL, NULL), +('160', 'directus_webhooks', 'id', 'integer', 'primary-key', NULL, '1', NULL, '1', '0', '1', '0', NULL, 'full', NULL, NULL, NULL), +('161', 'directus_webhooks', 'status', 'status', 'status', '{\"status_mapping\":{\"active\":{\"name\":\"Active\",\"value\":\"active\",\"text_color\":\"white\",\"background_color\":\"green\",\"browse_subdued\":false,\"browse_badge\":true,\"soft_delete\":false,\"published\":true},\"inactive\":{\"name\":\"Inactive\",\"value\":\"inactive\",\"text_color\":\"white\",\"background_color\":\"blue-grey\",\"browse_subdued\":true,\"browse_badge\":true,\"soft_delete\":false,\"published\":false}}}', '1', NULL, '0', '0', '0', '0', '1', 'full', NULL, NULL, NULL), +('162', 'directus_webhooks', 'http_action', 'string', 'dropdown', '{\"choices\":{\"get\":\"GET\",\"post\":\"POST\"}}', '1', NULL, '1', '0', '0', '0', '2', 'half-space', NULL, NULL, NULL), +('163', 'directus_webhooks', 'url', 'string', 'text-input', '{\"placeholder\":\"https:\\/\\/example.com\",\"iconRight\":\"link\"}', '1', NULL, '1', '0', '0', '0', '3', 'full', NULL, '', NULL), +('164', 'directus_webhooks', 'collection', 'string', 'collections', NULL, '1', NULL, '1', '0', '0', '0', '4', 'half', NULL, '', NULL), +('165', 'directus_webhooks', 'directus_action', 'string', 'dropdown', '{\"choices\":{\"item.create:after\":\"Create\",\"item.update:after\":\"Update\",\"item.delete:after\":\"Delete\"}}', '1', NULL, '1', '0', '0', '0', '5', 'half', NULL, '', NULL), +('166', 'directus_webhooks', 'info', 'alias', 'divider', '{\"style\":\"medium\",\"title\":\"How Webhooks Work\",\"hr\":true,\"margin\":false,\"description\":\"When the selected action occurs for the selected collection, Directus will send an HTTP request to the above URL.\"}', '1', NULL, '0', '0', '0', '1', '6', 'full', NULL, NULL, NULL); + +INSERT INTO `directus_migrations` (`version`, `migration_name`, `start_time`, `end_time`, `breakpoint`) VALUES ('20180220023123', 'CreateFieldsTable', '2019-12-05 09:36:20', '2019-12-05 09:36:20', '0'), +('20180220023138', 'CreateActivityTable', '2019-12-05 09:36:20', '2019-12-05 09:36:20', '0'), +('20180220023152', 'CreateCollectionsPresetsTable', '2019-12-05 09:36:20', '2019-12-05 09:36:20', '0'), +('20180220023157', 'CreateCollectionsTable', '2019-12-05 09:36:20', '2019-12-05 09:36:20', '0'), +('20180220023208', 'CreateFilesTable', '2019-12-05 09:36:20', '2019-12-05 09:36:20', '0'), +('20180220023213', 'CreateFoldersTable', '2019-12-05 09:36:20', '2019-12-05 09:36:20', '0'), +('20180220023217', 'CreateRolesTable', '2019-12-05 09:36:20', '2019-12-05 09:36:20', '0'), +('20180220023226', 'CreatePermissionsTable', '2019-12-05 09:36:20', '2019-12-05 09:36:20', '0'), +('20180220023232', 'CreateRelationsTable', '2019-12-05 09:36:20', '2019-12-05 09:36:20', '0'), +('20180220023238', 'CreateRevisionsTable', '2019-12-05 09:36:20', '2019-12-05 09:36:20', '0'), +('20180220023243', 'CreateSettingsTable', '2019-12-05 09:36:20', '2019-12-05 09:36:20', '0'), +('20180220023248', 'CreateUsersTable', '2019-12-05 09:36:20', '2019-12-05 09:36:20', '0'), +('20181022175715', 'Upgrade070003', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20181102153600', 'TimezoneChoices', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20181105165224', 'Upgrade070006', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20181122195602', 'LocaleInterface', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20181123171520', 'RemoveScope', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20181210204720', 'AddTrustedProxiesSettingField', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20181222023800', 'AddProjectUrlSettingField', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20181227042755', 'IncreaseUsersLastPageLength', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190104155309', 'AddUsersEmailValidation', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190111193724', 'AddAppUrlSettingField', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190111212736', 'AddMissingSettingsField', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190118181424', 'AddRolesUsersField', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190130215921', 'AddFilesChecksumField', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190318173400', 'AddNavOverride', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190412122400', 'SetPasswordTypeToHash', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190412123000', 'UseFileInterface', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190412145800', 'SetO2mOptionsRoles', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190415125300', 'SetWidth', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190419154400', 'SettingsFields', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190419161200', 'CollectionNotes', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190419173000', 'MiscRequiredFields', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190422131600', 'UseJson', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190426156200', 'SetNullableValueFieldSettingsTable', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190520094300', 'UseTimeline', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190614103321', 'AddUsers2faSecretField', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190618190024', 'AddEnforce2faRoleField', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190626114228', 'AddFileNamingInSetting', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190702092323', 'AddLoginAttemptsAllowedSetting', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190704063752', 'UpdateIstanbulTimezone', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190708095522', 'AddFileExtensionSetting', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190709053719', 'AddFileMaxSizeSetting', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190722110232', 'PasswordValidationSettingField', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190726064001', 'UpdateNoteForDefaultLimit', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190819070856', 'UpdateDirectusFieldsField', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20190912072543', 'CreateUserSessions', '2019-12-05 09:36:20', '2019-12-05 09:36:20', '0'), +('20190917090849', 'CreateWebHooks', '2019-12-05 09:36:20', '2019-12-05 09:36:20', '0'), +('20191123053252', 'UpdateDirectusFilesField', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20191129123300', 'UseNewWysiwyg', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20191202164200', 'AddUserSessions', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20191202164201', 'AddWebhooks', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20191202164202', 'RemoveActivitySeen', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20191202164203', 'ConvertUserRoles', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20191202164204', 'UpdateDirectusUsers', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20191202164205', 'UpdateDirectusSettings', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20191202164206', 'UpdateDirectusFiles', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20191202164207', 'UpdateDirectusRoles', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20191202164208', 'ResetDirectusRelations', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20191202164209', 'ResetDirectusFields', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20191203105400', 'AddHashFiledownloadIfEmpty', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'), +('20191204151300', 'UseCorrectWysiwyg', '2019-12-05 14:36:20', '2019-12-05 14:36:20', '0'); + +INSERT INTO `directus_relations` (`id`, `collection_many`, `field_many`, `collection_one`, `field_one`, `junction_field`) VALUES ('1', 'directus_activity', 'action_by', 'directus_users', NULL, NULL), +('2', 'directus_collections_presets', 'user', 'directus_users', NULL, NULL), +('3', 'directus_collections_presets', 'group', 'directus_groups', NULL, NULL), +('4', 'directus_fields', 'collection', 'directus_collections', 'fields', NULL), +('5', 'directus_files', 'uploaded_by', 'directus_users', NULL, NULL), +('6', 'directus_files', 'folder', 'directus_folders', NULL, NULL), +('7', 'directus_folders', 'parent_folder', 'directus_folders', NULL, NULL), +('8', 'directus_permissions', 'group', 'directus_groups', NULL, NULL), +('9', 'directus_revisions', 'activity', 'directus_activity', NULL, NULL), +('10', 'directus_users', 'role', 'directus_roles', 'users', NULL), +('11', 'directus_users', 'avatar', 'directus_files', NULL, NULL); + +INSERT INTO `directus_roles` (`id`, `name`, `description`, `ip_whitelist`, `external_id`, `module_listing`, `collection_listing`, `enforce_2fa`) VALUES ('1', 'Administrator', 'Admins have access to all managed data within the system by default', NULL, NULL, NULL, NULL, '0'), +('2', 'Public', 'Controls what API data is publicly available without authenticating', NULL, NULL, NULL, NULL, '0'); + +INSERT INTO `directus_settings` (`id`, `key`, `value`) VALUES ('1', 'project_url', ''), +('2', 'project_logo', ''), +('3', 'project_color', '#13181a'), +('4', 'project_foreground', ''), +('5', 'project_background', ''), +('6', 'default_locale', 'en-US'), +('7', 'telemetry', '1'), +('8', 'default_limit', '200'), +('9', 'sort_null_last', '1'), +('10', 'password_policy', ''), +('11', 'auto_sign_out', '10080'), +('12', 'login_attempts_allowed', '10'), +('13', 'trusted_proxies', ''), +('14', 'file_max_size', '100MB'), +('15', 'file_mimetype_whitelist', ''), +('16', 'file_naming', 'uuid'), +('17', 'youtube_api_key', ''), +('18', 'asset_whitelist', '[{\"key\":\"thumbnail\",\"width\":200,\"height\":200,\"fit\":\"contain\",\"quality\":80}]'), +('19', 'asset_whitelist_system', '[{\"key\":\"card\",\"width\":200,\"height\":200,\"fit\":\"crop\",\"quality\":80},{\"key\":\"avatar\",\"width\":100,\"height\":100,\"fit\":\"crop\",\"quality\":80}]'); + +INSERT INTO `directus_users` (`id`, `status`, `role`, `first_name`, `last_name`, `email`, `password`, `token`, `timezone`, `locale`, `locale_options`, `avatar`, `company`, `title`, `email_notifications`, `last_access_on`, `last_page`, `external_id`, `theme`, `2fa_secret`) VALUES ('1', 'active', '1', 'Admin', 'User', 'admin@example.com', '$2y$10$CifjZIWHTjZeQktU53JK6Of1LmulMj9D02XrC3kca/qXyWbmZTxvO', 'admin', 'America/New_York', 'en-US', NULL, NULL, NULL, NULL, '1', '2019-12-05 09:39:19', NULL, NULL, 'auto', NULL); + + + + +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; \ No newline at end of file diff --git a/src/web.php b/src/web.php index 720a03dec6..20895ac0d9 100644 --- a/src/web.php +++ b/src/web.php @@ -71,7 +71,7 @@ $errorReporting = E_ALL; $displayErrors = 1; -if ($app->getConfig()->get('app.env', 'development') === 'production') { +if ($app->getConfig()->get('env', 'development') === 'production') { $displayErrors = $errorReporting = 0; } @@ -135,7 +135,10 @@ ->add($middleware['table_gateway']); +$app->get('/{project}/assets/{id}', \Directus\Api\Routes\Assets::class); + $app->group('/{project}', function () use ($middleware) { + $this->get('/', \Directus\Api\Routes\ProjectHome::class) ->add($middleware['auth_user']) ->add($middleware['rate_limit_user']) @@ -161,6 +164,10 @@ ->add($middleware['rate_limit_user']) ->add($middleware['auth']) ->add($middleware['table_gateway']); + $this->group('/folders', \Directus\Api\Routes\Folders::class) + ->add($middleware['rate_limit_user']) + ->add($middleware['auth']) + ->add($middleware['table_gateway']); $this->group('/items', \Directus\Api\Routes\Items::class) ->add($middleware['rate_limit_user']) ->add($middleware['auth']) @@ -227,8 +234,8 @@ ->add($middleware['auth']) ->add($middleware['table_gateway']); - $this->group('/pages', function () { - $endpointsList = \Directus\get_custom_endpoints('public/extensions/core/pages', true); + $this->group('/modules', function () { + $endpointsList = \Directus\get_custom_endpoints('public/extensions/core/modules', true); foreach ($endpointsList as $name => $endpoints) { \Directus\create_group_route_from_array($this, $name, $endpoints); @@ -268,13 +275,13 @@ ->add($middleware['auth']) ->add($middleware['table_gateway']) ->add($middleware['database_migration']); -$app->group('/pages', \Directus\Api\Routes\Pages::class) +$app->group('/modules', \Directus\Api\Routes\Modules::class) ->add($middleware['rate_limit_user']) ->add($middleware['auth_user']) ->add($middleware['auth']) ->add($middleware['table_gateway']) ->add($middleware['database_migration']); - + $app->group('/server', \Directus\Api\Routes\Server::class); $app->group('/types', \Directus\Api\Routes\Types::class) ->add($middleware['rate_limit_user'])