| OLD | NEW |
| 1 # CMS # | 1 # CMS # |
| 2 | 2 |
| 3 We use this CMS for [adblockplus.org](https://github.com/adblockplus/web.adblock
plus.org/) | 3 We use this CMS for [adblockplus.org](https://github.com/adblockplus/web.adblock
plus.org/) |
| 4 and related websites. It converts a directory with content data into static | 4 and related websites. It converts a directory with content data into static |
| 5 files. You are free to use it for other projects but please keep in mind that we | 5 files. You are free to use it for other projects but please keep in mind that we |
| 6 make no stability guarantees whatsoever and might change functionality any time. | 6 make no stability guarantees whatsoever and might change functionality any time. |
| 7 | 7 |
| 8 ## How to use ## | 8 ## Getting started ## |
| 9 | 9 |
| 10 ### Running the test server ### | 10 The easiest way to get started is to run a test server. The test server will |
| 11 | 11 convert your content directory on the fly, your changes will become visible |
| 12 The test server will convert your content directory on the fly, your changes | 12 immediately. To run it you need: |
| 13 will become visible immediately. To run it you need: | |
| 14 | 13 |
| 15 * Python 2.7 | 14 * Python 2.7 |
| 16 * [Jinja2](http://jinja.pocoo.org/) and | 15 * [Jinja2](http://jinja.pocoo.org/) and |
| 17 [Markdown](https://pypi.python.org/pypi/Markdown) modules (can be installed by | 16 [Markdown](https://pypi.python.org/pypi/Markdown) modules (can be installed by |
| 18 running `easy_install Jinja2 Markdown` from the command line) | 17 running `easy_install Jinja2 Markdown` from the command line) |
| 19 * A current copy of the | 18 * A current copy of the |
| 20 [cms repository](https://github.com/adblockplus/cms/) (can be | 19 [cms repository](https://github.com/adblockplus/cms/) (can be |
| 21 [downloaded as ZIP file](https://github.com/adblockplus/cms/archive/master.zip
) | 20 [downloaded as ZIP file](https://github.com/adblockplus/cms/archive/master.zip
) |
| 22 or cloned via `git clone https://github.com/adblockplus/cms.git`) | 21 or cloned via `git clone https://github.com/adblockplus/cms.git`) |
| 23 | 22 |
| 24 Optionally, the [Werkzeug](http://werkzeug.pocoo.org/) module can be installed | 23 Optionally, the [Werkzeug](http://werkzeug.pocoo.org/) module can be installed |
| 25 as well, this will provide some developer features. | 24 as well, this will provide some developer features. |
| 26 | 25 |
| 27 Run the `runserver.py` script from your content directory, e.g.: | 26 Run the `runserver.py` script from your content directory, e.g.: |
| 28 | 27 |
| 29 python ../cms/runserver.py | 28 python ../cms/runserver.py |
| 30 | 29 |
| 31 Alternatively, the content directory could also be specified as command line | 30 Alternatively, the content directory could also be specified as command line |
| 32 parameter of `runserver.py`. This will start a local web server on port 5000, | 31 parameter of `runserver.py`. This will start a local web server on port 5000, |
| 33 e.g. the page the page `pages/example.md` will be accessible under | 32 e.g. the page the page `pages/example.md` will be accessible under |
| 34 `http://localhost:5000/en/example`. | 33 `http://localhost:5000/en/example`. |
| 35 | 34 |
| 36 Note that the test server is inefficient and shouldn't be run in production. | 35 Note that the test server is inefficient and shouldn't be run in production. |
| 37 There you should generate static files as explained below. | 36 There you should generate static files as explained below. |
| 38 | 37 |
| 39 ### Generating the standalone test server ### | 38 ## Documentation ## |
| 40 | 39 |
| 41 The standalone test server is a single binary, without any further dependencies. | 40 - How to use |
| 42 It can be copied to another system and will no longer require Python or any of | 41 - [Running the test server](docs/usage/test-server.md) |
| 43 its modules. In order to generate the standalone test server you need all the | 42 - [Generating the standalone test server](docs/usage/standalone-test-server.
md) |
| 44 prerequisites required to run the test server and | 43 - [Generating static files](docs/usage/generate-static-files.md) |
| 45 [PyInstaller](https://github.com/pyinstaller/pyinstaller/wiki). PyInstaller can | 44 - [Syncing translations](docs/usage/syncing-translations.md) |
| 46 be installed by running `easy_install pyinstaller`. | 45 - Content structure |
| 47 | 46 - [Configuration (`settings.ini`)](docs/content/settings.md) |
| 48 Run the following command from the directory of the `cms` repository: | 47 - [Custom Jinja2 global functions and variables (`globals`)](docs/content/gl
obals.md) |
| 49 | 48 - [Custom Jinja2 filters (`filters`)](docs/content/filters.md) |
| 50 pyinstaller runserver.spec | 49 - [Localization files (`locales`)](docs/content/locales.md) |
| 51 | 50 - [Page layout templates (`templates`)](docs/content/templates.md) |
| 52 If successful, this will put the standalone test server into the `dist` | 51 - [Various include files (`includes`)](docs/content/includes.md) |
| 53 directory. | 52 - [User-visible pages (`pages`)](docs/content/pages.md) |
| 54 | 53 - [Static content (`static`)](docs/content/static.md) |
| 55 ### Generating static files ### | 54 - API |
| 56 | 55 - [Variables](docs/api/variables.md) |
| 57 On your production server you should convert the content directory into static | 56 - [Custom filters](docs/api/filters.md) |
| 58 files. To do that you need: | 57 - [Global functions](docs/api/functions.md) |
| 59 | |
| 60 * Python 2.7 | |
| 61 * [Jinja2](http://jinja.pocoo.org/) and | |
| 62 [Markdown](https://pypi.python.org/pypi/Markdown) modules (can be installed by | |
| 63 running `easy_install Jinja2 Markdown` from the command line) | |
| 64 * A current copy of the | |
| 65 [cms repository](https://github.com/adblockplus/cms/) (can be | |
| 66 [downloaded as ZIP file](https://github.com/adblockplus/cms/archive/master.zip
) | |
| 67 or cloned via `git clone https://github.com/adblockplus/cms.git`) | |
| 68 | |
| 69 Run the following command from the directory of the `cms` repository: | |
| 70 | |
| 71 python -m cms.bin.generate_static_pages www_directory target_directory | |
| 72 | |
| 73 Here `www_directory` should be replaced by the path to your content directory. | |
| 74 `target_directory` is the path where static files will be placed. | |
| 75 | |
| 76 Note: Localized versions of pages will only be generated when their translations | |
| 77 are at least 30% complete. (Measured by comparing the total number | |
| 78 of translatable strings on a page to the number of strings that have been | |
| 79 translated for a given locale.) This is different from the test server which | |
| 80 will include less complete translations. | |
| 81 | |
| 82 ### Syncing translations ### | |
| 83 | |
| 84 Before syncing translations ensure the following: | |
| 85 | |
| 86 - The `crowdin-project-name` setting has been set in the site's configuration. | |
| 87 - The `urllib3` Python module is installed. | |
| 88 | |
| 89 Now to sync with Crowdin type the following: | |
| 90 | |
| 91 python -m cms.bin.translate www_directory crowdin_project_api_key [logging_l
evel] | |
| 92 | |
| 93 The script might take a while to run, it does the following things: | |
| 94 | |
| 95 1. Requests information about your Crowdin project | |
| 96 2. Checks all local locales are supported by Crowdin and enabled for your projec
t | |
| 97 3. Renders all pages in your default language and extracts all page strings | |
| 98 4. Creates any required directories in your Crowdin project | |
| 99 5. Uploads any new page strings and updates any existing page strings | |
| 100 6. Uploads any pre-existing translation files for any newly uploaded pages | |
| 101 7. Requests Crowdin generate a fresh translations export archive | |
| 102 8. Downloads the archive of translations and extracts it replacing all local | |
| 103 translation files | |
| 104 | |
| 105 Notes: | |
| 106 | |
| 107 - You have to use the Crowdin project's API key, not your account API key | |
| 108 - You should probably enable "Skip untranslated strings" under your | |
| 109 project settings. | |
| 110 - Translations are only _uploaded_ for new files. If you are syncing with | |
| 111 Crowdin for the first time, and you have existing translation files pay | |
| 112 attention to any warnings. If there are any problems it is recommended | |
| 113 you re-create another fresh Crowdin project and try again. (Otherwise | |
| 114 translations for the pages already uploaded could be lost!) | |
| 115 - If you are running the script from a cronjob or similar you will probably | |
| 116 want to set the logging level to `ERROR` or similar | |
| 117 | |
| 118 ## Content structure ## | |
| 119 | |
| 120 Currently, the following directories of your content directory will be | |
| 121 considered: | |
| 122 | |
| 123 * `filters`: Custom Jinja2 filters | |
| 124 * `globals`: Custom Jinja2 global functions and variables | |
| 125 * `includes`: Various include files | |
| 126 * `locales`: Localization files | |
| 127 * `pages`: User-visible pages | |
| 128 * `static`: Static content | |
| 129 * `templates`: Page layout templates | |
| 130 | |
| 131 There should also be a `settings.ini` file with configuration. | |
| 132 | |
| 133 All of these are explained in more detail below. | |
| 134 | |
| 135 ### Configuration (settings.ini) ### | |
| 136 | |
| 137 The following sections can be defined in `settings.ini`: | |
| 138 | |
| 139 * `[general]`: following settings should be listed here: | |
| 140 * `defaultlocale`: The fallback locale, to be used whenever no localized | |
| 141 page/strings can be found for a locale. | |
| 142 * `defaultpage`: the default page which is displayed by the server if the URL | |
| 143 doesn't contain a page name. Note that while the test server will consider | |
| 144 that setting automatically, the real server might need to be configured | |
| 145 accordingly. | |
| 146 * `crowdin-project-name`: The Crowdin project name, this must be set for if | |
| 147 you intend to use the cms.bin.translate script to update the Crowdin | |
| 148 translations. | |
| 149 * `[langnames]`: defines the language names correspoding to particular language | |
| 150 codes. | |
| 151 * `[rtl]`: any language codes listed here are treated as right-to-left languages
. | |
| 152 The values of the settings are ignored. | |
| 153 * `[locale_overrides]`: every entry defines that a page should use a different | |
| 154 locale file, not the one matching its name (to be used when multiple pages | |
| 155 share localization data). | |
| 156 | |
| 157 ### Localization files ### | |
| 158 | |
| 159 The language-specific data is stored in the `locales` directory. Each language | |
| 160 (identified by its locale code) is given a subdirectory here. The `.json` files | |
| 161 contain localizable strings using the following format: | |
| 162 | |
| 163 { | |
| 164 "stringid": { | |
| 165 "message": "Translated string", | |
| 166 "description": "Optional string description" | |
| 167 }, | |
| 168 ... | |
| 169 } | |
| 170 | |
| 171 Any other files are considered localizable files and will be available on the | |
| 172 server unchanged. This is useful for images for example: a language-dependent | |
| 173 image can be placed into the `locales/en` directory and a German version | |
| 174 of the same image into the `locales/de` directory. E.g. the file | |
| 175 `locales/en/foo.png` will be available on the server under the URL `/en/foo.png`
. | |
| 176 | |
| 177 ### Pages ### | |
| 178 | |
| 179 The pages are defined in the `pages` directory. The file extension defines the | |
| 180 format in which a page is specified and won't be visible on the web server. | |
| 181 The page name is prepended by the locale code in the URL, e.g. `pages/foo.md` | |
| 182 can be available under the URLs `/en/foo` and `/de/foo` (the English and German | |
| 183 versions respectively). Regardless of the format, a page can define a number of | |
| 184 settings using the following format: | |
| 185 | |
| 186 setting = value | |
| 187 | |
| 188 Note that the settings have to placed at the beginning of the file, before the | |
| 189 actual content. The following settings can be changed: | |
| 190 | |
| 191 * `template`: This defines the template to be used for this page (without the | |
| 192 file extension). By default the `default` template is used for all pages. | |
| 193 * `title`: The locale string to be used as page title. By default the `title` | |
| 194 string is used. | |
| 195 * `noheading`: Setting this to any value will make sure no heading is displayed. | |
| 196 By default a `<h1>` tag with the page title is added above the content. | |
| 197 * `notoc`: Setting this to any value will prevent a table of contents from being | |
| 198 generated. By default a table of contents is always generated if the page | |
| 199 contains any headers with an `id` attribute. | |
| 200 | |
| 201 The following tag inserts an include file into the page (can be in a different | |
| 202 format): | |
| 203 | |
| 204 <? include foo ?> | |
| 205 | |
| 206 Include files should be placed into the `includes` directory of the repository. | |
| 207 In the case above the contents of `includes/foo.md` or `includes/foo.tmpl` will | |
| 208 be inserted (whichever is present). | |
| 209 | |
| 210 #### Markdown format (md) #### | |
| 211 | |
| 212 This format should normally be used, it allows the pages to be defined using the | |
| 213 [Markdown](http://daringfireball.net/projects/markdown/syntax) syntax. Raw HTML | |
| 214 tags are allowed and can be used where Markdown syntax isn't sufficient. The | |
| 215 [Python-Markdown Extra](https://pythonhosted.org/Markdown/extensions/extra.html) | |
| 216 extension is active and allows specifying custom attributes for the generated | |
| 217 HTML tags, HTML block elements that contain Markdown and more. | |
| 218 | |
| 219 Any content between `<head>` and `</head>` tags will be inserted into the head | |
| 220 of the generated web page, this is meant for styles, scripts and the like. | |
| 221 Other pages should be linked by using their name as link target (relative links)
, | |
| 222 these links will be resolved to point to the most appropriate page language. | |
| 223 Embedding localizable images works the same, use the image name as image source. | |
| 224 | |
| 225 Localizable strings can be specified inline with the following syntax: | |
| 226 | |
| 227 {{string_name String contents in default language}} | |
| 228 | |
| 229 Try to give the string a descriptive name as it will help translators. | |
| 230 Optionally you can add a description for strings as well to provide even more | |
| 231 context: | |
| 232 | |
| 233 {{string_name[The string description] String contents}} | |
| 234 | |
| 235 _Note: String names and descriptions have no effect on the generated page, | |
| 236 assuming string name is unique to the page. The name and description are just | |
| 237 used to provide translators with additional context about the string._ | |
| 238 | |
| 239 Finally if you find yourself using the same string multiple times in a page | |
| 240 you can reduce duplication by simply omitting the description and contents | |
| 241 after the first use. For example: | |
| 242 | |
| 243 {{title[Title of the page] Adblock Plus - Home}} | |
| 244 {{title}} | |
| 245 | |
| 246 #### Raw HTML format (html) #### | |
| 247 | |
| 248 This format is similar to the Markdown format but uses regular HTML syntax. | |
| 249 No processing is performed beyond inserting localized strings and resolving | |
| 250 links to pages and images. This format is mainly meant for legacy content. | |
| 251 The syntax for localizable strings documented above can be used as with | |
| 252 Markdown. | |
| 253 | |
| 254 #### Jinja2 format (tmpl) #### | |
| 255 | |
| 256 Complicated pages can be defined using the | |
| 257 [Jinja2 template format](http://jinja.pocoo.org/docs/templates/). Automatic | |
| 258 escaping is active so by default values inserted into the page cannot contain | |
| 259 any HTML code. Any content between `<head>` and `</head>` tags will be inserted | |
| 260 into the head of the generated web page, everything else defined the content of | |
| 261 the page. | |
| 262 | |
| 263 The following variables can be used: | |
| 264 | |
| 265 * `page`: The page name | |
| 266 * `config`: Contents of the `settings.ini` file in this repository (a | |
| 267 [configparser object](http://docs.python.org/2/library/configparser.html)) | |
| 268 * `locale`: Locale code of the page language | |
| 269 * `available_locales`: Locale codes of all languages available for this page | |
| 270 | |
| 271 Following custom filters can be used: | |
| 272 | |
| 273 * `translate(default, name, comment=None)`: translates the given default string | |
| 274 and string name for the current page and locale. The string name should be | |
| 275 unique for the page but otherwise is only seen by the translators. Optionally | |
| 276 a comment (description) can be specified to help provide the translators with | |
| 277 additional context. | |
| 278 * `linkify(url, locale=None, **attrs)`: generates an `<a href="...">` tag for | |
| 279 the URL. If the URL is a page name it will be converted into a link to the | |
| 280 most appropriate page language. The language used can also be specified | |
| 281 manually with the locale parameter. Any further keyword arguments passed | |
| 282 are turned into additional HTML attributes for the tag. | |
| 283 * `toclist(html)`: extracts a list of headings from HTML code, this can be used | |
| 284 to generate a table of contents. | |
| 285 | |
| 286 The following global functions can be used: | |
| 287 | |
| 288 * `get_string(name, page=None)`: retrieves a string from a locale file. | |
| 289 Unless a page is specified the locale file matching the name of the current | |
| 290 page is used. | |
| 291 * `get_page_content(page, locale=None)`: returns a dictionary of the content | |
| 292 and params for the given page and locale. Locale defaults to the current one | |
| 293 if not specified. Provided keys include `head`, `body`, `available_locales` | |
| 294 and `translation_ratio`. | |
| 295 | |
| 296 ### Static files ### | |
| 297 | |
| 298 Any files located in the `static` directory will be available on the server | |
| 299 unchanged. The file `static/css/foo.css` will be available under the URL | |
| 300 `/css/foo.css`. | |
| 301 | |
| 302 ### Templates ### | |
| 303 | |
| 304 The templates specified in the `templates` directory specify the overall | |
| 305 structure that is common for all pages. These templates should have the file | |
| 306 extension `tmpl` and use the [Jinja2 template format](http://jinja.pocoo.org/doc
s/templates/). | |
| 307 The differences to pages using the same format are: | |
| 308 | |
| 309 * No special treatment of the `<head>` tag. | |
| 310 * Additional variables `head` and `body` are defined with the HTML code that | |
| 311 should be added to the head and content of the page respectively. | |
| 312 | |
| 313 By default, `default.tmpl` will be used for all pages. If other templates are | |
| 314 defined, the pages need to choose them explicitly using the `template` setting. | |
| 315 | |
| 316 ### Custom filters ### | |
| 317 | |
| 318 The `filters` directory can define custom Jinja2 filters which will be available | |
| 319 in all Jinja2 templates. The file name defines the filter name, e.g. | |
| 320 `filters/myfilter.py` will define a filter named `myfilter`. This file should | |
| 321 also contain a function called `myfilter`, this one will be called when the | |
| 322 filter is invoked. For more information on Jinja2 filters see | |
| 323 [official documentation](http://jinja.pocoo.org/docs/dev/api/#writing-filters). | |
| 324 | |
| 325 ### Custom functions and variables ### | |
| 326 | |
| 327 The `globals` directory can define custom Jinja2 globals which will be available | |
| 328 in all Jinja2 templates. Typically, this is used for custom functions. The file | |
| 329 name should match the name of the function or variable to be defined, and export | |
| 330 a variable with that name. E.g. `globals/myfunction.py` can define a function | |
| 331 called `myfunction` that will become available to all Jinja2 templates. | |
| OLD | NEW |