{"id":24452918,"url":"https://github.com/extensionengine/tailor","last_synced_at":"2026-01-26T18:55:28.055Z","repository":{"id":38845326,"uuid":"124099324","full_name":"ExtensionEngine/tailor","owner":"ExtensionEngine","description":"Content authoring platform","archived":false,"fork":false,"pushed_at":"2025-02-07T04:57:48.000Z","size":64761,"stargazers_count":32,"open_issues_count":107,"forks_count":10,"subscribers_count":11,"default_branch":"develop","last_synced_at":"2025-03-07T22:04:46.910Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Vue","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ExtensionEngine.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-03-06T15:35:24.000Z","updated_at":"2025-01-19T21:21:28.000Z","dependencies_parsed_at":"2023-02-14T00:31:40.451Z","dependency_job_id":"7d137422-570a-4205-a825-90e25c4b6e70","html_url":"https://github.com/ExtensionEngine/tailor","commit_stats":{"total_commits":4849,"total_committers":38,"mean_commits":"127.60526315789474","dds":0.6879769024541142,"last_synced_commit":"df8a20cad1ca103aff9c6ce1f9d7621436fd6047"},"previous_names":[],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ExtensionEngine%2Ftailor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ExtensionEngine%2Ftailor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ExtensionEngine%2Ftailor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ExtensionEngine%2Ftailor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ExtensionEngine","download_url":"https://codeload.github.com/ExtensionEngine/tailor/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243534383,"owners_count":20306502,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-21T01:17:39.303Z","updated_at":"2026-01-26T18:55:27.987Z","avatar_url":"https://github.com/ExtensionEngine.png","language":"Vue","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n  \u003cimg width=\"180\" src=\"./client/assets/img/default-logo-compact.svg\"\u003e\n\u003c/div\u003e\n\n# Tailor CMS\n\n[![CircleCI build\nstatus](https://badgen.net/circleci/github/ExtensionEngine/tailor/develop?style=svg)](https://circleci.com/gh/ExtensionEngine/tailor)\n[![Codacy Badge](https://app.codacy.com/project/badge/Grade/f3eab80316244b7b959b7bbf3d7c3ace)](https://www.codacy.com/gh/ExtensionEngine/tailor/dashboard?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=ExtensionEngine/tailor\u0026amp;utm_campaign=Badge_Grade)\n[![Known\nVulnerabilities](https://badgen.net/snyk/ExtensionEngine/tailor/develop)](https://snyk.io/test/github/ExtensionEngine/tailor)\n[![GitHub package\nversion](https://badgen.net/github/release/ExtensionEngine/tailor)](https://github.com/ExtensionEngine/tailor/releases)\n[![GitHub\nlicense](https://badgen.net/github/license/ExtensionEngine/tailor)](https://github.com/ExtensionEngine/tailor/blob/develop/LICENSE)\n[![js @extensionengine\nstyle](https://badgen.net/badge/code%20style/@extensionengine/black)](https://github.com/ExtensionEngine/eslint-config)\n[![style @extensionengine\nstyle](https://badgen.net/badge/stylelint/@extensionengine/black)](https://github.com/ExtensionEngine/stylelint-config)\n[![Open Source\nLove](https://badgen.net/badge/Open%20Source/%E2%9D%A4/3eaf8e)](https://github.com/ellerbrock/open-source-badge/)\n\nConfigurable headless CMS for complex content structures.\n\n## Dependencies\n\n- Node.js (\u003e= 16.16.0)\n- npm (\u003e= 8.11.0)\n- PostgreSQL (\u003e= 9.4)\n\n## Installation\n\n### Prerequisites\n\n- Node.js \u0026 npm: https://nodejs.org/en/download/\n- PostgreSQL: https://www.postgresql.org/download/\n- Clone this repo\n\n### Setup\n\n- Run `npm install` in the repo directory\n- Create a database in PostgreSQL\n- Application is configured via environment variables contained in a file named\n  `.env`. Use the `.env.example` file as a template: `cp .env.example .env` and\n  enter configuration details.\n- Initialize database by running `npm run db migrate`\n- To enable demo repository schema configuration copy `tailor.config.js.example`\n  into `tailor.config.js.`. For more details about the custom schema configuration\n  please refer [to this guide](https://extensionengine.github.io/tailor-docs/tailor/developer-guide/configuration.html).\n- Configure asset storage proxy by following the steps [in this guide](https://extensionengine.github.io/tailor-docs/tailor/developer-guide/storage-proxy.html)\n  based on your environment.\n- You can create admin user by running `npm run add:admin \u003cemail\u003e \u003cpassword\u003e`\n- App branding is configured via values set in a file named `.brandrc.js`. \n  Use the `.brandrc.js.example` file as a template: `cp\n  .brandrc.js.example .brandrc.js` and enter configuration details (Optional).\n\n## Launch\n\n### Development\n\n- Server: `npm run dev:server`\n- Client (webpack dev server): `npm run dev:client`\n\nThis project uses a monorepo setup. In order to contribute to [packages](./packages) following commands should be executed:\n- Run `npm run packages:setup` - initial setup, dependency installation, package linking, etc. This command should be executed only a single time. \n- Run `npm run packages:build` - build all packages. Run this command after altering the package's code.\n- Run `npm run packages:build --package=\u003cpackage-name\u003e` to build only specified package. For example: `npm run packages:build --package=core-components`.\n\n### Production\n\n- Bundle client by issuing `npm run build`\n- `npm run start`\n\n## Content repository structure\n\nRepository structure can be altered using tailor configuration file, which must be placed inside the root\ndirectory and named `tailor.config.js`.\n\nUse the `tailor.config.js.example` file as a template:\n\n```\n$ cp tailor.config.js.example tailor.config.js\n```\n\nand enter the configuration details. At the current time, it is not possible to override the filename or\nlocation of the configuration file.\n\nContent repository structures are defined using the following properties:\n\n### `SCHEMAS`\n\nAn array of Schema objects.\n\n#### Schema\n\n- **id** `String` - Schema identifier.\n- **name** `String` - Schema display name.\n- **workflowId** `String` - [Workflow](#workflow) identifier.\n- **meta** `Array\u003cMetadata\u003e` - An array of objects defining repository metadata.\n- **structure** `Array\u003cActivityConfig\u003e` - An array of objects which define\n  schema structure.\n- **contentContainers** `Array\u003cContentContainer\u003e` - Array of content container\n  configs.\n- **elementMeta** `Array\u003cElementMetaConfig\u003e` - An array of objects defining\n  content element metadata.\n\n#### ActivityConfig - Schema structure elements\n\nConfiguration for schema structure nodes (activities). Contains the following\nproperties:\n\n- **type** `String` - Const for marking activity type.\n- **rootLevel** `Boolean` - Used to define first level (root) activity types\n- **subLevels** `Array\u003cString\u003e` - An array of sub-types.\n- **label** `String` - Display label.\n- **color** `String` - Display color in hexadecimal notation.\n- **isTrackedInWorkflow** `Boolean` - Defines whether the workflow status will be tracked for this activity type.\n- **contentContainers** `Array\u003cString\u003e` - Array of content container types that\n  define which content containers can be added.\n- **hasExams** `Boolean` - Activity allows adding exam activities to it.\n- **exams** `Object` - Configuration for activity exams.\n- **relationships** `Array\u003cActivityRelationship\u003e` - Defines what relationships this\n  activity has to other activities.\n- **meta** `Array\u003cMetadata\u003e` - An array of objects defining activity metadata.\n\n#### ActivityRelationship\n\nDefines the structure of the activity relationship field.\n\n- **type** `String` - Defines the name of the relationship. The relationship\n  will be published under this value.\n- **label** `String` - Display label.\n- **placeholder** `String` - Display label for the select picker.\n- **multiple** `Boolean` - Defines if the relationship can have multiple\n  associations chosen. True by default.\n- **searchable** `Boolean` - Defines if the list of activities can be searched.\n  True by default.\n- **allowEmpty** `Boolean` - Defines if the member list can be empty. True by\n  default.\n- **allowCircularLinks** `Boolean` - Defines if a member of the relationship\n  instance can set the owner of that instance as a member of its own instance of\n  that relationship. Example, activity X sets activity Y as its prerequisite. If\n  `allowCircualLinks` is set to true then activity Y can set activity X as its\n  prerequisite. False by default.\n- **allowInsideLineage** `Boolean` - Defines if an ancestor or a descendant can\n  be a member of the relationship. False by default.\n- **allowedTypes** `Array\u003cString\u003e` - Defines activity types that can be associated in a relationship.\n\n#### Metadata\n\nDefines the structure of the activity metadata field.\n\n- **key** `String` - Unique key for the field.\n- **type** `String` - Type of the input component used on the client.\n- **label** `String` - Display label.\n- **placeholder** `String` - Input component placeholder.\n- **validate** `MetadataValidator` - Validator object.\n- **defaultValue** `*` - Default field value.\n\n#### MetadataValidator\n\nDefines validation rules on an activity metadata field.\n\n- **rules** `Object` - Contains the following properties:\n- max `Number` - Maximum character count.\n- required `Boolean` - Defines if the field is required.\n\n### `CONTENT_CONTAINERS`\n\nAn array of ContentContainer objects.\n\n#### ContentContainer\n\nConfiguration for content containers. Contains the following properties:\n\n- **type** `String` - `const-cased` string for marking `ContentContainer` type.\n- **templateId** `String` - `const-cased` string that defines which custom\n  `ContentContainer` is used to display this container. Needs to match the\n  `templateId` property of the desired custom `ContentContainer`. If not\n  specified the default `ContentContainer` is used to display this container.\n- **label** `String` - String used for referencing `ContentContainer` on the UI.\n- **multiple** `Boolean` - Defines if there can be multiple instances of the\n  `ContentContainer` inside a single `Activity`. False by default.\n- **types** `Array\u003cString\u003e` - An array of possible content element types that\n  can exist inside a `ContentContainer`. If not specified all types of elements\n  are allowed.\n- **displayHeading** `Boolean` - Defines if a heading is displayed on top of the\n  `ContentContainer`. False by default.\n- **layout** `Boolean` - Defines if elements inside a `ContentContainer`\n  instance can be placed two in a row. True by default.\n- **config** `Object` - Defines `ContentContainer` specific properties.\n- **required** `Boolean` - Defines if an instance of the `ContentContainer` is\n  created if non exist. True by default.\n- **publishedAs** `String` - Defines the name of the file under which the\n  container will be published. Defaults to `container`. The name of the\n  structure component used is the `kebab-cased` version of the `type` property.\n  (example: ABC_DEF -\u003e abc-def)\n\n#### ElementMetaConfig\n\nDefines the structure of an content element metadata.\n\n- **type** `String` - Type of content element (example: \"IMAGE\", \"HTML\").\n- **inputs** `Array\u003cElementMeta\u003e` - Defines what meta fields content element has.\n- **relationships** `Array\u003cElementRelationship\u003e` - Defines what relationship\n  metadata content element has (relationships with content elements from the same \n  or other activities in the repository).\n\n#### ElementRelationship\n\nDefines the structure of an content element relationship field.\n\n- **key** `String` - Defines the name of the relationship. The relationship\n  will be published under this value.\n- **label** `String` - Display label.\n- **placeholder** `String` - Label for relationship add button and modal title.\n- **multiple** `Boolean` - Defines if the relationship can have multiple\n  associations chosen. True by default.\n- **allowedTypes** `Array\u003cString\u003e` - Defines to what type of content elements\n   given content element can have relationship with (example: `['VIDEO']`).\n\n#### ElementMeta\n\nDefines what meta fields content element has.\n\n- **key** `String` - Unique key for the field.\n- **type** `String` - Type of the input component used on the client.\n- **label** `String` - Display label.\n- **description** `String` - Description of meta field.\n- **options** `Array\u003cObject\u003e` - Options for certain types of input component.\n  For example, for select component, options would be:\n  ```json\n  \"type\": \"SELECT\"\n  \"options\": [{\n      \"label\": \"First\",\n      \"value\": \"first\"\n    }, {\n      \"label\": \"Second\",\n      \"value\": \"second\"\n    }]\n  ```\n\n### `PREVIEW_URL`\n\nA string template that will be interpolated on the client using two route\nparams, `repositoryId` and `activityId`, into a preview URL for each activity.\nExample:\n`https://my.url.com/#/repository/{repositoryId}/activity/{activityId}/preview`\n\n## Workflows\n\nFor each schema, workflow can be defined to enable users to track and assign activities which are flagged for tracking. Each workflow is defined by a set of statuses that the activity can have.\nWorkflows are assigned to schemas through schema's `workflowId` option in [tailor configuration file](#content-repository-structure).\n\nWorkflows are configured with the following options in the [tailor configuration file](#content-repository-structure):\n\n### `WORKFLOWS`\n\nAn array of Workflow objects.\n\n#### Workflow\n\nDefines activity statuses for repository workflow. Workflow can be reused across multiple [schemas](#schema) by assigning the same workflow ID to schema's `workflowId` option.\n\n- **id** `String` - Workflow identifier.\n- **statuses** `Array\u003cActivityStatus\u003e` - An array of possible activity statuses.\n- **dueDateWarningThreshold** `Object` - Defines threshold (in days, weeks or months) relative to activity's due date, after which the warning of upcoming due date is displayed.\n\n#### ActivityStatus\n- **id** `String` - Activity status identifier.\n- **label** `String` - Display label.\n- **color** `String` - Display color.\n- **default** `Boolean` - Defines that the status is the default, which the activity has when it's created.\n\n## EXTENSIONS\n\nTailor supports creation of custom content elements and custom containers. These extensions\ncan have unique content and structure that the default content elements and\ncontainers do not support. The template for creating custom content elements\ncan be found [here](https://github.com/ExtensionEngine/tailor-element-template)\nwhile the template for creating custom containers can be found\n[here](https://github.com/ExtensionEngine/tailor-container-template).\n\n### Installing extensions\n\n1. copy (or git clone) extension files in `extensions/content-\u003celements or containers\u003e/\u003cmy-extension-name\u003e`\n2. create index.js file in `extensions/content-\u003celements or containers\u003e` directory\n3. in the file from the previous step add\n```javascript\nmodule.exports = ['my-extension-name'];\n```\n\nNote that `module.exports` is an array and you can add as many extensions and just include their folder names in this array to include all of them.\nAfter installation, the extension is ready for use and should be listed in `tailor.config.js` file.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fextensionengine%2Ftailor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fextensionengine%2Ftailor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fextensionengine%2Ftailor/lists"}