├── .gitignore
├── .postcssrc.js
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── README.md
├── babel.config.js
├── netlify.toml
├── package-lock.json
├── package.json
├── public
├── config.yml
└── index.html
├── src
├── SaleinaCMS.vue
├── assets
│ ├── icons
│ │ ├── add.svg
│ │ ├── collection.svg
│ │ ├── delete.svg
│ │ ├── media.svg
│ │ └── remove.svg
│ ├── logo.green.svg
│ └── logo.svg
├── components
│ ├── collection
│ │ ├── add.vue
│ │ ├── collection.vue
│ │ ├── edit-file-collection.vue
│ │ ├── edit-folder-collection.vue
│ │ ├── edit.vue
│ │ ├── file-collection.vue
│ │ └── folder-collection.vue
│ ├── inc
│ │ ├── button.vue
│ │ ├── index.js
│ │ ├── input.vue
│ │ ├── loading.vue
│ │ ├── media-preview.vue
│ │ └── tabs
│ │ │ ├── components
│ │ │ ├── Tab.vue
│ │ │ └── Tabs.vue
│ │ │ ├── expiringStorage.js
│ │ │ └── index.js
│ ├── login.vue
│ ├── main.vue
│ ├── media.vue
│ └── widgets
│ │ ├── boolean.vue
│ │ ├── date.vue
│ │ ├── datetime.vue
│ │ ├── file.vue
│ │ ├── hidden.vue
│ │ ├── image.vue
│ │ ├── index.js
│ │ ├── list.vue
│ │ ├── markdown.vue
│ │ ├── number.vue
│ │ ├── object.vue
│ │ ├── select.vue
│ │ ├── string.vue
│ │ └── text.vue
├── config.js
├── main.js
├── router.js
├── store
│ ├── actions.js
│ ├── backends
│ │ └── gitlab
│ │ │ ├── actions.js
│ │ │ ├── index.js
│ │ │ ├── media.actions.js
│ │ │ ├── media.mutations.js
│ │ │ └── mutations.js
│ ├── index.js
│ └── mutations.js
├── styles.css
└── utils
│ ├── lib.js
│ └── schema.js
├── vue.config.js
└── website
├── README.md
├── config.toml
├── content
├── _index.md
├── blog
│ └── webhooks.md
├── docs
│ ├── add-to-your-site.md
│ ├── backends.md
│ ├── boolean.md
│ ├── collection-types.md
│ ├── configuration-options.md
│ ├── date.md
│ ├── datetime.md
│ ├── file.md
│ ├── hidden.md
│ ├── image.md
│ ├── introduction.md
│ ├── list.md
│ ├── markdown.md
│ ├── number.md
│ ├── object.md
│ ├── select.md
│ ├── string.md
│ ├── text.md
│ ├── update-the-cms-version.md
│ └── widgets.md
└── support
│ └── _index.md
├── layouts
├── _default
│ ├── baseof.html
│ ├── li.html
│ ├── list.html
│ └── single.html
├── docs
│ └── single.html
├── index.html
└── section
│ └── support.html
└── static
├── _redirects
├── admin
├── config.yml
└── index.html
├── css
└── style.styl
└── images
└── logo.png
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 | website/static/css/*.css
5 |
6 | # local env files
7 | .env.local
8 | .env.*.local
9 |
10 | # Log files
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 |
15 | # Editor directories and files
16 | .idea
17 | .vscode
18 | *.suo
19 | *.ntvs*
20 | *.njsproj
21 | *.sln
22 | *.sw*
23 |
--------------------------------------------------------------------------------
/.postcssrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | autoprefixer: {}
4 | }
5 | }
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of experience,
9 | nationality, personal appearance, race, religion, or sexual identity and
10 | orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at admin@zede.solutions. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at [http://contributor-covenant.org/version/1/4][version]
72 |
73 | [homepage]: http://contributor-covenant.org
74 | [version]: http://contributor-covenant.org/version/1/4/
75 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # CONTRIBUTING
2 |
3 | Contributions are always welcome, no matter how large or small. Before contributing,
4 | please read the [code of conduct](CODE_OF_CONDUCT.md).
5 |
6 | ## Setup
7 |
8 | > Install node on your system: [Node](https://nodejs.org/en/download)
9 |
10 | ### Install dependencies
11 |
12 | > Only required on the first run, subsequent runs can use `npm run serve` to run the development server.
13 |
14 | ```sh
15 | $ git clone https://github.com/saleina/SaleinaCMS
16 | $ cd SaleinaCMS
17 | $ npm install
18 | ```
19 |
20 | ### Run locally
21 |
22 | ```sh
23 | $ npm run serve
24 | ```
25 |
26 | ## Pull Requests
27 |
28 | We actively welcome your pull requests!
29 |
30 | If you need help with Git or our workflow, please ask on [Telegram](tg://resolve?domain=saleinacmsdiscussions). We want your contributions even if you're just learning Git. Our maintainers are happy to help!
31 |
32 | Saleina CMS uses the [Forking Workflow](https://www.atlassian.com/git/tutorials/comparing-workflows#forking-workflow) + [Feature Branches](https://www.atlassian.com/git/tutorials/comparing-workflows#feature-branch-workflow). Additionally, PR's should be [rebased](https://www.atlassian.com/git/tutorials/merging-vs-rebasing) on master when opened, and again before merging.
33 |
34 | 1. Fork the repo.
35 | 2. Create a branch from `master`. If you're addressing a specific issue, prefix your branch name with the issue number.
36 | 3. If you've added code that should be tested, add tests.
37 | 4. If you've changed APIs, update the documentation.
38 | 5. PR's must be rebased before merge (feel free to ask for help).
39 | 6. PR should be reviewed by two maintainers prior to merging.
40 |
41 | ## License
42 |
43 | By contributing to Saleina CMS, you agree that your contributions will be licensed
44 | under its [MIT license](LICENSE).
45 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Zelalem Mekonen
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | build:
2 | cd website ; hugo --minify ; npm install ; ../node_modules/.bin/stylus --include ../node_modules/nib/nib ./public/css
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Saleina CMS
2 |
3 | [](https://www.jsdelivr.com/package/npm/saleina-cms)
4 |
5 | A CMS for static site generators. Give non-technical users a simple way to edit
6 | and add content to any site built with a static site generator.
7 |
8 | ## How it works
9 |
10 | Saleina CMS is a single-page app that you pull into the `/admin` part of your site.
11 |
12 | It presents a clean UI for editing content stored in a Git repository.
13 |
14 | You setup a YAML config to describe the content model of your site, and typically
15 | tweak the main layout of the CMS a bit to fit your own site.
16 |
17 | When a user navigates to `/admin` they'll be prompted to login, and once authenticated
18 | they'll be able to create new content or edit existing content.
19 |
20 | Read more about Saleina CMS [Introduction](https://saleinacms.org/docs/introduction/).
21 |
22 | # Installation and Configuration
23 |
24 | A Quick and easy install, that just requires you to create a single HTML file and a configuration file. All the CMS files are loaded from a CDN. To learn more about this installation method, refer to the [Docs](https://saleinacms.org/docs/introduction/)
25 |
26 | # Support Us
27 |
28 |
29 |
30 | ## Our Sponsors
31 |
32 |
33 | # Change Log
34 |
35 | This project adheres to [Semantic Versioning](http://semver.org/).
36 | Every release is documented on the Github [Releases](https://github.com/saleina/SaleinaCMS/releases) page.
37 |
38 | # License
39 |
40 | Saleina CMS is released under the [MIT License](LICENSE).
41 | Please make sure you understand its [implications and guarantees](https://writing.kemitchell.com/2016/09/21/MIT-License-Line-by-Line.html).
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/app'
4 | ]
5 | }
--------------------------------------------------------------------------------
/netlify.toml:
--------------------------------------------------------------------------------
1 | [build]
2 | command = "make build"
3 | "publish" = "website/public"
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saleina-cms",
3 | "version": "0.32.1",
4 | "author": {
5 | "name": "Zelalem Mekonen",
6 | "email": "zola@programmer.net"
7 | },
8 | "description": "Static site CMS with git as a backend",
9 | "license": "MIT",
10 | "scripts": {
11 | "serve": "vue-cli-service serve",
12 | "build": "vue-cli-service build"
13 | },
14 | "files": [
15 | "dist/saleina-cms.min.js"
16 | ],
17 | "dependencies": {
18 | "flatpickr": "^4.5.1",
19 | "gray-matter": "^4.0.1",
20 | "mustache": "^2.3.2",
21 | "pell": "^1.0.4",
22 | "showdown": "^1.8.6",
23 | "slugify": "^1.3.1",
24 | "toml-js": "0.0.8",
25 | "turndown": "^4.0.2",
26 | "validate.js": "^0.12.0",
27 | "vue": "^2.5.17",
28 | "vue-router": "^3.0.1",
29 | "vue-toasted": "^1.1.24",
30 | "vuex": "^3.0.1",
31 | "vuex-router-sync": "^5.0.0"
32 | },
33 | "devDependencies": {
34 | "@vue/cli-plugin-babel": "^3.0.0",
35 | "@vue/cli-service": "^3.0.0",
36 | "nib": "^1.1.2",
37 | "stylus": "^0.54.5",
38 | "vue-svg-loader": "^0.5.0",
39 | "vue-template-compiler": "^2.5.17"
40 | },
41 | "browserslist": [
42 | "> 1%",
43 | "last 2 versions",
44 | "not ie <= 8"
45 | ]
46 | }
47 |
--------------------------------------------------------------------------------
/public/config.yml:
--------------------------------------------------------------------------------
1 | backend:
2 | name: gitlab
3 | repo: saleina/saleinacms
4 | client_id: 83cb793b4a6a96883c9502178b2c58151310851f9e44263f9ff19c3ad0da7357
5 |
6 | media_folder: /website/static/images/uploads/
7 |
8 | public_folder: /images/uploads/
9 |
10 | collections:
11 | - label: Blog
12 | name: blog
13 | folder: website/content/blog
14 | delete: true
15 | tabs:
16 | - label: Basic
17 | fields:
18 | - {label: Draft, name: draft, widget: boolean, default: true}
19 | - {label: Title, name: title, widget: string}
20 | - {label: Publish Date, name: date, widget: datetime, format: "Z"}
21 | - label: Body
22 | fields:
23 | - {label: Body, name: body, widget: markdown}
24 |
25 | - label: Docs
26 | name: docs
27 | create: false
28 | folder: website/content/docs
29 | tabs:
30 | - label: Basic
31 | fields:
32 | - {label: Title, name: title, widget: string}
33 | - label: Group
34 | name: group
35 | widget: select
36 | options:
37 | - {label: Start, value: start}
38 | - {label: Widgets, value: widgets}
39 | - {label: Reference, value: reference}
40 | - {label: Weight, name: weight, widget: number, required: false}
41 | - label: Body
42 | fields:
43 | - {label: Body, name: body, widget: markdown}
44 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Post type identifier, used in routes. Must be unique.
144 |
145 |
146 |
label
147 |
What the post type will be called in the admin UI.
148 |
149 |
150 |
folder
151 |
Where files of this type are stored, relative to the repo root.
152 |
153 |
154 |
slug
155 |
Template for filenames. {{year}}, {{month}}, and {{day}} will pull from the post's date field or save date. {{slug}} is a url-safe version of the post's title. Default is simply {{slug}}.
156 |
157 |
158 |
tabs
159 |
tabs listed here are shown as tabs in the cotent editor. Each tab contains the following properties:
160 |
161 |
label: Tab label in the editor UI.
162 |
fields: Fields listed here are shown as fields in the content editor, then saved as front matter at the beginning of the document (except for body, which follows the front matter). Each field contains the following properties:
163 |
164 |
label: Field label in the editor UI.
165 |
name: Field name in the document front matter.
166 |
widget: Determines UI style and value data type (details below).
167 |
default (optional): Sets a default value for the field.
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 | As described above, the `widget` property specifies a built-in or custom UI widget for a given field. When a content editor enters a value into a widget, that value will be saved in the document front matter as the value for the `name` specified for that field. A full listing of available widgets can be found in the [Widgets doc](/docs/widgets).
176 |
177 | Based on this example, you can go through the post types in your site and add the appropriate settings to your Saleina CMS `config.yml` file. Each post type should be listed as a separate node under the `collections` field.
178 |
179 | ## Accessing the CMS
180 |
181 | Your site CMS is now fully configured and ready for login!
182 |
183 | You can access your site's CMS at `yoursite.com/admin/`.
184 |
185 | That's All Folks!
186 |
--------------------------------------------------------------------------------
/website/content/docs/backends.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Backends
3 | weight: 25
4 | group: start
5 | ---
6 |
7 | Saleina CMS stores content in your GitLab repository. In order for this to work, you need to authenticate with your Git host. We have a few options for handling this.
8 |
9 | ## GitLab Backend
10 |
11 | For repositories stored on GitLab, the `gitlab` backend allows CMS users to log in directly with their GitLab account. Note that all users must have push access to your content repository for this to work.
12 |
13 | The GitLab API allows for two types of OAuth2 flows: [Web Application Flow](https://docs.gitlab.com/ce/api/oauth2.html#web-application-flow), and [Implicit Grant](https://docs.gitlab.com/ce/api/oauth2.html#implicit-grant), which operates _without_ the need for an authentication server and which is the one Saleina CMS uses.
14 |
15 | ### Client-Side Implicit Grant
16 |
17 | With GitLab's Implicit Grant, users can authenticate with GitLab directly from the client. To do this:
18 |
19 | 1. Follow the [GitLab docs](https://docs.gitlab.com/ee/integration/oauth_provider.html#adding-an-application-through-the-profile) to add your Saleina CMS instance as an OAuth application. For the **Redirect URI**, enter the address where you access Saleina CMS, for example, `https://www.example.com/admin/`. For scope, select `api`.
20 | 2. GitLab will give you an **Application ID**. Copy this and enter it in your Saleina CMS `config.yml` file, along with the following settings:
21 |
22 | ```yaml
23 | backend:
24 | name: gitlab
25 | repo: owner-name/repo-name # Path to your GitLab repository
26 | client_id: your-app-id # Application ID from your GitLab settings
27 | ```
28 |
29 | GitLab will also provide you with a client secret. You should _never_ store this in your repo or reveal it in the client.
--------------------------------------------------------------------------------
/website/content/docs/boolean.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Boolean
3 | group: widgets
4 | ---
5 |
6 | The boolean widget translates a toggle switch input to a true/false value.
7 |
8 | - **Name:** `boolean`
9 | - **UI:** toggle switch
10 | - **Data type:** boolean
11 | - **Options:**
12 | - `default`: accepts `true` or `false`; defaults to `false`
13 | - **Example:**
14 |
15 | ```yaml
16 | - {label: "Draft", name: "draft", widget: "boolean", default: true}
17 | ```
18 |
--------------------------------------------------------------------------------
/website/content/docs/collection-types.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Collection Types
3 | weight: 27
4 | group: start
5 | ---
6 |
7 | All editable content types are defined in the `collections` field of your `config.yml` file, and displayed in the left sidebar of the editor UI.
8 |
9 | Collections come in two main types: `folder` and `files`.
10 |
11 | ## Folder collections
12 |
13 | Folder collections represent one or more files with the same format, fields, and configuration options, all stored within the same folder in the repository. You might use a folder collection for blog posts, product pages, author data files, etc.
14 |
15 | Unlike file collections, folder collections have the option to allow editors to create new items in the collection. This is set by the boolean `create` field.
16 |
17 | **Note:** Folder collections must have at least one field with the name "title" for creating new entry slugs. That field should use the default "string" widget. The "label" for the field can be any string value.
18 |
19 | Example:
20 |
21 | ```yaml
22 | - label: "Blog"
23 | name: "blog"
24 | folder: "content/blog"
25 | tabs:
26 | - label: Basic
27 | fields:
28 | - {label: "Title", name: "title", widget: "string"}
29 | - {label: "Publish Date", name: "date", widget: "datetime"}
30 | - {label: "Featured Image", name: "thumbnail", widget: "image"}
31 | - label: Body
32 | fields:
33 | - {label: "Body", name: "body", widget: "markdown"}
34 | ```
35 |
36 | ## File collections
37 |
38 | A `files` collection contains one or more uniquely configured files. Unlike items in `folder` collections, which repeat the same configuration over all files in the folder, each item in a `files` collection has an explicitly set path, filename, and configuration. This can be useful for unique files with a custom set of fields, like a settings file or a custom landing page with a unique content structure.
39 |
40 | When configuring a `files` collection, each file in the collection is configured separately, and listed under the `files` field of the collection. Each file has its own list of `tabs` and `fields`, and a unique filepath specified in the `file` field (relative to the base of the repo).
41 |
42 | **Note:** Files listed in a file collection must already exist in the repo, and must have a valid value for the file type. For example, an empty file works as valid YAML, but a JSON file must have a non-empty value to be valid, such as an empty object.
43 |
44 | Example:
45 |
46 | ```yaml
47 | - label: "Pages"
48 | name: "pages"
49 | files:
50 | - label: "Home"
51 | name: "home"
52 | file: "content/_index.md"
53 | tabs:
54 | - label: "Basic"
55 | fields:
56 | - {label: "Title", name: "title", widget: "string"}
57 | - {label: "Intro", name: "intro", widget: "markdown"}
58 |
59 | - label: "Team"
60 | fields:
61 | - label: "Team"
62 | name: "team"
63 | widget: "list"
64 | fields:
65 | - {label: "Name", name: "name", widget: "string"}
66 | - {label: "Position", name: "position", widget: "string"}
67 | - {label: "Photo", name: "photo", widget: "image"}
68 | ```
69 |
--------------------------------------------------------------------------------
/website/content/docs/configuration-options.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Configuration Options
3 | weight: 23
4 | group: reference
5 | ---
6 |
7 | All configuration options for Saleina CMS are specified in a `config.yml` file, in the folder where you access the editor UI (usually in the `/admin` folder).
8 |
9 | To see working configuration examples check out the [CMS demo site](https://demo.saleinacms.org). (No login required: click the login button and the CMS will open.) You can refer to the demo [configuration code](https://github.com/saleina/saleinacms/blob/master/public/config.yml) to see how each option was configured.
10 |
11 | You can find details about all configuration options below. Note that [YAML syntax](https://en.wikipedia.org/wiki/YAML#Basic_components) allows lists and objects to be written in block or inline style, and the code samples below include a mix of both.
12 |
13 |
14 | ## Backend
15 |
16 | *This setting is required.*
17 |
18 | The `backend` option specifies how to access the content for your site, including authentication.
19 |
20 | ## Logo
21 |
22 | The `logo` option specifies a custom logo to display in the editor UI. It expects a url to an image file.
23 |
24 | **Example**
25 | ```yaml
26 | logo: "http://example.com/logo.png"
27 | ```
28 |
29 | ## Media and Public Folders
30 |
31 | *This setting is required.*
32 |
33 | Saleina CMS users can upload files to your repository using the Media Gallery. The following settings specify where these files are saved, and where they can be accessed on your built site.
34 |
35 | **Options**
36 |
37 | - `media_folder` (required): Folder path where uploaded files should be saved, relative to the base of the repo.
38 | - `public_folder` (optional): Folder path where uploaded files will be accessed, relative to the base of the built site. For fields controlled by [file] or [image] widgets, the value of the field is generated by prepending this path to the filename of the selected file. Defaults to the value of `media_folder`, with an opening `/`.
39 |
40 | **Example**
41 |
42 | ``` yaml
43 | media_folder: "static/images/uploads"
44 | public_folder: "/images/uploads"
45 | ```
46 |
47 | Based on the settings above, if a user used an image widget field called `avatar` to upload and select an image called `philosoraptor.png`, the image would be saved to the repository at `/static/images/uploads/philosoraptor.png`, and the `avatar` field for the file would be set to `/images/uploads/philosoraptor.png`.
48 |
49 | ## Collections
50 |
51 | *This setting is required.*
52 |
53 | The `collections` setting is the heart of your Saleina CMS configuration, as it determines how content types and editor fields in the UI generate files and content in your repository. Each collection you configure displays in the left sidebar of the Content page of the editor UI, in the order they are entered into your Saleina CMS `config.yml` file.
54 |
55 | `collections` accepts a list of collection objects, each with the following options:
56 |
57 | - `name` (required): unique identifier for the collection, used as the key when referenced in other contexts.
58 | - `Label`: label for the collection in the editor UI; defaults to the value of `name`
59 | - `file` or `folder` (requires one of these): specifies the collection type and location; details in [Collection Types](/docs/collection-types)
60 | - `create`: for `folder` collections only; `true` allows users to create new items in the collection; defaults to `false`
61 | - `delete`: `false` prevents users from deleting items in a collection; defaults to `true`
62 | - `hooks`: see detailed description below
63 | - `type`: see detailed description below
64 | - `slug`: see detailed description below
65 | - `tabs` (required): see detailed description below
66 |
67 | The last few options require more detailed information.
68 |
69 | ### `hooks`
70 |
71 | Hooks allow you to be notified when certain actions occur in Saleina CMS, it allows limitless integrations and possibilities.
72 |
73 | `hooks` accepts the following properties
74 |
75 | - `created`: Accepts a url to be notified when a new item is created in a folder collection, it sends the created data with a `POST` method
76 |
77 | - `updated`: Accepts a url to be notified when an item in a file or folder collection is updated, it sends the updated data with a `PUT` method
78 |
79 | - `deleted`: Accepts a url to be notified when an item in a file or folder collection is deleted, it sends the deleted data with `DELETE` method.
80 |
81 | **Example:**
82 |
83 | ```yaml
84 | collections:
85 | - label: "Blog"
86 | name: "blog"
87 | folder: "website/content/blog"
88 | delete: true
89 | hooks:
90 | created: "https://example.com/blog/" # url to be called when a new blog post is created
91 | updated: "https://example.com/blog/" # url to be called when a blog post is updated
92 | deleted: "https://example.com/blog/" # url to be called when a blog post is deleted
93 | tabs:
94 | - label: "Basic"
95 | fields:
96 | - {label: "Draft", name: "draft", widget: "boolean", default: true}
97 | - {label: "Title", name: "title", widget: "string"}
98 | - {label: "Publish Date", name: "date", widget: "datetime", format: "Z"}
99 | - label: "Body"
100 | fields:
101 | - {label: "Body", name: "body", widget: "markdown"}
102 | ```
103 |
104 | **Example data:**
105 |
106 | ```json
107 | {
108 | "path": "data/settings.json",
109 | "content": "{}",
110 | "branch": "master"
111 | }
112 | ```
113 |
114 | ### `type`
115 |
116 | These setting determines how collection files are parsed and saved. It's optional and by default Saleina CMS will assume `md`. If your collection contains a different file type or you'd like more control, you can set these field explicitly.
117 |
118 | ### `slug`
119 |
120 | For folder collections where users can create new items, the `slug` option specifies a template for generating new filenames based on a file's creation date and `title` field. (This means that all collections with `create: true` must have a `title` field.)
121 |
122 | **Available template tags:**
123 |
124 | - `{{slug}}`: a url-safe version of the `title` field for the file
125 | - `{{year}}`: 4-digit year of the file creation date
126 | - `{{month}}`: 2-digit month of the file creation date
127 | - `{{day}}`: 2-digit day of the month of the file creation date
128 | - `{{hour}}`: 2-digit hour of the file creation date
129 | - `{{minute}}`: 2-digit minute of the file creation date
130 | - `{{second}}`: 2-digit second of the file creation date
131 |
132 | **Example:**
133 |
134 | ```yaml
135 | slug: "{{year}}-{{month}}-{{day}}_{{slug}}"
136 | ```
137 |
138 | ### `tabs`
139 | The `tabs` option maps a collection of fields to editor tabs. The order of the tabs in your Saleina CMS `config.yml` file determines their order in the editor UI and in the saved file.
140 |
141 | `tabs` accepts the following proprties
142 |
143 | - `label` (required): label for the tab in the editor UI.
144 | - `fields` (required): option maps editor UI widgets to field-value pairs in the saved file, the order of the fields in your Saleina CMS `config.yml` file determines their order in the editor UI and in the saved file.
145 |
146 | `fields` accepts a list of collection objects, each with the following options:
147 |
148 | - `name` (required): unique identifier for the field, used as the key when referenced in other contexts.
149 | - `label`: label for the field in the editor UI; defaults to the value of `name`
150 | - `widget`: defines editor UI and inputs and file field data types; details in [Widgets](/docs/widgets)
151 | - `default`: specify a default value for a field; available for most widget types (see [Widgets](/docs/widgets) for details on each widget type)
152 | - `required`: specify as `false` to make a field optional; defaults to `true`
153 | - `pattern`: add field validation by specifying a string with a regex pattern.
154 |
155 | In files with frontmatter, one field should be named `body`. This special field represents the section of the document (usually markdown) that comes after the frontmatter.
156 |
157 | **Example:**
158 |
159 | ```yaml
160 | tabs:
161 | - label: Basic
162 | fields:
163 | - {label: "Title", name: "title", widget: "string", pattern: ".{20,}"}
164 | - {label: "Layout", name: "layout", widget: "hidden", default: "blog"}
165 | - label: Body
166 | fields:
167 | - {label: "Featured Image", name: "thumbnail", widget: "image", required: false}
168 | - {label: "Body", name: "body", widget: "markdown"}
169 | ```
--------------------------------------------------------------------------------
/website/content/docs/date.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Date
3 | group: widgets
4 | ---
5 |
6 | The date widget translates a date picker input to a date string. For saving date and time together, use the datetime widget.
7 |
8 | - **Name:** `date`
9 | - **UI:** date picker
10 | - **Data type:** Flatpickr formatted date string
11 | - **Options:**
12 | - `default`: accepts a date string, or an empty string to accept blank input; otherwise defaults to current date
13 | - `format`: optional; accepts Flatpickr.js [tokens](https://flatpickr.js.org/formatting/); defaults to raw Date object (if supported by output format)
14 | - **Example:**
15 |
16 | ```yaml
17 | - label: "Birthdate"
18 | name: "birthdate"
19 | widget: "date"
20 | default: ""
21 | format: "Y/m/d"
22 | ```
23 |
--------------------------------------------------------------------------------
/website/content/docs/datetime.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Datetime
3 | group: widgets
4 | ---
5 |
6 | The datetime widget translates a datetime picker to a datetime string. For saving the date only, use the date widget.
7 |
8 | - **Name:** `datetime`
9 | - **UI:** datetime picker
10 | - **Data type:** Flatpickr formatted datetime string
11 | - **Options:**
12 | - `default`: accepts a datetime string, or an empty string to accept blank input; otherwise defaults to current datetime
13 | - `format`: optional; accepts Flatpickr.js [tokens](https://flatpickr.js.org/formatting/); defaults to raw Date object (if supported by output format)
14 | - **Example:**
15 |
16 | ```yaml
17 | - label: "Start time"
18 | name: "start"
19 | widget: "datetime"
20 | format: "Y-m-d h:i K"
21 | ```
22 |
--------------------------------------------------------------------------------
/website/content/docs/file.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: File
3 | group: widgets
4 | ---
5 |
6 | The file widget allows editors to upload a file or select an existing one from the media library. The path to the file will be saved to the field as a string.
7 |
8 | - **Name:** `file`
9 | - **UI:** file picker button opens media gallery allowing to pick files; displays selected file name
10 | - **Data type:** file path string, based on `media_folder`/`public_folder` configuration
11 | - **Options:**
12 | - `default`: accepts a file path string; defaults to null
13 | - **Example:**
14 |
15 | ```yaml
16 | - label: "Document"
17 | name: "document"
18 | widget: "file"
19 | default: "/uploads/doc.pdf"
20 | ```
21 |
--------------------------------------------------------------------------------
/website/content/docs/hidden.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Hidden
3 | group: widgets
4 | ---
5 |
6 | Hidden widgets do not display in the UI. In folder collections that allow users to create new items, you will often want to set a default for hidden fields, so they will be set without requiring an input.
7 |
8 | - **Name:** `hidden`
9 | - **UI:** none
10 | - **Data type:** any valid data type
11 | - **Options:**
12 | - `default`: accepts any valid data type; recommended for collections that allow adding new items
13 | - **Example:**
14 |
15 | ```yaml
16 | - {label: "Layout", name: "layout", widget: "hidden", default: "blog"}
17 | ```
18 |
--------------------------------------------------------------------------------
/website/content/docs/image.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Image
3 | group: widgets
4 | ---
5 |
6 | The image widget allows editors to upload an image or select an existing one from the media library. The path to the image file will be saved to the field as a string.
7 |
8 | - **Name:** `image`
9 | - **UI:** file picker button opens media gallery allowing image files (jpg, jpeg, gif, png, bmp, svg) only; displays selected image thumbnail
10 | - **Data type:** file path string, based on `media_folder`/`public_folder` configuration
11 | - **Options:**
12 | - `default`: accepts a file path string; defaults to null
13 | - **Example:**
14 |
15 | ```yaml
16 | - label: "Featured Image"
17 | name: "thumbnail"
18 | widget: "image"
19 | default: "/uploads/chocolate-dogecoin.jpg"
20 | ```
21 |
--------------------------------------------------------------------------------
/website/content/docs/introduction.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introduction
3 | weight: 1
4 | group: start
5 | ---
6 |
7 | Saleina CMS is an open source content management system for your Git workflow that enables you to provide editors with friendly UI and intuitive workflow. You can use it with any static site generator to create faster, more flexible web projects. Content is stored in your Git repository alongside your code for easier versioning, multi-channel publishing, and the option to handle content updates directly in Git.
8 |
9 | At its core, Saleina CMS is an open-source Vue app that acts as a wrapper for the Git workflow, using the GitLab API. This provides many advantages, including:
10 |
11 | * **Fast, web-based UI:** with tabbed UI, rich-text editing, and media uploads.
12 | * **Platform agnostic:** works with most static site generators.
13 | * **Easy installation:** add two files to your site and hook up the backend by linking to our CDN.
14 | * **Modern authentication:** using GitLab.
15 | * **Flexible content types:** specify an unlimited number of content types with custom fields.
16 |
17 | ## Find out more
18 |
19 | - Configure your existing site by following a [tutorial](/docs/add-to-your-site/) or checking [configuration options](/docs/configuration-options).
20 | - Ask questions and share ideas in the Saleina CMS community on [Telegram](tg://resolve?domain=saleinacmsdiscussions).
21 |
--------------------------------------------------------------------------------
/website/content/docs/list.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: List
3 | group: widgets
4 | ---
5 |
6 | The list widget allows you to create a repeatable item in the UI which saves as a list of widget values. map a user-provided string with a comma delimiter into a list. You can choose any widget as a child of a list widget—even other lists.
7 |
8 | - **Name:** `list`
9 | - **UI:** if `fields` is specified, field containing a repeatable child widget, with controls for adding, and deleting widgets; if unspecified, a text input for entering comma-separated values
10 | - **Data type:** list of widget values
11 | - **Options:**
12 | - `default`: if `fields` is specified, declare defaults on the child widgets; if not, you may specify a list of strings to populate the text field
13 | - `max`: limits the number of items in the list
14 | - `fields`: a nested list of multiple widget fields to be included in each repeatable iteration
15 | - **Example** (`fields` not specified):
16 |
17 | ```yaml
18 | - label: "Tags"
19 | name: "tags"
20 | widget: "list"
21 | default: ["news"]
22 | ```
23 |
24 | - **Example** (with `fields`):
25 |
26 | ```yaml
27 | - label: "Testimonials"
28 | name: "testimonials"
29 | widget: "list"
30 | fields:
31 | - {label: Quote, name: quote, widget: string, default: "Everything is awesome!"}
32 | - label: Author
33 | name: author
34 | widget: object
35 | fields:
36 | - {label: Name, name: name, widget: string, default: "Emmet"}
37 | - {label: Avatar, name: avatar, widget: image, default: "/img/emmet.jpg"}
38 | ```
39 |
--------------------------------------------------------------------------------
/website/content/docs/markdown.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Markdown
3 | group: widgets
4 | ---
5 |
6 | The markdown widget provides a full fledged text editor that allows users to format text with features such as headings and blockquotes.
7 |
8 | *Please note:* in case you want to use your markdown editor to fill a markdown's file content after the frontmatter, you'll have name the field as `body` so then the CMS can recognize it and save the file accordingly.
9 |
10 | - **Name:** `markdown`
11 | - **UI:** full text editor
12 | - **Data type:** markdown
13 | - **Options:**
14 | - `default`: accepts markdown content
15 | - **Example:**
16 |
17 | ```yaml
18 | - {label: "Blog post content", name: "body", widget: "markdown"}
19 | ```
--------------------------------------------------------------------------------
/website/content/docs/number.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Number
3 | group: widget
4 | ---
5 |
6 | The number widget uses an HTML number input, saving the value as a string, integer, or floating point number.
7 |
8 | - **Name:** `number`
9 | - **UI:** HTML [number input](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number)
10 | - **Data type:** string by default; configured by `valueType` option
11 | - **Options:**
12 | - `default`: accepts string or number value; defaults to empty string
13 | - `valueType`: accepts `int` or `float`; any other value results in saving as a string
14 | - `min`: accepts a number for minimum value accepted; unset by default
15 | - `max`: accepts a number for maximum value accepted; unset by default
16 | - **Example:**
17 |
18 | ```yaml
19 | - label: "Puppy Count"
20 | name: "puppies"
21 | widget: "number"
22 | default: 2
23 | valueType: "int"
24 | min: 1
25 | max: 101
26 | ```
27 |
--------------------------------------------------------------------------------
/website/content/docs/object.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Object
3 | group: widgets
4 | ---
5 |
6 | The object widget allows you to group multiple widgets together, nested under a single field. You can choose any widget as a child of an object widget—even other objects.
7 |
8 | - **Name:** `object`
9 | - **UI:** a field containing one or more child widgets
10 | - **Data type:** list of child widget values
11 | - **Options:**
12 | - `default`: you can set defaults within each sub-field's configuration
13 | - `fields`: (**required**) a nested list of widget fields to include in your widget
14 | - **Example:**
15 |
16 | ```yaml
17 | - label: "Profile"
18 | name: "profile"
19 | widget: "object"
20 | fields:
21 | - {label: "Public", name: "public", widget: "boolean", default: true}
22 | - {label: "Name", name: "name", widget: "string"}
23 | - label: "Birthdate"
24 | name: "birthdate"
25 | widget: "date"
26 | default: ""
27 | format: "MM/DD/YYYY"
28 | - label: "Address"
29 | name: "address"
30 | widget: "object"
31 | fields:
32 | - {label: "Street Address", name: "street", widget: "string"}
33 | - {label: "City", name: "city", widget: "string"}
34 | - {label: "Postal Code", name: "post-code", widget: "string"}
35 | ```
36 |
--------------------------------------------------------------------------------
/website/content/docs/select.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Select
3 | group: widgets
4 | ---
5 |
6 | The select widget allows you to pick a single string value from a dropdown menu.
7 |
8 | - **Name:** `select`
9 | - **UI:** HTML select input
10 | - **Data type:** string
11 | - **Options:**
12 | - `default`: accepts a string; defaults to an empty string
13 | - `options`: (**required**) a list of options for the dropdown menu; can be listed in two ways:
14 | - string values: the label displayed in the dropdown is the value saved in the file
15 | - object with `label` and `value` fields: the label displays in the dropdown; the value is saved in the file
16 | - **Example** (options as strings):
17 |
18 | ```yaml
19 | - label: "Align Content"
20 | name: "align"
21 | widget: "select"
22 | options: ["left", "center", "right"]
23 | ```
24 | - **Example** (options as objects):
25 |
26 | ```yaml
27 | - label: "City"
28 | name: "airport-code"
29 | widget: "select"
30 | options:
31 | - { label: "Chicago", value: "ORD" }
32 | - { label: "Paris", value: "CDG" }
33 | - { label: "Tokyo", value: "HND" }
34 | ```
35 |
36 |
--------------------------------------------------------------------------------
/website/content/docs/string.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: String
3 | group: widgets
4 | ---
5 |
6 | The string widget translates a basic text input to a string value. For larger textarea inputs, use the text widget.
7 |
8 | - **Name:** `string`
9 | - **UI:** text input
10 | - **Data type:** string
11 | - **Options:**
12 | - `default`: accepts a string; defaults to an empty string
13 | - **Example:**
14 |
15 | ```yaml
16 | - {label: "Title", name: "title", widget: "string"}
17 | ```
18 |
--------------------------------------------------------------------------------
/website/content/docs/text.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Text
3 | group: widgets
4 | ---
5 |
6 | The text widget takes a multiline text field and saves it as a string. For shorter text inputs, use the string widget.
7 |
8 | - **Name:** `text`
9 | - **UI:** HTML textarea
10 | - **Data type:** string
11 | - **Options:**
12 | - `default`: accepts a string; defaults to an empty string
13 | - **Example:**
14 |
15 | ```yaml
16 | - {label: "Description", name: "description", widget: "text"}
17 | ```
18 |
19 |
--------------------------------------------------------------------------------
/website/content/docs/update-the-cms-version.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Update the CMS Version
3 | weight: 60
4 | group: start
5 | ---
6 |
7 | ## CDN
8 |
9 | If you are using the CMS through a CDN like JsDelivr, then that depends on the version tag you are using. You can find the version tag you are using in the `/admin/index.html` file of your site.
10 |
11 | - (Recommended) If you use `^1.0.0`, the CMS will do all updates except major versions automatically.
12 | - It will upgrade to `1.0.1`, `1.1.0`, `1.1.2`.
13 | - It will not upgrade to `2.0.0` or higher.
14 | - It will not upgrade to beta versions.
15 |
16 | - If you use `~1.0.0`, the CMS will do only patch updates automatically.
17 | - It will upgrade `1.0.1`, `1.0.2`.
18 | - It will not upgrade to `1.1.0` or higher.
19 | - It will not upgrade beta versions.
20 |
--------------------------------------------------------------------------------
/website/content/docs/widgets.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Introduction
3 | weight: 30
4 | group: widgets
5 | ---
6 |
7 | Widgets define the data type and interface for entry fields. Saleina CMS comes with several built-in widgets. Click the widget names in the sidebar to jump to specific widget details.
8 |
9 | Widgets are specified as collection fields in the Saleina CMS `config.yml` file. Note that [YAML syntax](https://en.wikipedia.org/wiki/YAML#Basic_components) allows lists and objects to be written in block or inline style, and the code samples below include a mix of both.
10 |
11 | ## Common widget options
12 |
13 | The following options are available on all fields:
14 |
15 | - `required`: specify as `false` to make a field optional; defaults to `true`
16 | - `pattern`: add field validation by specifying a string with a [regex pattern](https://regexr.com/).
17 |
18 | - **Example:**
19 |
20 | ```yaml
21 | - label: "Title"
22 | name: "title"
23 | widget: "string"
24 | pattern: ".{12,}"
25 | ```
--------------------------------------------------------------------------------
/website/content/support/_index.md:
--------------------------------------------------------------------------------
1 | ---
2 | ---
3 | # Sponsor Saleina CMS Development
4 |
5 | Saleina CMS is an MIT licensed open source project and completely free to use. However, the amount of effort needed to maintain and develop new features for the project is not sustainable without proper financial backing. You can support Saleina CMS development via the following methods:
6 |
7 |
8 |
9 | ## Our Sponsors
10 |
11 |
Get the speed, security, and scalability of a static site, while still providing a convenient editing interface for content.
14 |
15 |
16 |
17 |
An integrated part of your Git workflow
18 |
Content is stored in your Git repository alongside your code for easier versioning, multi-channel publishing, and the option to handle content updates directly in Git.
19 |
20 |
21 |
22 |
Editor friendly user interface
23 |
The web based app includes tabbed UI, rich-text editing, and media management.
24 |
25 |
26 |
27 |
Community driven project
28 |
Saleina CMS is built by a community and you can help.