aboutsummaryrefslogtreecommitdiffhomepage
path: root/doc/md/dev
diff options
context:
space:
mode:
authornodiscc <nodiscc@gmail.com>2020-09-12 12:38:05 +0000
committerGitHub <noreply@github.com>2020-09-12 12:38:05 +0000
commite809908f9e593b2cec11f72849caa1dae6394451 (patch)
treeb26f77ec59b7c25800599d751212db72cfc65870 /doc/md/dev
parent6128ab6a55430a2b705be31ff417c0c552a0db1f (diff)
parent97870f35121bed42ac126652d81bc43416b44356 (diff)
downloadShaarli-e809908f9e593b2cec11f72849caa1dae6394451.tar.gz
Shaarli-e809908f9e593b2cec11f72849caa1dae6394451.tar.zst
Shaarli-e809908f9e593b2cec11f72849caa1dae6394451.zip
Merge pull request #1389 from shaarli/doc-rework-setup
doc: rework installation/setup guides, general refactoring
Diffstat (limited to 'doc/md/dev')
-rw-r--r--doc/md/dev/Development.md179
-rw-r--r--doc/md/dev/GnuPG-signature.md70
-rw-r--r--doc/md/dev/Plugin-system.md758
-rw-r--r--doc/md/dev/Release-Shaarli.md145
-rw-r--r--doc/md/dev/Theming.md86
-rw-r--r--doc/md/dev/Translations.md157
-rw-r--r--doc/md/dev/Unit-tests.md138
-rw-r--r--doc/md/dev/Versioning.md63
-rw-r--r--doc/md/dev/images/poedit-1.jpgbin0 -> 72956 bytes
9 files changed, 1596 insertions, 0 deletions
diff --git a/doc/md/dev/Development.md b/doc/md/dev/Development.md
new file mode 100644
index 00000000..5c085e03
--- /dev/null
+++ b/doc/md/dev/Development.md
@@ -0,0 +1,179 @@
1# Development
2
3Please read [Contributing to Shaarli](https://github.com/shaarli/Shaarli/tree/master/CONTRIBUTING.md)
4
5## Guidelines
6
7
8- [Unit tests](Unit-tests)
9- Javascript linting - Shaarli uses [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript).
10Run `make eslint` to check JS style.
11- [GnuPG signature](GnuPG-signature) for tags/releases
12
13
14## Third-party libraries
15
16CSS:
17
18- Yahoo UI [CSS Reset](http://yuilibrary.com/yui/docs/cssreset/) - standardize cross-browser rendering
19
20Javascript:
21
22- [Awesomeplete](https://leaverou.github.io/awesomplete/) ([GitHub](https://github.com/LeaVerou/awesomplete)) - autocompletion in input forms
23- [bLazy](http://dinbror.dk/blazy/) ([GitHub](https://github.com/dinbror/blazy)) - lazy loading for thumbnails
24- [qr.js](http://neocotic.com/qr.js/) ([GitHub](https://github.com/neocotic/qr.js)) - QR code generation
25
26PHP (managed through [`composer.json`](https://github.com/shaarli/Shaarli/blob/master/composer.json)):
27
28- [RainTPL](https://github.com/rainphp/raintpl) - HTML templating for PHP
29- [`shaarli/netscape-bookmark-parser`](https://packagist.org/packages/shaarli/netscape-bookmark-parser) - Import bookmarks from Netscape files
30- [`erusev/parsedown`](https://packagist.org/packages/erusev/parsedown) - Parse MarkDown syntax for the MarkDown plugin
31- [`slim/slim`](https://packagist.org/packages/slim/slim) - Handle routes and middleware for the REST API
32- [`ArthurHoaro/web-thumbnailer`](https://github.com/ArthurHoaro/web-thumbnailer) - PHP library which will retrieve a thumbnail for any given URL
33- [`pubsubhubbub/publisher`](https://github.com/pubsubhubbub/php-publisher) - A PubSubHubbub publisher module for PHP.
34- [`gettext/gettext`](https://github.com/php-gettext/Gettext) - PHP library to collect and manipulate gettext (.po, .mo, .php, .json, etc)
35
36
37## Security
38
39- The password is salted, hashed and stored in the data subdirectory, in a PHP file, and protected by htaccess. Even if the webserver does not support htaccess, the hash is not readable by URL. Even if the .php file is stolen, the password cannot deduced from the hash. The salt prevents rainbow-tables attacks.
40- Directories are protected using `.htaccess` files
41- Forms are protected against [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery):
42 - Forms which act on data (save,delete…) contain a token generated by the server.
43 - Any posted form which does not contain a valid token is rejected.
44 - Any token can only be used once.
45 - Tokens are attached to the session and cannot be reused in another session.
46- Sessions automatically expire after 60 minutes.
47- Sessions are protected against hijacking: the session ID cannot be used from a different IP address.
48- Links are stored as an associative array which is serialized, compressed (with deflate), base64-encoded and saved as a comment in a `.php` file - even if the server does not support `.htaccess` files, the data file will still not be readable by URL.
49- Bruteforce protection: Successful and failed login attempts are logged - IP bans are enforced after a configurable amount of failures. Logs can also be used consumed by [fail2ban](../Server-configuration.md#fail2ban)
50- A pop-up notification is shown when a new release is available.
51
52## Link structure
53
54Every link available through the `LinkDB` object is represented as an array
55containing the following fields:
56
57 * `id` (integer): Unique identifier.
58 * `title` (string): Title of the link.
59 * `url` (string): URL of the link. Used for displayable links (without redirector, url encoding, etc.).
60 Can be absolute or relative for Notes.
61 * `real_url` (string): Real destination URL, can be redirected, encoded, etc.
62 * `shorturl` (string): Permalink small hash.
63 * `description` (string): Link text description.
64 * `private` (boolean): whether the link is private or not.
65 * `tags` (string): all link tags separated by a single space
66 * `thumbnail` (string|boolean): relative path of the thumbnail cache file, or false if there isn't any.
67 * `created` (DateTime): link creation date time.
68 * `updated` (DateTime): last modification date time.
69
70Small hashes are used to make a link to an entry in Shaarli. They are unique: the date of the item (eg. `20110923_150523`) is hashed with CRC32, then converted to base64 and some characters are replaced. They are always 6 characters longs and use only `A-Z a-z 0-9 - _` and `@`.
71
72
73## Directory structure
74
75Here is the directory structure of Shaarli and the purpose of the different files:
76
77```bash
78 index.php # Main program
79 application/ # Shaarli classes
80 ├── LinkDB.php
81
82 ...
83
84 └── Utils.php
85 tests/ # Shaarli unitary & functional tests
86 ├── LinkDBTest.php
87
88 ...
89
90 ├── utils # utilities to ease testing
91 │ └── ReferenceLinkDB.php
92 └── UtilsTest.php
93 assets/
94 ├── common/ # Assets shared by multiple themes
95 ├── ...
96 ├── default/ # Assets for the default template, before compilation
97 ├── fonts/ # Font files
98 ├── img/ # Images used by the default theme
99 ├── js/ # JavaScript files in ES6 syntax
100 ├── scss/ # SASS files
101 └── vintage/ # Assets for the vintage template, before compilation
102 └── ...
103 COPYING # Shaarli license
104 inc/ # static assets and 3rd party libraries
105 └── rain.tpl.class.php # RainTPL templating library
106 images/ # Images and icons used in Shaarli
107 data/ # data storage: bookmark database, configuration, logs, banlist...
108 ├── config.json.php # Shaarli configuration (login, password, timezone, title...)
109 ├── datastore.php # Your link database (compressed).
110 ├── ipban.php # IP address ban system data
111 ├── lastupdatecheck.txt # Update check timestamp file
112 └── log.txt # login/IPban log.
113 tpl/ # RainTPL templates for Shaarli. They are used to build the pages.
114 ├── default/ # Default Shaarli theme
115 ├── fonts/ # Font files
116 ├── img/ # Images
117 ├── js/ # JavaScript files compiled by Babel and compatible with all browsers
118 ├── css/ # CSS files compiled with SASS
119 └── vintage/ # Legacy Shaarli theme
120 └── ...
121 cache/ # thumbnails cache
122 # This directory is automatically created. You can erase it anytime you want.
123 tmp/ # Temporary directory for compiled RainTPL templates.
124 # This directory is automatically created. You can erase it anytime you want.
125 vendor/ # Third-party dependencies. This directory is created by Composer
126```
127
128Shaarli needs read access to:
129
130- the root index.php file
131- the `application/`, `plugins/` and `inc/` directories (recursively)
132
133Shaarli needs read/write access to the `cache/`, `data/`, `pagecache/`, and `tmp/` directories
134
135
136## Automation
137
138A [`Makefile`](https://github.com/shaarli/Shaarli/blob/master/Makefile) is available to perform project-related operations:
139
140- [Static analysis](#Static-analysis) - check that the code is compliant to PHP conventions
141- [Unit tests](#Unit-tests) - ensure there are no regressions introduced by new commits
142- Documentation - generate a local HTML copy of the markdown documentation
143
144### Continuous Integration
145
146[Travis CI](http://docs.travis-ci.com/) is a Continuous Integration build server, that runs a build:
147
148- each time a commit is merged to the mainline (`master` branch)
149- each time a Pull Request is submitted or updated
150
151After all jobs have finished, Travis returns the results to GitHub:
152
153- a status icon represents the result for the `master` branch: [![](https://api.travis-ci.org/shaarli/Shaarli.svg)](https://travis-ci.org/shaarli/Shaarli)
154- Pull Requests are updated with the Travis build result.
155
156See [`.travis.yml`](https://github.com/shaarli/Shaarli/blob/master/.travis.yml).
157
158
159### Documentation
160
161[mkdocs](https://www.mkdocs.org/) is used to convert markdown documentation to HTML pages. The [public documentation](https://shaarli.readthedocs.io/en/master/) website is rendered and hosted by [readthedocs.org](https://readthedocs.org/). A copy of the documentation is also included in prebuilt [release archives](https://github.com/shaarli/Shaarli/releases) (`doc/html/` path in your Shaarli installation). To generate the HTML documentation locally, install a recent version of Python `setuptools` and run `make doc`.
162
163
164## Static analysis
165
166Patches should try to stick to the [PHP Standard Recommendations](http://www.php-fig.org/psr/) (PSR), especially:
167
168- [PSR-1](http://www.php-fig.org/psr/psr-1/) - Basic Coding Standard
169- [PSR-2](http://www.php-fig.org/psr/psr-2/) - Coding Style Guide
170
171
172**Work in progress:** Static analysis is currently being discussed here: in [#95 - Fix coding style (static analysis)](https://github.com/shaarli/Shaarli/issues/95), [#130 - Continuous Integration tools & features](https://github.com/shaarli/Shaarli/issues/130)
173
174Static analysis tools can be installed with Composer, and used through Shaarli's [Makefile](https://github.com/shaarli/Shaarli/blob/master/Makefile).
175
176For an overview of the available features, see:
177
178- [Code quality: Makefile to run static code checkers](https://github.com/shaarli/Shaarli/pull/124) (#124)
179- [Run PHPCS against different coding standards](https://github.com/shaarli/Shaarli/pull/276) (#276)
diff --git a/doc/md/dev/GnuPG-signature.md b/doc/md/dev/GnuPG-signature.md
new file mode 100644
index 00000000..25578001
--- /dev/null
+++ b/doc/md/dev/GnuPG-signature.md
@@ -0,0 +1,70 @@
1## Introduction
2### PGP and GPG
3[Gnu Privacy Guard](https://gnupg.org/) (GnuPG) is an Open Source implementation of the [Pretty Good Privacy](https://en.wikipedia.org/wiki/Pretty_Good_Privacy#OpenPGP) (OpenPGP) specification. Its main purposes are digital authentication, signature and encryption. It is often used by the [FLOSS](https://en.wikipedia.org/wiki/Free_and_open-source_software) community to verify:
4
5- Linux package signatures: Debian [SecureApt](https://wiki.debian.org/SecureApt), ArchLinux [Master Keys](https://www.archlinux.org/master-keys/)
6- [Version control](https://en.wikipedia.org/wiki/Revision_control) releases & maintainer identity
7
8> You MUST understand that presence of data in the keyserver (pools) in no way connotes trust. Anyone can generate a key, with any name or email address, and upload it. All security and trust comes from evaluating security at the “object level”, via PGP [Web of trust](https://en.wikipedia.org/wiki/Web_of_trust) signatures. This keyserver makes it possible to retrieve keys, looking them up via various indices, but the collection of keys in this public pool is KNOWN to contain malicious and fraudulent keys. It is the common expectation of server operators that users understand this and use software which, like all known common OpenPGP implementations, evaluates trust accordingly. This expectation is so common that it is not normally explicitly stated.
9
10-- Phil Pennock (author of the [SKS](https://bitbucket.org/skskeyserver/sks-keyserver/wiki/Home) key server - http://sks.spodhuis.org/)
11
12Trust can be gained by having your key signed by other people (and signing their key back, too :) ), for instance during [key signing parties](https://en.wikipedia.org/wiki/Key_signing_party): [Keysigning party HOWTO](http://www.cryptnet.net/fdp/crypto/keysigning_party/en/keysigning_party.html),
13
14
15## Generate a GPG key
16- [Generating a GPG key for Git tagging](http://stackoverflow.com/a/16725717) (StackOverflow)
17- [Generating a GPG key](https://help.github.com/articles/generating-a-gpg-key/) (GitHub)
18
19### gpg - provide identity information
20```bash
21$ gpg --gen-key
22
23gpg (GnuPG) 2.1.6; Copyright (C) 2015 Free Software Foundation, Inc.
24This is free software: you are free to change and redistribute it.
25There is NO WARRANTY, to the extent permitted by law.
26
27Note: Use "gpg2 --full-gen-key" for a full featured key generation dialog.
28
29GnuPG needs to construct a user ID to identify your key.
30
31Real name: Marvin the Paranoid Android
32Email address: marvin@h2g2.net
33You selected this USER-ID:
34 "Marvin the Paranoid Android <marvin@h2g2.net>"
35
36Change (N)ame, (E)mail, or (O)kay/(Q)uit? o
37We need to generate a lot of random bytes. It is a good idea to perform
38some other action (type on the keyboard, move the mouse, utilize the
39disks) during the prime generation; this gives the random number
40generator a better chance to gain enough entropy.
41```
42
43### gpg - entropy interlude
44At this point, you will:
45- be prompted for a secure password to protect your key (the input method will depend on your Desktop Environment and configuration)
46- be asked to use your machine's input devices (mouse, keyboard, etc.) to generate random entropy; this step _may take some time_
47
48### gpg - key creation confirmation
49```bash
50gpg: key A9D53A3E marked as ultimately trusted
51public and secret key created and signed.
52
53gpg: checking the trustdb
54gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
55gpg: depth: 0 valid: 2 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 2u
56pub rsa2048/A9D53A3E 2015-07-31
57 Key fingerprint = AF2A 5381 E54B 2FD2 14C4 A9A3 0E35 ACA4 A9D5 3A3E
58uid [ultimate] Marvin the Paranoid Android <marvin@h2g2.net>
59sub rsa2048/8C0EACF1 2015-07-31
60```
61
62### gpg - submit your public key to a PGP server (Optional)
63``` bash
64$ gpg --keyserver pgp.mit.edu --send-keys A9D53A3E
65gpg: sending key A9D53A3E to hkp server pgp.mit.edu
66```
67
68## Create and push a GPG-signed tag
69
70See [Release Shaarli](Release Shaarli).
diff --git a/doc/md/dev/Plugin-system.md b/doc/md/dev/Plugin-system.md
new file mode 100644
index 00000000..a87bd0cf
--- /dev/null
+++ b/doc/md/dev/Plugin-system.md
@@ -0,0 +1,758 @@
1# Plugin system
2
3## Developer API
4
5### What can I do with plugins?
6
7The plugin system lets you:
8
9- insert content into specific places across templates.
10- alter data before templates rendering.
11- alter data before saving new links.
12
13
14### How can I create a plugin for Shaarli?
15
16First, chose a plugin name, such as `demo_plugin`.
17
18Under `plugin` folder, create a folder named with your plugin name. Then create a <plugin_name>.meta file and a <plugin_name>.php file in that folder.
19
20You should have the following tree view:
21
22```
23| index.php
24| plugins/
25|---| demo_plugin/
26| |---| demo_plugin.meta
27| |---| demo_plugin.php
28```
29
30
31### Plugin initialization
32
33At the beginning of Shaarli execution, all enabled plugins are loaded. At this point, the plugin system looks for an `init()` function in the <plugin_name>.php to execute and run it if it exists. This function must be named this way, and takes the `ConfigManager` as parameter.
34
35 <plugin_name>_init($conf)
36
37This function can be used to create initial data, load default settings, etc. But also to set *plugin errors*. If the initialization function returns an array of strings, they will be understand as errors, and displayed in the header to logged in users.
38
39The plugin system also looks for a `description` variable in the <plugin_name>.meta file, to be displayed in the plugin administration page.
40
41 description="The plugin does this and that."
42
43### Understanding hooks
44
45A plugin is a set of functions. Each function will be triggered by the plugin system at certain point in Shaarli execution.
46
47These functions need to be named with this pattern:
48
49```
50hook_<plugin_name>_<hook_name>($data, $conf)
51```
52
53Parameters:
54
55- data: see [$data section](https://shaarli.readthedocs.io/en/master/Plugin-System/#plugins-data)
56- conf: the `ConfigManager` instance.
57
58For example, if my plugin want to add data to the header, this function is needed:
59
60 hook_demo_plugin_render_header
61
62If this function is declared, and the plugin enabled, it will be called every time Shaarli is rendering the header.
63
64
65### Plugin's data
66
67#### Parameters
68
69Every hook function has a `$data` parameter. Its content differs for each hooks.
70
71**This parameter needs to be returned every time**, otherwise data is lost.
72
73 return $data;
74
75#### Special data
76
77Special additional data are passed to every hook through the
78`$data` parameter to give you access to additional context, and services.
79
80Complete list:
81
82 * `_PAGE_` (string): if the current hook is used to render a template, its name is passed through this additional parameter.
83 * `_LOGGEDIN_` (bool): whether the user is logged in or not.
84 * `_BASE_PATH_` (string): if Shaarli instance is hosted under a subfolder, contains the subfolder path to `index.php` (e.g. `https://domain.tld/shaarli/` -> `/shaarli/`).
85 * `_BOOKMARK_SERVICE_` (`BookmarkServiceInterface`): bookmark service instance, for advanced usage.
86
87Example:
88
89```php
90if ($data['_PAGE_'] === TemplatePage::LINKLIST && $data['LOGGEDIN'] === true) {
91 // Do something for logged in users when the link list is rendered
92}
93```
94
95#### Filling templates placeholder
96
97Template placeholders are displayed in template in specific places.
98
99RainTPL displays every element contained in the placeholder's array. These element can be added by plugins.
100
101For example, let's add a value in the placeholder `top_placeholder` which is displayed at the top of my page:
102
103```php
104$data['top_placeholder'][] = 'My content';
105# OR
106array_push($data['top_placeholder'], 'My', 'content');
107
108return $data;
109```
110
111
112#### Data manipulation
113
114When a page is displayed, every variable send to the template engine is passed to plugins before that in `$data`.
115
116The data contained by this array can be altered before template rendering.
117
118For example, in linklist, it is possible to alter every title:
119
120```php
121// mind the reference if you want $data to be altered
122foreach ($data['links'] as &$value) {
123 // String reverse every title.
124 $value['title'] = strrev($value['title']);
125}
126
127return $data;
128```
129
130### Metadata
131
132Every plugin needs a `<plugin_name>.meta` file, which is in fact an `.ini` file (`KEY="VALUE"`), to be listed in plugin administration.
133
134Each file contain two keys:
135
136- `description`: plugin description
137- `parameters`: user parameter names, separated by a `;`.
138- `parameter.<PARAMETER_NAME>`: add a text description the specified parameter.
139
140> Note: In PHP, `parse_ini_file()` seems to want strings to be between by quotes `"` in the ini file.
141
142
143### It's not working!
144
145Use `demo_plugin` as a functional example. It covers most of the plugin system features.
146
147If it's still not working, please [open an issue](https://github.com/shaarli/Shaarli/issues/new).
148
149
150### Hooks
151
152| Hooks | Description |
153| ------------- |:-------------:|
154| [render_header](#render_header) | Allow plugin to add content in page headers. |
155| [render_includes](#render_includes) | Allow plugin to include their own CSS files. |
156| [render_footer](#render_footer) | Allow plugin to add content in page footer and include their own JS files. |
157| [render_linklist](#render_linklist) | It allows to add content at the begining and end of the page, after every link displayed and to alter link data. |
158| [render_editlink](#render_editlink) | Allow to add fields in the form, or display elements. |
159| [render_tools](#render_tools) | Allow to add content at the end of the page. |
160| [render_picwall](#render_picwall) | Allow to add content at the top and bottom of the page. |
161| [render_tagcloud](#render_tagcloud) | Allow to add content at the top and bottom of the page, and after all tags. |
162| [render_taglist](#render_taglist) | Allow to add content at the top and bottom of the page, and after all tags. |
163| [render_daily](#render_daily) | Allow to add content at the top and bottom of the page, the bottom of each link and to alter data. |
164| [render_feed](#render_feed) | Allow to do add tags in RSS and ATOM feeds. |
165| [save_link](#save_link) | Allow to alter the link being saved in the datastore. |
166| [delete_link](#delete_link) | Allow to do an action before a link is deleted from the datastore. |
167| [save_plugin_parameters](#save_plugin_parameters) | Allow to manipulate plugin parameters before they're saved. |
168
169
170#### render_header
171
172Triggered on every page - allows plugins to add content in page headers.
173
174
175##### Data
176
177`$data` is an array containing:
178
179 - [Special data](#special-data)
180
181##### Template placeholders
182
183Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
184
185List of placeholders:
186
187- `buttons_toolbar`: after the list of buttons in the header.
188
189![buttons_toolbar_example](http://i.imgur.com/ssJUOrt.png)
190
191- `fields_toolbar`: after search fields in the header.
192
193> Note: This will only be called in linklist.
194
195![fields_toolbar_example](http://i.imgur.com/3GMifI2.png)
196
197
198#### render_includes
199
200Triggered on every page - allows plugins to include their own CSS files.
201
202##### data
203
204`$data` is an array containing:
205
206 - [Special data](#special-data)
207
208##### Template placeholders
209
210Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
211
212List of placeholders:
213
214- `css_files`: called after loading default CSS.
215
216> Note: only add the path of the CSS file. E.g: `plugins/demo_plugin/custom_demo.css`.
217
218
219#### render_footer
220
221Triggered on every page.
222
223Allow plugin to add content in page footer and include their own JS files.
224
225##### data
226
227`$data` is an array containing:
228
229 - [Special data](#special-data)
230
231##### Template placeholders
232
233Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
234
235List of placeholders:
236
237- `text`: called after the end of the footer text.
238- `endofpage`: called at the end of the page.
239
240![text_example](http://i.imgur.com/L5S2YEH.png)
241
242- `js_files`: called at the end of the page, to include custom JS scripts.
243
244> Note: only add the path of the JS file. E.g: `plugins/demo_plugin/custom_demo.js`.
245
246
247#### render_linklist
248
249Triggered when `linklist` is displayed (list of links, permalink, search, tag filtered, etc.).
250
251It allows to add content at the begining and end of the page, after every link displayed and to alter link data.
252
253##### data
254
255`$data` is an array containing:
256
257 - All templates data, including links.
258 - [Special data](#special-data)
259
260##### template placeholders
261
262Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
263
264List of placeholders:
265
266- `action_plugin`: next to the button "private only" at the top and bottom of the page.
267
268![action_plugin_example](http://i.imgur.com/Q12PWg0.png)
269
270- `link_plugin`: for every link, between permalink and link URL.
271
272![link_plugin_example](http://i.imgur.com/3oDPhWx.png)
273
274- `plugin_start_zone`: before displaying the template content.
275
276![plugin_start_zone_example](http://i.imgur.com/OVBkGy3.png)
277
278- `plugin_end_zone`: after displaying the template content.
279
280![plugin_end_zone_example](http://i.imgur.com/6IoRuop.png)
281
282
283#### render_editlink
284
285Triggered when the link edition form is displayed.
286
287Allow to add fields in the form, or display elements.
288
289##### data
290
291`$data` is an array containing:
292
293 - All templates data.
294 - [Special data](#special-data)
295
296##### template placeholders
297
298Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
299
300List of placeholders:
301
302- `edit_link_plugin`: after tags field.
303
304![edit_link_plugin_example](http://i.imgur.com/5u17Ens.png)
305
306
307#### render_tools
308
309Triggered when the "tools" page is displayed.
310
311Allow to add content at the end of the page.
312
313##### data
314
315`$data` is an array containing:
316
317 - All templates data.
318 - [Special data](#special-data)
319
320##### template placeholders
321
322Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
323
324List of placeholders:
325
326- `tools_plugin`: at the end of the page.
327
328![tools_plugin_example](http://i.imgur.com/Bqhu9oQ.png)
329
330
331#### render_picwall
332
333Triggered when picwall is displayed.
334
335Allow to add content at the top and bottom of the page.
336
337##### data
338
339`$data` is an array containing:
340
341 - All templates data.
342 - [Special data](#special-data)
343
344##### template placeholders
345
346Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
347
348List of placeholders:
349
350- `plugin_start_zone`: before displaying the template content.
351- `plugin_end_zone`: after displaying the template content.
352
353![plugin_start_end_zone_example](http://i.imgur.com/tVTQFER.png)
354
355
356#### render_tagcloud
357
358Triggered when tagcloud is displayed.
359
360Allow to add content at the top and bottom of the page.
361
362##### data
363
364`$data` is an array containing:
365
366 - All templates data.
367 - [Special data](#special-data)
368
369##### Template placeholders
370
371Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
372
373List of placeholders:
374
375- `plugin_start_zone`: before displaying the template content.
376- `plugin_end_zone`: after displaying the template content.
377
378For each tag, the following placeholder can be used:
379
380- `tag_plugin`: after each tag
381
382![plugin_start_end_zone_example](http://i.imgur.com/vHmyT3a.png)
383
384
385#### render_taglist
386
387Triggered when taglist is displayed - allows to add content at the top and bottom of the page.
388
389##### data
390
391`$data` is an array containing:
392
393 - All templates data.
394 - [Special data](#special-data)
395
396##### Template placeholders
397
398Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
399
400List of placeholders:
401
402- `plugin_start_zone`: before displaying the template content.
403- `plugin_end_zone`: after displaying the template content.
404
405For each tag, the following placeholder can be used:
406
407- `tag_plugin`: after each tag
408
409#### render_daily
410
411Triggered when tagcloud is displayed.
412
413Allow to add content at the top and bottom of the page, the bottom of each link and to alter data.
414
415
416##### data
417
418`$data` is an array containing:
419
420 - All templates data, including links.
421 - [Special data](#special-data)
422
423##### Template placeholders
424
425Items can be displayed in templates by adding an entry in `$data['<placeholder>']` array.
426
427List of placeholders:
428
429- `link_plugin`: used at bottom of each link.
430
431![link_plugin_example](http://i.imgur.com/hzhMfSZ.png)
432
433- `plugin_start_zone`: before displaying the template content.
434- `plugin_end_zone`: after displaying the template content.
435
436
437#### render_feed
438
439Triggered when the ATOM or RSS feed is displayed.
440
441Allow to add tags in the feed, either in the header or for each items. Items (links) can also be altered before being rendered.
442
443##### data
444
445`$data` is an array containing:
446
447 - All templates data, including links.
448 - [Special data](#special-data)
449
450##### Template placeholders
451
452Tags can be added in feeds by adding an entry in `$data['<placeholder>']` array.
453
454List of placeholders:
455
456- `feed_plugins_header`: used as a header tag in the feed.
457
458For each links:
459
460- `feed_plugins`: additional tag for every link entry.
461
462
463#### save_link
464
465Triggered when a link is save (new link or edit).
466
467Allow to alter the link being saved in the datastore.
468
469##### data
470
471`$data` is an array containing the link being saved:
472
473- id
474- title
475- url
476- shorturl
477- description
478- private
479- tags
480- created
481- updated
482
483Also [special data](#special-data).
484
485
486#### delete_link
487
488Triggered when a link is deleted.
489
490Allow to execute any action before the link is actually removed from the datastore
491
492##### data
493
494`$data` is an array containing the link being deleted:
495
496- id
497- title
498- url
499- shorturl
500- description
501- private
502- tags
503- created
504- updated
505
506Also [special data](#special-data).
507
508#### save_plugin_parameters
509
510Triggered when the plugin parameters are saved from the plugin administration page.
511
512Plugins can perform an action every times their settings are updated.
513For example it is used to update the CSS file of the `default_colors` plugins.
514
515##### data
516
517`$data` input contains the `$_POST` array.
518
519So if the plugin has a parameter called `MYPLUGIN_PARAMETER`,
520the array will contain an entry with `MYPLUGIN_PARAMETER` as a key.
521
522Also [special data](#special-data).
523
524## Guide for template designers
525
526### Plugin administration
527
528Your theme must include a plugin administration page: `pluginsadmin.html`.
529
530> Note: repo's template link needs to be added when the PR is merged.
531
532Use the default one as an example.
533
534Aside from classic RainTPL loops, plugins order is handle by JavaScript. You can just include `plugin_admin.js`, only if:
535
536- you're using a table.
537- you call orderUp() and orderUp() onclick on arrows.
538- you add data-line and data-order to your rows.
539
540Otherwise, you can use your own JS as long as this field is send by the form:
541
542<input type="hidden" name="order_{$key}" value="{$counter}">
543
544### Placeholder system
545
546In order to make plugins work with every custom themes, you need to add variable placeholder in your templates.
547
548It's a RainTPL loop like this:
549
550 {loop="$plugin_variable"}
551 {$value}
552 {/loop}
553
554You should enable `demo_plugin` for testing purpose, since it uses every placeholder available.
555
556### List of placeholders
557
558**page.header.html**
559
560At the end of the menu:
561
562 {loop="$plugins_header.buttons_toolbar"}
563 {$value}
564 {/loop}
565
566At the end of file, before clearing floating blocks:
567
568 {if="!empty($plugin_errors) && $is_logged_in"}
569 <ul class="errors">
570 {loop="plugin_errors"}
571 <li>{$value}</li>
572 {/loop}
573 </ul>
574 {/if}
575
576**includes.html**
577
578At the end of the file:
579
580```html
581{loop="$plugins_includes.css_files"}
582<link type="text/css" rel="stylesheet" href="{$value}#"/>
583{/loop}
584```
585
586**page.footer.html**
587
588At the end of your footer notes:
589
590```html
591{loop="$plugins_footer.text"}
592 {$value}
593{/loop}
594```
595
596At the end of file:
597
598```html
599{loop="$plugins_footer.js_files"}
600 <script src="{$value}#"></script>
601{/loop}
602```
603
604**linklist.html**
605
606After search fields:
607
608```html
609{loop="$plugins_header.fields_toolbar"}
610 {$value}
611{/loop}
612```
613
614Before displaying the link list (after paging):
615
616```html
617{loop="$plugin_start_zone"}
618 {$value}
619{/loop}
620```
621
622For every links (icons):
623
624```html
625{loop="$value.link_plugin"}
626 <span>{$value}</span>
627{/loop}
628```
629
630Before end paging:
631
632```html
633{loop="$plugin_end_zone"}
634 {$value}
635{/loop}
636```
637
638**linklist.paging.html**
639
640After the "private only" icon:
641
642```html
643{loop="$action_plugin"}
644 {$value}
645{/loop}
646```
647
648**editlink.html**
649
650After tags field:
651
652```html
653{loop="$edit_link_plugin"}
654 {$value}
655{/loop}
656```
657
658**tools.html**
659
660After the last tool:
661
662```html
663{loop="$tools_plugin"}
664 {$value}
665{/loop}
666```
667
668**picwall.html**
669
670Top:
671
672```html
673<div id="plugin_zone_start_picwall" class="plugin_zone">
674 {loop="$plugin_start_zone"}
675 {$value}
676 {/loop}
677</div>
678```
679
680Bottom:
681
682```html
683<div id="plugin_zone_end_picwall" class="plugin_zone">
684 {loop="$plugin_end_zone"}
685 {$value}
686 {/loop}
687</div>
688```
689
690**tagcloud.html**
691
692Top:
693
694```html
695 <div id="plugin_zone_start_tagcloud" class="plugin_zone">
696 {loop="$plugin_start_zone"}
697 {$value}
698 {/loop}
699 </div>
700```
701
702Bottom:
703
704```html
705 <div id="plugin_zone_end_tagcloud" class="plugin_zone">
706 {loop="$plugin_end_zone"}
707 {$value}
708 {/loop}
709 </div>
710```
711
712**daily.html**
713
714Top:
715
716```html
717<div id="plugin_zone_start_picwall" class="plugin_zone">
718 {loop="$plugin_start_zone"}
719 {$value}
720 {/loop}
721</div>
722```
723
724After every link:
725
726```html
727<div class="dailyEntryFooter">
728 {loop="$link.link_plugin"}
729 {$value}
730 {/loop}
731</div>
732```
733
734Bottom:
735
736```html
737<div id="plugin_zone_end_picwall" class="plugin_zone">
738 {loop="$plugin_end_zone"}
739 {$value}
740 {/loop}
741</div>
742```
743
744**feed.atom.xml** and **feed.rss.xml**:
745
746In headers tags section:
747```xml
748{loop="$feed_plugins_header"}
749 {$value}
750{/loop}
751```
752
753After each entry:
754```xml
755{loop="$value.feed_plugins"}
756 {$value}
757{/loop}
758```
diff --git a/doc/md/dev/Release-Shaarli.md b/doc/md/dev/Release-Shaarli.md
new file mode 100644
index 00000000..2c772406
--- /dev/null
+++ b/doc/md/dev/Release-Shaarli.md
@@ -0,0 +1,145 @@
1# Release Shaarli
2
3## Requirements
4
5This guide assumes that you have:
6
7- a GPG key matching your GitHub authentication credentials/email (the email address identified by the GPG key is the same as the one in your `~/.gitconfig`)
8- a GitHub fork of Shaarli
9- a local clone of your Shaarli fork, with the following remotes:
10 - `origin` pointing to your GitHub fork
11 - `upstream` pointing to the main Shaarli repository
12- maintainer permissions on the main Shaarli repository, to:
13 - push the signed tag
14 - create a new release
15- [Composer](https://getcomposer.org/) needs to be installed
16- The [venv](https://docs.python.org/3/library/venv.html) Python 3 module needs to be installed for HTML documentation generation.
17
18## Release notes and `CHANGELOG.md`
19
20GitHub allows drafting the release notes for the upcoming release, from the [Releases](https://github.com/shaarli/Shaarli/releases) page. This way, the release note can be drafted while contributions are merged to `master`. See http://keepachangelog.com/en/0.3.0/ for changelog formatting.
21
22`CHANGELOG.md` should contain the same information as the release note draft for the upcoming version. Update it to:
23
24- add new entries (additions, fixes, etc.)
25- mark the current version as released by setting its date and link
26- add a new section for the future unreleased version
27
28```bash
29## [v0.x.y](https://github.com/shaarli/Shaarli/releases/tag/v0.x.y) - UNRELEASES
30
31### Added
32
33### Changed
34
35### Fixed
36
37### Removed
38
39### Deprecated
40
41### Security
42
43```
44
45
46## Update the list of Git contributors
47
48```bash
49$ make authors
50$ git commit -s -m "Update AUTHORS"
51```
52
53## Create and merge a Pull Request
54
55Create a Pull Request to marge changes from your remote, into `master` in the community Shaarli repository, and have it merged.
56
57
58## Create the release branch and update shaarli_version.php
59
60```bash
61# fetch latest changes from master to your local copy
62git checkout master
63git pull upstream master
64
65# If releasing a new minor version, create a release branch
66$ git checkout -b v0.x
67
68# Bump shaarli_version.php from dev to 0.x.0, **without the v**
69$ vim shaarli_version.php
70$ git add shaarli_version
71$ git commit -s -m "Bump Shaarli version to v0.x.0"
72$ git push upstream v0.x
73```
74
75## Create and push a signed tag
76
77Git [tags](http://git-scm.com/book/en/v2/Distributed-Git-Maintaining-a-Project#Tagging-Your-Releases) are used to identify specific revisions with a unique version number that follows [semantic versioning](https://semver.org/)
78
79```bash
80# update your local copy
81git checkout v0.5
82git pull upstream v0.5
83
84# create a signed tag
85git tag -s -m "Release v0.5.0" v0.5.0
86
87# push the tag to upstream
88git push --tags upstream
89```
90
91Here is how to verify a signed tag. [`v0.5.0`](https://github.com/shaarli/Shaarli/releases/tag/v0.5.0) is the first GPG-signed tag pushed on the Community Shaarli. Let's have a look at its signature!
92
93```bash
94# update the list of available tags
95git fetch upstream
96
97# get the SHA1 reference of the tag
98git show-ref tags/v0.5.0
99# gives: f7762cf803f03f5caf4b8078359a63783d0090c1 refs/tags/v0.5.0
100
101# verify the tag signature information
102git verify-tag f7762cf803f03f5caf4b8078359a63783d0090c1
103# gpg: Signature made Thu 30 Jul 2015 11:46:34 CEST using RSA key ID 4100DF6F
104# gpg: Good signature from "VirtualTam <virtualtam@flibidi.net>" [ultimate]
105```
106
107## Publish the GitHub release
108
109- In the `master` banch, update version badges in `README.md` to point to the newly released Shaarli version
110- Update the previously drafted [release](https://github.com/shaarli/Shaarli/releases) (notes, tag) and publish it
111- Profit!
112
113
114## Generate full release zip archives
115
116Release archives will contain Shaarli code plus all required third-party libraries. They are useful for users who:
117
118- have no SSH access, no possibility to install PHP packages/server extensions, no possibility to run scripts (shared hosting)
119- do not want to install build/dev dependencies on their server
120
121 `git checkout` the appropriate branch, then:
122
123```bash
124# checkout the appropriate branch
125git checkout 0.x.y
126# generate zip archives
127make release_archive
128```
129
130This will create `shaarli-v0.x.y-full.tar`, `shaarli-v0.x.y-full.zip`. These archives need to be manually uploaded on the previously created GitHub [release](https://github.com/shaarli/Shaarli/releases).
131
132
133### Update the `latest` branch
134
135```bash
136# checkout the 'latest' branch
137git checkout latest
138# merge changes from your newly published release branch
139git merge v0.x.y
140# fix eventual conflicts with git mergetool...
141# run tests
142make test
143# push the latest branch
144git push upstream latest
145```
diff --git a/doc/md/dev/Theming.md b/doc/md/dev/Theming.md
new file mode 100644
index 00000000..1ad30465
--- /dev/null
+++ b/doc/md/dev/Theming.md
@@ -0,0 +1,86 @@
1# Theming
2
3## Foreword
4
5There are two ways of customizing how Shaarli looks:
6
71. by using a custom CSS to override Shaarli's CSS
82. by using a full theme that provides its own RainTPL templates, CSS and Javascript resources
9
10## Custom CSS
11
12Shaarli's appearance can be modified by adding CSS rules to:
13
14- Shaarli < `v0.9.0`: `inc/user.css`
15- Shaarli >= `v0.9.0`: `data/user.css`
16
17This file allows overriding rules defined in the template CSS files (only add changed rules), or define a whole new theme.
18
19**Note**: Do not edit `tpl/default/css/shaarli.css`! Your changes would be overridden when updating Shaarli.
20
21## Themes
22
23Installation:
24
25- find a theme you'd like to install
26- copy or clone the theme folder under `tpl/<a_sweet_theme>`
27- enable the theme:
28 - Shaarli < `v0.9.0`: edit `data/config.json.php` and set the value of `raintpl_tpl` to the new theme name:
29 `"raintpl_tpl": "tpl\/my-template\/"`
30 - Shaarli >= `v0.9.0`: select the theme through the _Tools_ page
31
32## Community CSS & themes
33
34### Custom CSS
35
36- [mrjovanovic/serious-theme-shaarli](https://github.com/mrjovanovic/serious-theme-shaarli) - A serious theme for Shaarli
37- [shaarli/shaarli-themes](https://github.com/shaarli/shaarli-themes)
38
39### Themes
40
41- [AkibaTech/Shaarli Superhero Theme](https://github.com/AkibaTech/Shaarli---SuperHero-Theme) - A template/theme for Shaarli
42- [alexisju/albinomouse-template](https://github.com/alexisju/albinomouse-template) - A full template for Shaarli
43- [ArthurHoaro/shaarli-launch](https://github.com/ArthurHoaro/shaarli-launch) - Customizable Shaarli theme
44- [dhoko/ShaarliTemplate](https://github.com/dhoko/ShaarliTemplate) - A template/theme for Shaarli
45- [kalvn/shaarli-blocks](https://github.com/kalvn/shaarli-blocks) - A template/theme for Shaarli
46- [kalvn/Shaarli-Material](https://github.com/kalvn/Shaarli-Material) - A theme (template) based on Google's Material Design for Shaarli, the superfast delicious clone
47- [ManufacturaInd/shaarli-2004licious-theme](https://github.com/ManufacturaInd/shaarli-2004licious-theme) - A template/theme as a humble homage to the early looks of the del.icio.us site
48- [xfnw/shaarli-default-dark](https://github.com/xfnw/shaarli-default-dark) - The default theme but nice and dark for your eyeballs
49
50### Shaarli forks
51
52- [misterair/Limonade](https://github.com/misterair/limonade) - A fork of (legacy) Shaarli with a new template
53- [vivienhaese/shaarlitheme](https://github.com/vivienhaese/shaarlitheme) - A Shaarli fork meant to be run in an openshift instance
54
55## Example installation: AlbinoMouse theme
56
57With the following configuration:
58
59- Apache 2 / PHP 5.6
60- user sites are enabled, e.g. `/home/user/public_html/somedir` is served as `http://localhost/~user/somedir`
61- `http` is the name of the Apache user
62
63```bash
64$ cd ~/public_html
65
66# clone repositories
67$ git clone https://github.com/shaarli/Shaarli.git shaarli
68$ pushd shaarli/tpl
69$ git clone https://github.com/alexisju/albinomouse-template.git
70$ popd
71
72# set access rights for Apache
73$ chgrp -R http shaarli
74$ chmod g+rwx shaarli shaarli/cache shaarli/data shaarli/pagecache shaarli/tmp
75```
76
77Get config written:
78- go to the freshly installed site
79- fill the install form
80- log in to Shaarli
81
82Edit Shaarli's [configuration](Shaarli-configuration):
83```bash
84# the file should be owned by Apache, thus not writeable => sudo
85$ sudo sed -i s=tpl=tpl/albinomouse-template=g shaarli/data/config.php
86```
diff --git a/doc/md/dev/Translations.md b/doc/md/dev/Translations.md
new file mode 100644
index 00000000..8f3b8f10
--- /dev/null
+++ b/doc/md/dev/Translations.md
@@ -0,0 +1,157 @@
1## Translations
2
3Shaarli supports [gettext](https://www.gnu.org/software/gettext/manual/gettext.html) translations
4since `>= v0.9.2`.
5
6Note that only the `default` theme supports translations.
7
8### Contributing
9
10We encourage the community to contribute to Shaarli translations, either by improving existing translations or submitting a new language.
11
12Contributing to the translation does not require software development knowledge.
13
14Please submit a pull request with the `.po` file updated/created. Note that the compiled file (`.mo`) is not stored on the repository, and is generated during the release process.
15
16
17### How to
18
19Install [Poedit](https://poedit.net/) (used to extract strings to translate from the PHP source code, and generate `.po` files).
20
21Due to the usage of a template engine, it's important to generate PHP cache files to extract every translatable string. You can either use [this script](https://gist.github.com/ArthurHoaro/5d0323f758ab2401ef444a53f54e9a07) (recommended) or visit every template page in your browser to generate cache files, while logged in. Here is a list :
22
23```
24http://<replace_domain>/
25http://<replace_domain>/login
26http://<replace_domain>/daily
27http://<replace_domain>/tags/cloud
28http://<replace_domain>/tags/list
29http://<replace_domain>/picture-wall
30http://<replace_domain>/?nonope
31http://<replace_domain>/admin/add-shaare
32http://<replace_domain>/admin/password
33http://<replace_domain>/admin/tags
34http://<replace_domain>/admin/configure
35http://<replace_domain>/admin/tools
36http://<replace_domain>/admin/shaare
37http://<replace_domain>/admin/export
38http://<replace_domain>/admin/import
39http://<replace_domain>/admin/plugins
40```
41
42
43#### Improve existing translations
44
45- In Poedit, click on "Edit a Translation
46- Open `inc/languages/<lang>/LC_MESSAGES/shaarli.po` under Shaarli's directory
47- The existing list of translatable strings should load
48- Click on the "Update" button.
49- Start editing translations.
50
51![poedit-screenshot](images/poedit-1.jpg)
52
53Save when you're done, then you can submit a pull request containing the updated `shaarli.po`.
54
55
56#### Add a new language
57
58- In Poedit select "Create New Translation"
59- Open `inc/languages/<lang>/LC_MESSAGES/shaarli.po` under Shaarli's directory
60- Select the language you want to create.
61- Click on `File > Save as...`, save your file in `<shaarli directory>/inc/language/<new language>/LC_MESSAGES/shaarli.po` (`<new language>` here should be the language code respecting the [ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-2) format in lowercase - e.g. `de` for German)
62- Click on the "Update" button
63- Start editing translations.
64
65Save when you're done, then you can submit a pull request containing the new `shaarli.po`.
66
67
68### Theme translations
69
70[Theme](Theming) translation extensions are loaded automatically if they're present.
71
72As a theme developer, all you have to do is to add the `.po` and `.mo` compiled file like this:
73
74```
75tpl/<theme name>/language/<lang>/LC_MESSAGES/<theme name>.po
76tpl/<theme name>/language/<lang>/LC_MESSAGES/<theme name>.mo
77```
78
79Where `<lang>` is the ISO 3166-1 alpha-2 language code.
80
81Read the following section "Extend Shaarli's translation" to learn how to generate those files.
82
83
84### Extend Shaarli's translation
85
86If you're writing a custom theme, or a non official plugin, you might want to use the translation system,
87but you won't be able to able to override Shaarli's translation.
88
89However, you can add your own translation domain which extends the main translation list.
90
91> Note that you can find a live example of translation extension in the `demo_plugin`.
92
93First, create your translation files tree directory:
94
95```
96<your_module>/languages/<ISO 3166-1 alpha-2 language code>/LC_MESSAGES/
97```
98
99Your `.po` files must be named like your domain. E.g. if your translation domain is `my_theme`, then your file will be
100`my_theme.po`.
101
102Users have to register your extension in their configuration with the parameter
103`translation.extensions.<domain>: <translation files path>`.
104
105Example:
106
107```php
108if (! $conf->exists('translation.extensions.my_theme')) {
109 $conf->set('translation.extensions.my_theme', '<your_module>/languages/');
110 $conf->write(true);
111}
112```
113
114> Note that the page needs to be reloaded after the registration.
115
116It is then recommended to create a custom translation function which will call the `t()` function with your domain.
117For example :
118
119```php
120function my_theme_t($text, $nText = '', $nb = 1)
121{
122 return t($text, $nText, $nb, 'my_theme'); // the last parameter is your translation domain.
123}
124```
125
126All strings which can be translated should be processed through your function:
127
128```php
129my_theme_t('Comment');
130my_theme_t('Comment', 'Comments', 2);
131```
132
133Or in templates:
134
135```php
136{'Comment'|my_theme_t}
137{function="my_theme_t('Comment', 'Comments', 2)"}
138```
139
140> Note than in template, you need to visit your page at least once to generate a cache file.
141
142When you're done, open Poedit and load translation strings from sources:
143
144 1. `File > New`
145 2. Choose your language
146 3. Save your `PO` file in `<your_module>/languages/<language code>/LC_MESSAGES/my_theme.po`.
147 4. Go to `Catalog > Properties...`
148 5. Fill the `Translation Properties` tab
149 6. Add your source path in the `Sources Paths` tab
150 7. In the `Sources Keywords` tab uncheck "Also use default keywords" and add the following lines:
151
152```
153my_theme_t
154my_theme_t:1,2
155```
156
157Click on the "Update" button and you're free to start your translations!
diff --git a/doc/md/dev/Unit-tests.md b/doc/md/dev/Unit-tests.md
new file mode 100644
index 00000000..25af82d7
--- /dev/null
+++ b/doc/md/dev/Unit-tests.md
@@ -0,0 +1,138 @@
1# Unit tests
2
3Shaarli uses the [PHPUnit](https://phpunit.de/) test framework; it can be installed with [Composer](https://getcomposer.org/), which is a dependency management tool.
4
5## Install composer
6
7You can either use:
8
9- a system-wide version, e.g. installed through your distro's package manager
10- a local version, downloadable [here](https://getcomposer.org/download/).
11
12```bash
13# system-wide version
14$ composer install
15$ composer update
16
17# local version
18$ php composer.phar self-update
19$ php composer.phar install
20$ php composer.phar update
21```
22
23## Install Shaarli dev dependencies
24
25```bash
26$ cd /path/to/shaarli
27$ composer update
28```
29
30## Install and enable Xdebug to generate PHPUnit coverage reports
31
32
33[Xdebug](http://xdebug.org/docs/install) is a PHP extension which provides debugging and profiling capabilities. Install Xdebug:
34
35```bash
36# for Debian-based distros:
37sudo aptitude install php5-xdebug
38
39# for ArchLinux:
40pacman -S xdebug
41
42# then add the following line to /etc/php/php.ini
43zend_extension=xdebug.so
44```
45
46## Run unit tests
47
48Ensure tests pass successuflly:
49
50```bash
51make test
52# ...
53# OK (36 tests, 65 assertions)
54```
55
56In case of failure the test suite will point you to actual errors and output a summary:
57
58```bash
59make test
60# ...
61# FAILURES!
62# Tests: 36, Assertions: 63, Errors: 1, Failures: 2.
63```
64
65By default, PHPUnit will run all suitable tests found under the `tests` directory. Each test has 3 possible outcomes:
66
67- `.` - success
68- `F` - failure: the test was run but its results are invalid
69 - the code does not behave as expected
70 - dependencies to external elements: globals, session, cache...
71- `E` - error: something went wrong and the tested code has crashed
72 - typos in the code, or in the test code
73 - dependencies to missing external elements
74
75If Xdebug has been installed and activated, two coverage reports will be generated:
76
77- a summary in the console
78- a detailed HTML report with metrics for tested code
79 - to open it in a web browser: `firefox coverage/index.html &`
80
81
82### Executing specific tests
83
84Add a [`@group`](https://phpunit.de/manual/current/en/appendixes.annotations.html#appendixes.annotations.group) annotation in a test class or method comment:
85
86```php
87/**
88 * Netscape bookmark import
89 * @group WIP
90 */
91class BookmarkImportTest extends PHPUnit_Framework_TestCase
92{
93 [...]
94}
95```
96
97To run all tests annotated with `@group WIP`:
98```bash
99$ vendor/bin/phpunit --group WIP tests/
100```
101
102## Running tests inside Docker containers
103
104Unit tests can be run inside [Docker](../Docker.md) containers.
105
106Test Dockerfiles are located under `tests/docker/<distribution>/Dockerfile`, and can be used to build Docker images to run Shaarli test suites under commonLinux environments. Dockerfiles are provided for the following environments:
107
108- [`alpine36`](https://github.com/shaarli/Shaarli/blob/master/tests/docker/alpine36/Dockerfile) - [Alpine Linux 3.6](https://www.alpinelinux.org/downloads/)
109- [`debian8`](https://github.com/shaarli/Shaarli/blob/master/tests/docker/debian8/Dockerfile) - [Debian 8 Jessie](https://www.debian.org/DebianJessie) (oldoldstable)
110- [`debian9`](https://github.com/shaarli/Shaarli/blob/master/tests/docker/debian9/Dockerfile) - [Debian 9 Stretch](https://wiki.debian.org/DebianStretch) (oldstable)
111- [`ubuntu16`](https://github.com/shaarli/Shaarli/blob/master/tests/docker/ubuntu16/Dockerfile) - [Ubuntu 16.04 Xenial Xerus](http://releases.ubuntu.com/16.04/) (old LTS)
112
113Each image provides:
114- a base Linux OS
115- Shaarli PHP dependencies (OS packages)
116- test PHP dependencies (OS packages)
117- Composer
118- Tests that run inside the conatiner using a standard Linux user account (running tests as `root` would bypass permission checks and may hide issues)
119
120Build a test image:
121
122```bash
123# build the Debian 9 Docker image
124cd /path/to/shaarli/tests/docker/debian9
125docker build -t shaarli-test:debian9 .
126```
127
128Run unit tests in a container:
129
130```bash
131cd /path/to/shaarli
132# install/update 3rd-party test dependencies
133composer install --prefer-dist
134# run tests using the freshly built image
135docker run -v $PWD:/shaarli shaarli-test:debian9 docker_test
136# run the full test campaign
137docker run -v $PWD:/shaarli shaarli-test:debian9 docker_all_tests
138```
diff --git a/doc/md/dev/Versioning.md b/doc/md/dev/Versioning.md
new file mode 100644
index 00000000..32c80a5c
--- /dev/null
+++ b/doc/md/dev/Versioning.md
@@ -0,0 +1,63 @@
1# Versioning
2
3If you're maintaining a 3rd party tool for Shaarli (theme, plugin, etc.), It's important to understand how Shaarli branches work ensure your tool stays compatible.
4
5
6## `master` branch
7
8The `master` branch is the development branch. Any new change MUST go through this branch using Pull Requests.
9
10Remarks:
11
12- This branch shouldn't be used for production as it isn't necessary stable.
13- 3rd party aren't required to be compatible with the latest changes.
14- Official plugins, themes and libraries (contained within Shaarli organization repos) must be compatible with the master branch.
15
16
17## `v0.x` branch
18
19The `v0.x` branch points to the latest `v0.x.y` release.
20
21If a major bug affects the original `v0.x.0` release, we may [backport](https://en.wikipedia.org/wiki/Backporting) a fix for this bug from master, to the `v0.x` branch, and create a new bugfix release (eg. `v0.x.1`) from this branch.
22
23This allows users of the original release to upgrade to the fixed version, without having to upgrade to a completely new minor/major release.
24
25
26## `latest` branch
27
28This branch point the latest release. It recommended to use it to get the latest tested changes.
29
30
31## Releases
32
33For every release, we manually generate a .zip file which contains all Shaarli dependencies, making Shaarli's installation only one step.
34
35
36## Advices on 3rd party git repos workflow
37
38### Versioning
39
40Any time a new Shaarli release is published, you should publish a new release of your repo if the changes affected you since the latest release (take a look at the [changelog](https://github.com/shaarli/Shaarli/releases) (*Draft* means not released yet) and the commit log (like [`tpl` folder](https://github.com/shaarli/Shaarli/commits/master/tpl/default) for themes)). You can either:
41
42 - use the Shaarli version number, with your repo version. For example, if Shaarli `v0.8.3` is released, publish a `v0.8.3-1` release, where `v0.8.3` states Shaarli compatibility and `-1` is your own version digit for the current Shaarli version.
43 - use your own versioning scheme, and state Shaarli compatibility in the release description.
44
45Using this, any user will be able to pick the release matching his own Shaarli version.
46
47### Major bugfix backport releases
48
49To be able to support backported fixes, it recommended to use our workflow:
50
51```bash
52# In master, fix the major bug
53git commit -m "Katastrophe"
54git push origin master
55# Get your commit hash
56git log --format="%H" -n 1
57# Create a new branch from your latest release, let's say v0.8.2-1 (the tag name)
58git checkout -b katastrophe v0.8.2-1
59# Backport the fix commit to your brand new branch
60git cherry-pick <fix commit hash>
61git push origin katastrophe
62# Then you just have to make a new release from the `katastrophe` branch tagged `v0.8.3-1`
63```
diff --git a/doc/md/dev/images/poedit-1.jpg b/doc/md/dev/images/poedit-1.jpg
new file mode 100644
index 00000000..673ae6d6
--- /dev/null
+++ b/doc/md/dev/images/poedit-1.jpg
Binary files differ