├── .gitignore
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── admin
├── chorus.md
├── commands.md
├── config.md
├── docker.md
└── moderation.md
├── developer
├── database.md
└── setup.md
├── start.md
└── writer
├── additional-blogs.md
├── autosave.md
├── css.md
├── display-format.md
├── drafts.md
├── excerpt.md
├── federation.md
├── following.md
├── hashtags.md
├── html.md
├── publicity.md
├── scheduling.md
├── static.md
└── writing.md
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Documentation
2 |
3 | WriteFreely documentation aims to communicate how WriteFreely works in an approachable way that doesn't waste readers' time. It should be plain, concise, and follow the general style we've developed for these guides.
4 |
5 | ## General notes
6 |
7 | ### Prioritize clarity
8 |
9 | These guides should seek to convey a depth of information, but not in a meandering or rambling way. Remove unnecessary words, and leave out fluff that doesn't teach the reader anything. Avoid excessively conversational writing. Speak plainly, and use active voice instead of passive.
10 |
11 | ### Specific audience
12 |
13 | Avoid generalizations, and be precise about who you're speaking to. In each article, there should only be a single type of user you're referring to as "you." If you use this pronoun in any given article, use it consistently, and then choose unambiguous terms to describe the different people outside of the relative "you." Always choose specific terms for "users" before reverting to the more generic term. In order of priority:
14 |
15 | 1. The _kind_ of user (e.g. "admin", "writer", "reader", "subscriber", "follower", "commenter")
16 | 2. "User"
17 |
18 | ### Specific steps
19 |
20 | Be specific about what you're instructing the reader to do, and explain actionable steps in unambiguous terms. For example:
21 |
22 | * ✔ **Good**: "Click the Share button, then copy the address in the window that appears."
23 | * ❌ No: "Copy the link."
24 | * ✔ **Good**: "Click the Reader checkbox to enable it." Or "Toggle the Reader checkbox." Or "Enable the Reader checkbox."
25 | * ❌ No: "Turn it on."
26 |
27 | ### Self-contained sections
28 |
29 | Remember the reader who wants to jump into the article section most important to them, skim the material, and then get out of here. Support them by making each section stand on its own as much as possible.
30 |
31 | ### Technical terms
32 |
33 | To keep our guides accessible to technical and non-technical readers alike, always aim for non-technical terminology, only reverting to technical jargon when absolutely necessary (this should be rare). Common examples:
34 |
35 | * "address" instead of "URL"
36 | * "site" or "website" instead of "instance"
37 | * "writer," "reader," etc. instead of "user"
38 |
39 | ## Style
40 |
41 | ### Abbreviations
42 |
43 | Aim to avoid abbreviations as much as possible, unless they're commonly known. When using abbreviations, expand unknown abbreviations at their first encounter, e.g.
44 |
45 | * ✔ **Good**: We support ActivityPub (AP) mentions. AP is a social web protocol.
46 |
47 | ### Links
48 |
49 | Link text should describe the nature of the linked document (e.g. "Admin guide" instead of "click here"). Do not include terminal punctuation in the link text. Aim for shorter links over longer ones.
50 |
51 | ### Images
52 |
53 | All included images should include `alt` text that describes the image for those who cannot see it.
54 |
55 | ### Code and technical terms
56 |
57 | For bits of source code, variable names, and typed text (such as configuration values), surround the text with a single backtick (\`
).
58 |
59 | For a full line or more of source code or configuration text, surround the text with three consecutive backticks on each end (\`\`\`
), with the backticks living on their own lines. If this _code block_ contains source code, include the language name immediately following the first three backticks to enable syntax highlighting (e.g. \`\`\`go
).
60 |
61 | ### UI elements
62 |
63 | User interface elements should be _italicized_.
64 |
65 | * ✔ **Good**: Scroll down and press _Save Changes_.
66 | * ❌ No: Scroll down and press "Save Changes."
67 | * ✔ **Good**: Press the _Publish_ button.
68 | * ❌ No: Press "Publish."
69 |
70 | Likewise, application sections / pages should be italicized when referred to as part of the UI.
71 |
72 | * ✔ **Good**: Next, click on _Blogs_ to go to the Blogs page.
73 |
74 | Otherwise, application sections / pages should simply be capitalized as proper nouns.
75 |
76 | * ✔ **Good**: Next, go to the Blogs page.
77 |
78 | ### Punctuation
79 |
80 | * **Commas** - use the [serial / Oxford comma](https://en.wikipedia.org/wiki/Serial_comma).
81 | * **Ampersand** - only use the ampersand in titles, if you use them at all.
82 | * **Em dashes** - insert em dashes as two hypens (`--`) surrounded by one space on each side. They will be converted to proper em dashes by our Markdown parser.
83 | * **Quotations** - insert a double prime character (`"`) instead of "curly" (or "smart") quotation marks. Position punctuation according to Chicago rules (periods on the inside, etc.).
84 |
85 | ## Contributing
86 |
87 | Once you've finished writing, you'll want to commit your changes and open a pull request on GitHub.
88 |
89 | ### Commit messages
90 |
91 | We highly value commit messages that follow established form within the project. See our [WriteFreely commit guidelines](https://github.com/writeas/writefreely/blob/develop/CONTRIBUTING.md#commit-messages) for best practices.
92 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Attribution-NonCommercial-ShareAlike 4.0 International
2 |
3 | =======================================================================
4 |
5 | Creative Commons Corporation ("Creative Commons") is not a law firm and
6 | does not provide legal services or legal advice. Distribution of
7 | Creative Commons public licenses does not create a lawyer-client or
8 | other relationship. Creative Commons makes its licenses and related
9 | information available on an "as-is" basis. Creative Commons gives no
10 | warranties regarding its licenses, any material licensed under their
11 | terms and conditions, or any related information. Creative Commons
12 | disclaims all liability for damages resulting from their use to the
13 | fullest extent possible.
14 |
15 | Using Creative Commons Public Licenses
16 |
17 | Creative Commons public licenses provide a standard set of terms and
18 | conditions that creators and other rights holders may use to share
19 | original works of authorship and other material subject to copyright
20 | and certain other rights specified in the public license below. The
21 | following considerations are for informational purposes only, are not
22 | exhaustive, and do not form part of our licenses.
23 |
24 | Considerations for licensors: Our public licenses are
25 | intended for use by those authorized to give the public
26 | permission to use material in ways otherwise restricted by
27 | copyright and certain other rights. Our licenses are
28 | irrevocable. Licensors should read and understand the terms
29 | and conditions of the license they choose before applying it.
30 | Licensors should also secure all rights necessary before
31 | applying our licenses so that the public can reuse the
32 | material as expected. Licensors should clearly mark any
33 | material not subject to the license. This includes other CC-
34 | licensed material, or material used under an exception or
35 | limitation to copyright. More considerations for licensors:
36 | wiki.creativecommons.org/Considerations_for_licensors
37 |
38 | Considerations for the public: By using one of our public
39 | licenses, a licensor grants the public permission to use the
40 | licensed material under specified terms and conditions. If
41 | the licensor's permission is not necessary for any reason--for
42 | example, because of any applicable exception or limitation to
43 | copyright--then that use is not regulated by the license. Our
44 | licenses grant only permissions under copyright and certain
45 | other rights that a licensor has authority to grant. Use of
46 | the licensed material may still be restricted for other
47 | reasons, including because others have copyright or other
48 | rights in the material. A licensor may make special requests,
49 | such as asking that all changes be marked or described.
50 | Although not required by our licenses, you are encouraged to
51 | respect those requests where reasonable. More considerations
52 | for the public:
53 | wiki.creativecommons.org/Considerations_for_licensees
54 |
55 | =======================================================================
56 |
57 | Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
58 | Public License
59 |
60 | By exercising the Licensed Rights (defined below), You accept and agree
61 | to be bound by the terms and conditions of this Creative Commons
62 | Attribution-NonCommercial-ShareAlike 4.0 International Public License
63 | ("Public License"). To the extent this Public License may be
64 | interpreted as a contract, You are granted the Licensed Rights in
65 | consideration of Your acceptance of these terms and conditions, and the
66 | Licensor grants You such rights in consideration of benefits the
67 | Licensor receives from making the Licensed Material available under
68 | these terms and conditions.
69 |
70 |
71 | Section 1 -- Definitions.
72 |
73 | a. Adapted Material means material subject to Copyright and Similar
74 | Rights that is derived from or based upon the Licensed Material
75 | and in which the Licensed Material is translated, altered,
76 | arranged, transformed, or otherwise modified in a manner requiring
77 | permission under the Copyright and Similar Rights held by the
78 | Licensor. For purposes of this Public License, where the Licensed
79 | Material is a musical work, performance, or sound recording,
80 | Adapted Material is always produced where the Licensed Material is
81 | synched in timed relation with a moving image.
82 |
83 | b. Adapter's License means the license You apply to Your Copyright
84 | and Similar Rights in Your contributions to Adapted Material in
85 | accordance with the terms and conditions of this Public License.
86 |
87 | c. BY-NC-SA Compatible License means a license listed at
88 | creativecommons.org/compatiblelicenses, approved by Creative
89 | Commons as essentially the equivalent of this Public License.
90 |
91 | d. Copyright and Similar Rights means copyright and/or similar rights
92 | closely related to copyright including, without limitation,
93 | performance, broadcast, sound recording, and Sui Generis Database
94 | Rights, without regard to how the rights are labeled or
95 | categorized. For purposes of this Public License, the rights
96 | specified in Section 2(b)(1)-(2) are not Copyright and Similar
97 | Rights.
98 |
99 | e. Effective Technological Measures means those measures that, in the
100 | absence of proper authority, may not be circumvented under laws
101 | fulfilling obligations under Article 11 of the WIPO Copyright
102 | Treaty adopted on December 20, 1996, and/or similar international
103 | agreements.
104 |
105 | f. Exceptions and Limitations means fair use, fair dealing, and/or
106 | any other exception or limitation to Copyright and Similar Rights
107 | that applies to Your use of the Licensed Material.
108 |
109 | g. License Elements means the license attributes listed in the name
110 | of a Creative Commons Public License. The License Elements of this
111 | Public License are Attribution, NonCommercial, and ShareAlike.
112 |
113 | h. Licensed Material means the artistic or literary work, database,
114 | or other material to which the Licensor applied this Public
115 | License.
116 |
117 | i. Licensed Rights means the rights granted to You subject to the
118 | terms and conditions of this Public License, which are limited to
119 | all Copyright and Similar Rights that apply to Your use of the
120 | Licensed Material and that the Licensor has authority to license.
121 |
122 | j. Licensor means the individual(s) or entity(ies) granting rights
123 | under this Public License.
124 |
125 | k. NonCommercial means not primarily intended for or directed towards
126 | commercial advantage or monetary compensation. For purposes of
127 | this Public License, the exchange of the Licensed Material for
128 | other material subject to Copyright and Similar Rights by digital
129 | file-sharing or similar means is NonCommercial provided there is
130 | no payment of monetary compensation in connection with the
131 | exchange.
132 |
133 | l. Share means to provide material to the public by any means or
134 | process that requires permission under the Licensed Rights, such
135 | as reproduction, public display, public performance, distribution,
136 | dissemination, communication, or importation, and to make material
137 | available to the public including in ways that members of the
138 | public may access the material from a place and at a time
139 | individually chosen by them.
140 |
141 | m. Sui Generis Database Rights means rights other than copyright
142 | resulting from Directive 96/9/EC of the European Parliament and of
143 | the Council of 11 March 1996 on the legal protection of databases,
144 | as amended and/or succeeded, as well as other essentially
145 | equivalent rights anywhere in the world.
146 |
147 | n. You means the individual or entity exercising the Licensed Rights
148 | under this Public License. Your has a corresponding meaning.
149 |
150 |
151 | Section 2 -- Scope.
152 |
153 | a. License grant.
154 |
155 | 1. Subject to the terms and conditions of this Public License,
156 | the Licensor hereby grants You a worldwide, royalty-free,
157 | non-sublicensable, non-exclusive, irrevocable license to
158 | exercise the Licensed Rights in the Licensed Material to:
159 |
160 | a. reproduce and Share the Licensed Material, in whole or
161 | in part, for NonCommercial purposes only; and
162 |
163 | b. produce, reproduce, and Share Adapted Material for
164 | NonCommercial purposes only.
165 |
166 | 2. Exceptions and Limitations. For the avoidance of doubt, where
167 | Exceptions and Limitations apply to Your use, this Public
168 | License does not apply, and You do not need to comply with
169 | its terms and conditions.
170 |
171 | 3. Term. The term of this Public License is specified in Section
172 | 6(a).
173 |
174 | 4. Media and formats; technical modifications allowed. The
175 | Licensor authorizes You to exercise the Licensed Rights in
176 | all media and formats whether now known or hereafter created,
177 | and to make technical modifications necessary to do so. The
178 | Licensor waives and/or agrees not to assert any right or
179 | authority to forbid You from making technical modifications
180 | necessary to exercise the Licensed Rights, including
181 | technical modifications necessary to circumvent Effective
182 | Technological Measures. For purposes of this Public License,
183 | simply making modifications authorized by this Section 2(a)
184 | (4) never produces Adapted Material.
185 |
186 | 5. Downstream recipients.
187 |
188 | a. Offer from the Licensor -- Licensed Material. Every
189 | recipient of the Licensed Material automatically
190 | receives an offer from the Licensor to exercise the
191 | Licensed Rights under the terms and conditions of this
192 | Public License.
193 |
194 | b. Additional offer from the Licensor -- Adapted Material.
195 | Every recipient of Adapted Material from You
196 | automatically receives an offer from the Licensor to
197 | exercise the Licensed Rights in the Adapted Material
198 | under the conditions of the Adapter's License You apply.
199 |
200 | c. No downstream restrictions. You may not offer or impose
201 | any additional or different terms or conditions on, or
202 | apply any Effective Technological Measures to, the
203 | Licensed Material if doing so restricts exercise of the
204 | Licensed Rights by any recipient of the Licensed
205 | Material.
206 |
207 | 6. No endorsement. Nothing in this Public License constitutes or
208 | may be construed as permission to assert or imply that You
209 | are, or that Your use of the Licensed Material is, connected
210 | with, or sponsored, endorsed, or granted official status by,
211 | the Licensor or others designated to receive attribution as
212 | provided in Section 3(a)(1)(A)(i).
213 |
214 | b. Other rights.
215 |
216 | 1. Moral rights, such as the right of integrity, are not
217 | licensed under this Public License, nor are publicity,
218 | privacy, and/or other similar personality rights; however, to
219 | the extent possible, the Licensor waives and/or agrees not to
220 | assert any such rights held by the Licensor to the limited
221 | extent necessary to allow You to exercise the Licensed
222 | Rights, but not otherwise.
223 |
224 | 2. Patent and trademark rights are not licensed under this
225 | Public License.
226 |
227 | 3. To the extent possible, the Licensor waives any right to
228 | collect royalties from You for the exercise of the Licensed
229 | Rights, whether directly or through a collecting society
230 | under any voluntary or waivable statutory or compulsory
231 | licensing scheme. In all other cases the Licensor expressly
232 | reserves any right to collect such royalties, including when
233 | the Licensed Material is used other than for NonCommercial
234 | purposes.
235 |
236 |
237 | Section 3 -- License Conditions.
238 |
239 | Your exercise of the Licensed Rights is expressly made subject to the
240 | following conditions.
241 |
242 | a. Attribution.
243 |
244 | 1. If You Share the Licensed Material (including in modified
245 | form), You must:
246 |
247 | a. retain the following if it is supplied by the Licensor
248 | with the Licensed Material:
249 |
250 | i. identification of the creator(s) of the Licensed
251 | Material and any others designated to receive
252 | attribution, in any reasonable manner requested by
253 | the Licensor (including by pseudonym if
254 | designated);
255 |
256 | ii. a copyright notice;
257 |
258 | iii. a notice that refers to this Public License;
259 |
260 | iv. a notice that refers to the disclaimer of
261 | warranties;
262 |
263 | v. a URI or hyperlink to the Licensed Material to the
264 | extent reasonably practicable;
265 |
266 | b. indicate if You modified the Licensed Material and
267 | retain an indication of any previous modifications; and
268 |
269 | c. indicate the Licensed Material is licensed under this
270 | Public License, and include the text of, or the URI or
271 | hyperlink to, this Public License.
272 |
273 | 2. You may satisfy the conditions in Section 3(a)(1) in any
274 | reasonable manner based on the medium, means, and context in
275 | which You Share the Licensed Material. For example, it may be
276 | reasonable to satisfy the conditions by providing a URI or
277 | hyperlink to a resource that includes the required
278 | information.
279 | 3. If requested by the Licensor, You must remove any of the
280 | information required by Section 3(a)(1)(A) to the extent
281 | reasonably practicable.
282 |
283 | b. ShareAlike.
284 |
285 | In addition to the conditions in Section 3(a), if You Share
286 | Adapted Material You produce, the following conditions also apply.
287 |
288 | 1. The Adapter's License You apply must be a Creative Commons
289 | license with the same License Elements, this version or
290 | later, or a BY-NC-SA Compatible License.
291 |
292 | 2. You must include the text of, or the URI or hyperlink to, the
293 | Adapter's License You apply. You may satisfy this condition
294 | in any reasonable manner based on the medium, means, and
295 | context in which You Share Adapted Material.
296 |
297 | 3. You may not offer or impose any additional or different terms
298 | or conditions on, or apply any Effective Technological
299 | Measures to, Adapted Material that restrict exercise of the
300 | rights granted under the Adapter's License You apply.
301 |
302 |
303 | Section 4 -- Sui Generis Database Rights.
304 |
305 | Where the Licensed Rights include Sui Generis Database Rights that
306 | apply to Your use of the Licensed Material:
307 |
308 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right
309 | to extract, reuse, reproduce, and Share all or a substantial
310 | portion of the contents of the database for NonCommercial purposes
311 | only;
312 |
313 | b. if You include all or a substantial portion of the database
314 | contents in a database in which You have Sui Generis Database
315 | Rights, then the database in which You have Sui Generis Database
316 | Rights (but not its individual contents) is Adapted Material,
317 | including for purposes of Section 3(b); and
318 |
319 | c. You must comply with the conditions in Section 3(a) if You Share
320 | all or a substantial portion of the contents of the database.
321 |
322 | For the avoidance of doubt, this Section 4 supplements and does not
323 | replace Your obligations under this Public License where the Licensed
324 | Rights include other Copyright and Similar Rights.
325 |
326 |
327 | Section 5 -- Disclaimer of Warranties and Limitation of Liability.
328 |
329 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
330 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
331 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
332 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
333 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
334 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
335 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
336 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
337 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
338 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
339 |
340 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
341 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
342 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
343 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
344 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
345 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
346 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
347 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
348 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
349 |
350 | c. The disclaimer of warranties and limitation of liability provided
351 | above shall be interpreted in a manner that, to the extent
352 | possible, most closely approximates an absolute disclaimer and
353 | waiver of all liability.
354 |
355 |
356 | Section 6 -- Term and Termination.
357 |
358 | a. This Public License applies for the term of the Copyright and
359 | Similar Rights licensed here. However, if You fail to comply with
360 | this Public License, then Your rights under this Public License
361 | terminate automatically.
362 |
363 | b. Where Your right to use the Licensed Material has terminated under
364 | Section 6(a), it reinstates:
365 |
366 | 1. automatically as of the date the violation is cured, provided
367 | it is cured within 30 days of Your discovery of the
368 | violation; or
369 |
370 | 2. upon express reinstatement by the Licensor.
371 |
372 | For the avoidance of doubt, this Section 6(b) does not affect any
373 | right the Licensor may have to seek remedies for Your violations
374 | of this Public License.
375 |
376 | c. For the avoidance of doubt, the Licensor may also offer the
377 | Licensed Material under separate terms or conditions or stop
378 | distributing the Licensed Material at any time; however, doing so
379 | will not terminate this Public License.
380 |
381 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
382 | License.
383 |
384 |
385 | Section 7 -- Other Terms and Conditions.
386 |
387 | a. The Licensor shall not be bound by any additional or different
388 | terms or conditions communicated by You unless expressly agreed.
389 |
390 | b. Any arrangements, understandings, or agreements regarding the
391 | Licensed Material not stated herein are separate from and
392 | independent of the terms and conditions of this Public License.
393 |
394 |
395 | Section 8 -- Interpretation.
396 |
397 | a. For the avoidance of doubt, this Public License does not, and
398 | shall not be interpreted to, reduce, limit, restrict, or impose
399 | conditions on any use of the Licensed Material that could lawfully
400 | be made without permission under this Public License.
401 |
402 | b. To the extent possible, if any provision of this Public License is
403 | deemed unenforceable, it shall be automatically reformed to the
404 | minimum extent necessary to make it enforceable. If the provision
405 | cannot be reformed, it shall be severed from this Public License
406 | without affecting the enforceability of the remaining terms and
407 | conditions.
408 |
409 | c. No term or condition of this Public License will be waived and no
410 | failure to comply consented to unless expressly agreed to by the
411 | Licensor.
412 |
413 | d. Nothing in this Public License constitutes or may be interpreted
414 | as a limitation upon, or waiver of, any privileges and immunities
415 | that apply to the Licensor or You, including from the legal
416 | processes of any jurisdiction or authority.
417 |
418 | =======================================================================
419 |
420 | Creative Commons is not a party to its public
421 | licenses. Notwithstanding, Creative Commons may elect to apply one of
422 | its public licenses to material it publishes and in those instances
423 | will be considered the “Licensor.” The text of the Creative Commons
424 | public licenses is dedicated to the public domain under the CC0 Public
425 | Domain Dedication. Except for the limited purpose of indicating that
426 | material is shared under a Creative Commons public license or as
427 | otherwise permitted by the Creative Commons policies published at
428 | creativecommons.org/policies, Creative Commons does not authorize the
429 | use of the trademark "Creative Commons" or any other trademark or logo
430 | of Creative Commons without its prior written consent including,
431 | without limitation, in connection with any unauthorized modifications
432 | to any of its public licenses or any other arrangements,
433 | understandings, or agreements concerning use of licensed material. For
434 | the avoidance of doubt, this paragraph does not form part of the
435 | public licenses.
436 |
437 | Creative Commons may be contacted at creativecommons.org.
438 |
439 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | ---
4 |
5 | [WriteFreely](https://writefreely.org) is a beautifully pared-down blogging platform that's simple on the surface, yet powerful underneath.
6 |
7 | This repository contains all documentation for running and using WriteFreely.
8 |
9 | ## Format
10 |
11 | All documentation is written in plain text / Markdown. It generates [documentation pages](https://writefreely.org/docs) on WriteFreely.org.
12 |
13 | ## Contributing
14 |
15 | Contributions are welcome from everyone, whether that's in the form of new documentation, suggestions for what to include, or any fixes / updates.
16 |
17 | ## Releases
18 |
19 | Documentation on `master` is kept up-to-date with the [writefreely `master` branch](https://github.com/writeas/writefreely). When a new WriteFreely release is made, the documentation should be up-to-date with it and tagged with a version number that matches the writefreely repo.
20 |
--------------------------------------------------------------------------------
/admin/chorus.md:
--------------------------------------------------------------------------------
1 | # Chorus Mode
2 |
3 | _This is an experimental feature._
4 |
5 | Chorus Mode reconfigures the WriteFreely user interface to be optimized for the _group_, instead of the _individual_. It's perfect for writing groups and company instances, where the purpose is to share knowledge collectively, instead of on separate, distinct blogs.
6 |
7 | ### Status
8 |
9 | This functionality is in a half-baked state, where it solves a certain need, but still needs to be fully fleshed-out. There are plenty of directions it could go in still, but we need more input and experimentation to figure out which one makes the most sense. Please [share any feedback](https://discuss.write.as/t/chorus-mode/2944) you have, if you use this feature!
10 |
11 | ## Effects
12 |
13 | Chorus Mode changes the navigation of the instance to stay grounded in the entire instance, rather than individual blogs. For example, blogs and blog posts maintain the site-wide navigation at the top of the page, instead of featuring a single blog's personal branding there.
14 |
15 | It overrides the `landing` config setting and makes the **Reader** the default view for all users, so they'll always land there when first arriving on the instance, whether logged in or not.
16 |
17 | Hashtags also filter posts collectively, showing posts from _across the instance_ all using the same hashtag when clicked, instead of only showing posts from a single blog.
18 |
19 | ## Uses
20 |
21 | ### Organizations
22 |
23 | Chorus Mode is perfect for creating an internal knowledge-sharing space within your organization. For example, the Write.as team uses Chorus Mode on their internal WriteFreely instance, where team members share proposals, strategy, and general updates with the rest of the team.
24 |
25 | Start with these `[app]` configuration options for your organization's WriteFreely instance:
26 |
27 | | Field | Value |
28 | | ----- | ----- |
29 | | `single_user` | `false` |
30 | | `chorus` | `true` |
31 | | `private` | `true` |
32 |
--------------------------------------------------------------------------------
/admin/commands.md:
--------------------------------------------------------------------------------
1 | # Admin Commands
2 |
3 | The following application commands allow administrators to perform certain actions on their instance, including installing, upgrading, or maintaining it.
4 |
5 | ## Options
6 |
7 | These options can be used in conjunction with any other flags.
8 |
9 | | Flag | Description |
10 | | ---- | ----------- |
11 | | `-c [filename]` | Config file to use with any other operation |
12 | | `--debug` | Output debug information in application logs |
13 | | `-h` | Output help for any command |
14 |
15 | ## Setup
16 |
17 | Use these flags to perform certain actions as part of the setup process.
18 |
19 | | Command | Description | Interactive? |
20 | | ------- | ----------- | ------------ |
21 | | `config start` | Start the configuration process | Yes |
22 | | `keys generate` | Generate encryption keys | No |
23 | | `db init` | Initialize the database by creating the necessary tables | No |
24 |
25 | For example, run these commands in order to set up your instance:
26 |
27 | ```
28 | writefreely config start
29 | writefreely keys generate
30 | ```
31 |
32 | ### Setup options
33 |
34 | #### `--config --sections="..."`
35 |
36 | You can optionally choose which configuration sections to walk through during the configuration process with the `--sections` flag. Values are space-separated and must be one of the following:
37 |
38 | * `app`
39 | * `db`
40 | * `server`
41 |
42 | Example usage:
43 |
44 | ```
45 | writefreely --config --sections="app db server"
46 | ```
47 |
48 | ## Upgrade
49 |
50 | These flags assist with upgrading an instance.
51 |
52 | | Command | Description |
53 | | ------- | ----------- |
54 | | `db migrate` | Migrate database schema to the latest version |
55 |
56 | ## User administration
57 |
58 | Use these flags to perform actions around users.
59 |
60 | | Command | Description | Interactive? |
61 | | ------- | ----------- | ------------ |
62 | | `user create --admin [username]:[password]` | Create an admin user in the database. Fails if admin already exists. | No |
63 | | `user create [username]:[password]` | Create a regular user in the database. Fails if no admin user exists yet. | No |
64 | | `user reset-pass [username]` | Reset the given user's password | Yes |
65 | | `user delete [username]` | Delete the given user, after confirming interactively | Yes |
66 |
67 | ## Miscellaneous
68 |
69 | | Command | Description |
70 | | ------- | ----------- |
71 | | `-v` | Print WriteFreely version information |
72 |
--------------------------------------------------------------------------------
/admin/config.md:
--------------------------------------------------------------------------------
1 | # Configuring WriteFreely
2 |
3 | WriteFreely is configured through an `.ini` file. By default, WriteFreely will look for the configuration file `config.ini` in the current directory. However, you can supply a different file or location by running WriteFreely with the `-c [filename]` flag, for example:
4 |
5 | ```
6 | writefreely -c /var/lib/writefreely/config.ini
7 | ```
8 |
9 | ## Server
10 |
11 | The following fields are valid in the `[server]` section of your configuration file. They affect how the application runs.
12 |
13 | | Field | Description | Default |
14 | | ----- | ----------- | ------- |
15 | | `port` | Port for the application to serve HTTP requests on | _None_ |
16 | | `bind` | Address to bind the application to | localhost |
17 | | `tls_cert_path` | TLS certificate path. If supplied with `tls_key_path`, requests will be served on port 443. If `autocert` is `true`, certificates and keys will be stored in the given directory. | _None_ |
18 | | `tls_key_path` | TLS private key path. If supplied with `tls_cert_path`, requests will be served on port 443. | _None_ |
19 | | `autocert` | Enable automatic certificate generation with Let's Encrypt. Requires `tls_cert_path` and `tls_key_path` to not be empty, and running in standalone server mode, i.e. `port` set to `443`. | `false` |
20 | | `gopher_port` | Port to run Gopher server on. If this is `0`, Gopher access will be disabled. | 0 |
21 | | `templates_parent_dir` | The parent directory containing the `templates` directory | _(current directory)_ |
22 | | `static_parent_dir` | The parent directory containing the `static` directory | _(current directory)_ |
23 | | `pages_parent_dir` | The parent directory containing the `pages` directory | _(current directory)_ |
24 | | `keys_parent_dir` | The parent directory containing the `keys` directory | _(current directory)_ |
25 |
26 | ## Database
27 |
28 | The following fields are valid in the `[database]` section of your configuration file. They affect how the application stores and retrieves data.
29 |
30 | | Field | Description | Default |
31 | | ----- | ----------- | ------- |
32 | | `type` | Database driver type. Valid choices: `mysql` or `sqlite3` | _None_ |
33 |
34 | These fields only apply to instances using **MySQL**.
35 |
36 | | Field | Description | Default |
37 | | ----- | ----------- | ------- |
38 | | `username` | Database username | _None_ |
39 | | `password` | Database password | _None_ |
40 | | `database` | Database name | _None_ |
41 | | `host` | Database hostname to connect to | localhost |
42 | | `port` | Database host port to connect to | 3306 |
43 | | `tls` | Whether or not to use TLS to connect to database | false |
44 |
45 | These fields only apply to instances using **SQLite**.
46 |
47 | | Field | Description | Default |
48 | | ----- | ----------- | ------- |
49 | | `filename` | Database file | _None_ |
50 |
51 | ## App
52 |
53 | The following fields are valid in the `[app]` section of your configuration file. They affect how the application functions, especially in user-facing ways.
54 |
55 | | Field | Description | Example value |
56 | | ----- | ----------- | ------- |
57 | | `site_name` | Name of the website, publicly shown in several places | Our Community |
58 | | `site_description` | Site description, shown in NodeInfo | A place to write freely. |
59 | | `host` | Full hostname (including scheme) users will see | https://pencil.writefree.ly |
60 | | `editor` | The editor template file all users will use. Default: `pad`, other options: `bare`, `classic`. | classic |
61 | | `single_user` | Whether or not the instance is for one blog | false |
62 | | `min_username_len` | Minimum required length of usernames | 3 |
63 | | `federation` | Whether or not federation via ActivityPub is enabled | true |
64 | | `notes_only` | By default, WriteFreely federates posts as [`Article`](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-article)s and [`Note`](https://www.w3.org/TR/activitystreams-vocabulary/#dfn-note)s. This only sends them as `Note`s. | false |
65 | | `public_stats` | Whether or not usage stats are made public via NodeInfo | true |
66 | | `monetization` | Enables monetization options for users (currently, only [Web Monetization](http://webmonetization.org/)) | true |
67 | | `disable_password_auth` | Whether or not new users can sign up via the landing page. Useful if you want to limit new users to your OAuth provider. | false |
68 | | `wf_modesty` | When enabled, this removes much of the WriteFreely branding and explanatory text | false |
69 |
70 | These fields can always be set, but only apply to **multi-user** instances.
71 |
72 | | Field | Description | Example value |
73 | | ----- | ----------- | ------- |
74 | | `private` | When enabled, all blogs and posts will only be readable by other authenticated users on the instance. | false |
75 | | `landing` | The default landing route for an unauthenticated user | /login |
76 | | `open_registration` | Whether or not anyone can register via the landing page | true |
77 | | `open_deletion` | Whether or not users can delete their account on their own (via web UI) | true |
78 | | `max_blogs` | Maximum number of blogs a single user can create under one account | 5 |
79 | | `local_timeline` | Whether or not the instance reader (and the _Public_ option on blogs) is enabled | true |
80 | | `user_invites` | Who is allowed to send user invites, if anyone. A blank value disables invites for all users. Valid choices: _empty_, `user`, or `admin` | user |
81 | | `default_visibility` | The default visibility setting for newly-created blogs. Valid choices: `unlisted` (default), `public`, or `private` | public |
82 |
83 | These fields are experimental and subject to change with little notice. _Using these? Let us know so we can make them a permanent part of WriteFreely!_
84 |
85 | | Field | Description | Example value |
86 | | ----- | ----------- | ------- |
87 | | `forest` | When enabled, certain unimportant details ("trees") are hidden from admin view, such as the Application Monitor. Meant for managed hosting environments. | false |
88 | | `chorus` | Indicates this instance is optimized for everyone's collective voice, rather than individuals. Meant for writing groups, company instances, etc. See Chorus docs for more info. | false |
89 |
90 | ## Email
91 |
92 | Configure the `[email]` section to enable password resets and other email-based functionality. Today, WriteFreely only supports [Mailgun](https://mailgun.com) as a sending provider.
93 |
94 | | Field | Description | Example value |
95 | |-------------------|----------------------------|--------------------|
96 | | `domain` | Domain to send emails from | writeasletters.com |
97 | | `mailgun_private` | Mailgun private key | _key-..._ |
98 |
99 | With both configuration values set, your instance will have the following email-based functionality automatically enabled:
100 |
101 | * **Forgot Password functionality**. Users will find a link on the _login_ page that allows them to reset their password via email.
102 | * **Email subscriptions / newsletters**. Blog authors can optionally let readers subscribe to their blog posts via email, and optionally supply an email address where readers can privately reply. Authors will also have access to their subscribers list.
103 |
104 | ## OAuth
105 |
106 | There are several possible OAuth configuration blocks for different implementations.
107 |
108 | ### Generic OAuth
109 |
110 | The following fields are valid in the `[oauth.generic]` section of your configuration file, which is for the most general OAuth setup that should work with many spec-compliant OAuth providers.
111 |
112 | | Field | Description | Example value |
113 | | ----- | ----------- | ------- |
114 | | `client_id` | The client ID, or client key, associated with WriteFreely in the OAuth provider application. | _(a long string of characters)_ |
115 | | `client_secret` | The client secret associated with WriteFreely in the OAuth provider application. | _(a long string of characters)_ |
116 | | `host` | The base url of the OAuth provider application, including the protocol. | https://example.com |
117 | | `display_name` | The human-readable name of the OAuth service that appears on the login button, will appear as "Log in with [display_name]". | _(name of the application)_ |
118 | | `callback_proxy` | The url of an inbound proxy that sits in front of the default `/oauth/callback/generic` endpoint. Use if you want the OAuth callback to be somewhere other than that generic location. Default is blank.| https://example.com/whatever/path |
119 | | `callback_proxy_api` | The url of an outbound proxy to send your OAuth requests through. Default is blank. | https://my-proxy.example.com |
120 | | `token_endpoint` | The API endpoint of the OAuth provider implementation to obtain an access token by presenting an authorization grant or refresh token. This is a fragment of a url, appended to `host` as described above. | /oauth/token |
121 | | `inspect_endpoint` | The API endpoint of the OAuth provider that returns basic user info given their authentication information. This is a fragment of a url, appended to `host` as described above. | /oauth/userinfo |
122 | | `auth_endpoint` | The API endpoint of the OAuth provider that returns an authorization grant. This is a fragment of a url, appended to `host` as described above. | public |
123 | | `scope` | A scope or set of scopes required by some OAuth providers. This will usually be blank in this config file, and is set to "read_user" by default. | read_user |
124 | | `allow_disconnect` | Whether or not an individual user is allowed to disconnect this OAuth provider from their account. | false |
125 | | `map_user_id` | Use this User ID key in the provider's user info, instead of the default key (`user_id`) | `user_id` |
126 | | `map_username` | Use this Username key in the provider's user info, instead of the default key (`username`) | `username` |
127 | | `map_display_name` | Use this Display Name key in the provider's user info, instead of the default key (_none_) | `name` |
128 | | `map_email` | Use this Email key in the provider's user info, instead of the default key (`email`) | `email` |
129 |
--------------------------------------------------------------------------------
/admin/docker.md:
--------------------------------------------------------------------------------
1 | # Docker
2 |
3 | ## Using Docker for Development
4 |
5 | If you'd like to use Docker as a base for working on a site's styles and such, you can run the following from a Bash shell.
6 |
7 | *Note: This process is intended only for working on site styling. If you'd like to run Write Freely in production as a Docker service, it'll require a little more work.*
8 |
9 | The `docker-setup.sh` script will present you with a few questions to set up your dev instance. You can hit enter for most of them, except for "Admin username" and "Admin password." You'll probably have to wait a few seconds after running `docker-compose up -d` for the Docker services to come up before running the bash script.
10 |
11 | ```
12 | docker-compose up -d
13 | ./docker-setup.sh
14 | ```
15 |
16 | Now you should be able to navigate to http://localhost:8080 and start working!
17 |
18 | When you're completely done working, you can run `docker-compose down` to destroy your virtual environment, including your database data. Otherwise, `docker-compose stop` will shut down your environment without destroying your data.
19 |
20 | ## Using Docker for Production
21 |
22 | WriteFreely doesn't yet provide an official Docker pathway to production. We're working on it, though!
23 |
--------------------------------------------------------------------------------
/admin/moderation.md:
--------------------------------------------------------------------------------
1 | # Moderation
2 |
3 | WriteFreely offers a growing number of tools to moderate your community of writers.
4 |
5 | ## Registration
6 |
7 | You can choose who to admit or leave out of your community as a good first step toward building a healthy online space. These are the types of registration you can choose from, from more open to closed:
8 |
9 | **Open Registration**. Anyone who comes to your site can join. This is perfect for getting as many people as possible to join your community. To use this mode, enable the "Open Registrations" option in your Admin dashboard, or set `open_registration = true` in your configuration file.
10 |
11 | **Invite-only**. This mode requires new users to have a special invite link to sign up. Admins may choose who can generate these invite links by going to their Dashboard and selecting "Users" or "Admins" for the "Allow sending invitations by" option.
12 |
13 | **Closed Registration**. In this mode, no new users may join your instance. This occurs when invites are disabled (in the Dashboard, the "No one" option is selected, or `user_invites = ` (blank) in the configuration file).
14 |
15 | ## Silencing Users
16 |
17 | If you have registered users who are causing problems on your instance, you can **silence** their account. This hides all of their posts and blogs (and any indication they ever existed) from the wider web, while keeping their data intact, in case you ever want to restore visibility to their account.
18 |
--------------------------------------------------------------------------------
/developer/database.md:
--------------------------------------------------------------------------------
1 | # Database Values
2 |
3 | The following is a description of the various database tables and attributes. (This document is not yet complete).
4 |
5 | ## `accesstokens`
6 | Generated user access tokens for accessing the API.
7 |
8 | ## `appcontent`
9 | Instance-level dynamic content, usually created via the admin dashboard, such as the About and Privacy pages.
10 |
11 | ## `appmigrations`
12 | All database migrations that have been run on this database.
13 |
14 | ## `collectionattributes`
15 | Used for additional properties on collections.
16 |
17 | ## `collectionkeys`
18 | Public / private keypairs for all collections on the instance. Used to sign ActivityPub / fediverse requests.
19 |
20 | ## `collectionpasswords`
21 | Salted and hashed passwords for password-protected collections.
22 |
23 | ## `collectionredirects`
24 | Data about former `alias`es for collections that have had them changed, so that the old `alias` redirects visitors to the new one.
25 |
26 | ## `collections`
27 | Table that contains collections (i.e. "Blogs").
28 |
29 | * `id`: *int(6)*. **Primary Key**. Blog id. Instance specific.
30 |
31 | * `alias`: *varchar(100)*. Blog identifier based on the blog’s title when created.
32 |
33 | * `title`: *varchar(255*. **Cannot be null**. Blog name.
34 |
35 | * `description`: *varchar(160)*. **Cannot be null**. User defined description of blog.
36 |
37 | * `style_sheet`: *text*. CSS stylesheet data goes here.
38 |
39 | * `script`: *text*. **Currently unused.**
40 |
41 | * `format`: *varchar(8)*. User defined format (`blog`, `novel`, or `notebook`).
42 |
43 | * `privacy`: *tinyint(1)*. **Cannot be null**. 0=Unlisted. 2=Private. 4=Password Protected.
44 |
45 | * `owner_id`: *int(6)*. **Cannot be null**. The id of the user that published this collection.
46 |
47 | * `view_count`: *int(6)*. **Cannot be null**. How many views this collection has.
48 |
49 | ## `posts`
50 | Table that contains data for posts within collections.
51 |
52 | * `id`: **Primary Key.** *Char(16)*. Randomly generated id.
53 |
54 | * `slug`: *varchar(100)*. Identifier for post. Smartly generated based on post content, favoring: Title > Text > Id.
55 |
56 | * `modify_token`: *char(32)*.
57 |
58 | * `text_appearance`: *char(4)*. **Cannot be null**. Font class (Serif=`norm`, Sans-serif=`sans`, Monospace=`wrap`)
59 |
60 | * `language`: *char2*. The post’s language.
61 |
62 | * `rtl`: *tinyint(1)*. Value is 0 if the post is written left-to-right, 1 if written right-to-left (e.g. Arabic), and null if user never explicitly provided this value.
63 |
64 | * `privacy`: **Not currently used.**
65 |
66 | * `owner_id`: *int(6)*. Id of the author. Id is instance-specific.
67 |
68 | * `collection_id`: *int(6)*. Id of the blog. Id is instance-specific. Null if post is a Draft.
69 |
70 | * `pinned_position`: *tinyint(1)*. The order the post is "pinned" (zero-indexed). Null if not pinned.
71 |
72 | * `created`: *timestamp*. **Cannot be null**. Time and date of first publish
73 |
74 | * `updated`: *timestamp*. **Cannot be null**. Time and date of last edit
75 |
76 | * `view_count`: *int(6)*. **Cannot be null**. How many views this post has.
77 |
78 | * `title`: *varchar(160)*. **Cannot be null**. Title, if author created one (may be empty string).
79 |
80 | * `content`: *text*. **Cannot be null**. The content of the post in plain text.
81 |
82 | ## `remotefollows`
83 | Data about which remote, i.e. ActivityPub / fediverse, users are following which collections.
84 |
85 | ## `remoteuserkeys`
86 | Public keys of all remote users.
87 |
88 | ## `remoteusers`
89 | Data needed to communicate with remote users.
90 |
91 | ## `userattributes`
92 | Additional attributes on users.
93 |
94 | ## `userinvites`
95 | User invite codes and metadata.
96 |
97 | ## `users`
98 | Table that contains user data.
99 |
100 | * `id`: *int(6)*. **Primary Key**. User id. Instance specific.
101 |
102 | * `username`: *varchar(100)*. **Cannot be null**. Chosen username.
103 |
104 | * `password`: *char(60)*. **Cannot be null**. Salted and hashed password.
105 |
106 | * `email`: *varbinary(255)*. Encrypted email address. (null if not given by user).
107 |
108 | * `created`: *datetime*. **Cannot be null**. Date and time account was created.
109 |
110 | ## `usersinvited`
111 | Data about which users were brought in by which user-invite.
112 |
--------------------------------------------------------------------------------
/developer/setup.md:
--------------------------------------------------------------------------------
1 | # Development Setup
2 |
3 | Ready to hack on your site? Here's a quick overview.
4 |
5 | ## Prerequisites
6 |
7 | * [Go 1.19+](https://golang.org/dl/)
8 | * [Node.js 15+](https://nodejs.org/en/download/)
9 |
10 | ## Quick start
11 |
12 | After installing Go and Node.js, run the following commands to build, configure, and run the application.
13 |
14 | ```bash
15 | go install github.com/writefreely/writefreely/cmd/writefreely@latest
16 |
17 | cd $GOPATH/pkg/mod/github.com/writefreely/writefreely@*
18 |
19 | make build # Compile the application
20 | make install # Config, generate keys, setup database, install LESS compiler
21 | make run # Run the application
22 | ```
23 |
24 | ## Detailed steps
25 |
26 | Use the following steps to build WriteFreely without Make. First, get the code:
27 |
28 | ```bash
29 | git clone https://github.com/writefreely/writefreely.git
30 | cd writefreely
31 | ```
32 |
33 | Finally, build the `writefreely` binary with SQLite support. (Remove `-tags='sqlite'` if you don't need SQLite support.)
34 |
35 | ```bash
36 | go build -v -tags='sqlite' ./cmd/writefreely/
37 | ```
38 |
39 | You can now run WriteFreely! But you'll need one more step to generate some assets and successfully run an instance.
40 |
41 | ### Building site assets
42 |
43 | You'll need Node.js and LESS installed to generate WriteFreely static assets. Install LESS using our utility script:
44 |
45 | ```bash
46 | ./less/install-less.sh
47 | ```
48 |
49 | Next, compile all stylesheets from the `less` directory, creating them in the `static/css/` directory:
50 |
51 | ```bash
52 | cd less
53 | CSSDIR=../static/css
54 | lessc app.less --clean-css="--s1 --advanced" ${CSSDIR}/write.css
55 | lessc fonts.less --clean-css="--s1 --advanced" ${CSSDIR}/fonts.css
56 | lessc icons.less --clean-css="--s1 --advanced" ${CSSDIR}/icons.css
57 | lessc prose.less --clean-css="--s1 --advanced" ${CSSDIR}/prose.css
58 | ```
59 |
60 | Finally, build ProseMirror:
61 |
62 | ```bash
63 | cd ../prose
64 | npm install
65 | npm run-script build
66 | ```
67 |
68 | Now you can run and distribute WriteFreely! 🎉
69 |
70 | Beyond this, you'll initialize your individual instance.
71 |
72 | ### Application initiation
73 |
74 | With your build complete, create a configuration file, encryption keys, and initialize your database.
75 |
76 | #### Config file
77 |
78 | Most users will want the interactive configuration process. Run with:
79 |
80 | ```bash
81 | writefreely config start
82 | ```
83 |
84 | For other cases where you only need a blank configuration, you can non-interactively generate `config.ini` with this command:
85 |
86 | ```bash
87 | writefreely config generate
88 | ```
89 |
90 | #### Encryption keys
91 |
92 | Generate encryption keys needed for storing user sessions and private data:
93 |
94 | ```bash
95 | writefreely keys generate
96 | ```
97 |
98 | #### Database
99 |
100 | With your instance configured for a database, run the following command to create the necessary tables:
101 |
102 | ```bash
103 | writefreely db init
104 | ```
105 |
106 | ### Run WriteFreely
107 |
108 | ```bash
109 | writefreely
110 | ```
111 |
--------------------------------------------------------------------------------
/start.md:
--------------------------------------------------------------------------------
1 | # Getting Started
2 |
3 | This guide will walk you through setting up your own WriteFreely instance. It requires technical knowledge and access to a server you control.
4 |
5 | Run into problems along the way? Ask for help [on our forum](https://discuss.write.as/c/writefreely/installation).
6 |
7 | Don't need to self-host? The Write.as team provides [fully-managed WriteFreely hosting](https://writefreely.org/services/hosting), which also funds our work on the project. You can also join an [existing community](https://writefreely.org/instances).
8 |
9 | ## Requirements
10 |
11 | WriteFreely has minimal requirements to get up and running. You'll only need:
12 |
13 | - The ability to run an executable
14 | - About 30 minutes
15 |
16 | **Requirements for MySQL-backed installs**
17 |
18 | - A MySQL (5.6+) database
19 | - Database server timezone set to UTC
20 |
21 | ## Set Up
22 |
23 | First, [download the latest release](https://github.com/writefreely/writefreely/releases/latest) for your operating system. If your OS and system architecture are not listed, you'll need to [build from source](https://writefreely.org/docs/latest/developer/setup), instead.
24 |
25 | > If you plan to use **MySQL** for your database, separately connect to your MySQL server now and run:
26 | >
27 | > ```
28 | > CREATE DATABASE writefreely CHARACTER SET latin1 COLLATE latin1_swedish_ci;
29 | > ```
30 |
31 | Extract the files and go into the project folder. Next, start the configuration process by running the following command. This will walk you through the various ways you can configure your instance:
32 |
33 | ```
34 | writefreely config start
35 | ```
36 |
37 | Generate the encryption keys for your instance by running:
38 |
39 | ```
40 | writefreely keys generate
41 | ```
42 |
43 | And finally, run:
44 |
45 | ```
46 | writefreely
47 | ```
48 |
49 | 🎉
50 |
51 | ## Running in Production
52 |
53 | To run your instance publicly, you can either run WriteFreely as a standalone server or put it behind a reverse proxy. ([Learn the difference here](https://discuss.write.as/t/difference-between-standalone-and-reverse-proxy-install/663).)
54 |
55 | ### Standalone server
56 |
57 | To configure the application to run as a standalone server, run:
58 |
59 | ```
60 | writefreely config start
61 | ```
62 |
63 | ...and choose **Production, standalone** for your _Environment_.
64 |
65 | Next, choose whether the site will be **Insecure** or **Secure**. If you choose **Secure**, you'll need to enter the absolute paths to your Certificate and Key. With those configured, WriteFreely will serve traffic on port 80 and 443, redirecting all insecure traffic to your https:// address.
66 |
67 | ### Behind a reverse proxy
68 |
69 | Here's an example [nginx](https://www.nginx.com/) configuration. It compresses certain files, allows federation endpoints on `/.well-known/` to co-exist with other uses (like Let's Encrypt), and serves static files with nginx instead of the application. Values you should change to match your own configuration are in **bold**:
70 |
71 | ```
72 | server {
73 | listen 80;
74 | listen [::]:80;
75 |
76 | server_name example.com;
77 |
78 | gzip on;
79 | gzip_types
80 | application/javascript
81 | application/x-javascript
82 | application/json
83 | application/rss+xml
84 | application/xml
85 | image/svg+xml
86 | image/x-icon
87 | application/vnd.ms-fontobject
88 | application/font-sfnt
89 | text/css
90 | text/plain;
91 | gzip_min_length 256;
92 | gzip_comp_level 5;
93 | gzip_http_version 1.1;
94 | gzip_vary on;
95 |
96 | location ~ ^/.well-known/(webfinger|nodeinfo|host-meta) {
97 | proxy_set_header Host $host;
98 | proxy_set_header X-Real-IP $remote_addr;
99 | proxy_set_header X-Forwarded-For $remote_addr;
100 | proxy_pass http://127.0.0.1:8080;
101 | proxy_redirect off;
102 | }
103 |
104 | location ~ ^/(css|img|js|fonts)/ {
105 | root /var/www/example.com/static;
106 | # Optionally cache these files in the browser:
107 | # expires 12M;
108 | }
109 |
110 | location / {
111 | proxy_set_header Host $host;
112 | proxy_set_header X-Real-IP $remote_addr;
113 | proxy_set_header X-Forwarded-For $remote_addr;
114 | proxy_pass http://127.0.0.1:8080;
115 | proxy_redirect off;
116 | }
117 | }
118 | ```
119 |
120 | At this point, it'd be a great time to [get a certificate](https://certbot.eff.org/) from Let's Encrypt for your instance.
121 |
122 | ### Starting the service
123 |
124 | Lastly, you'll want to set the application up so it continues running. Here's how to do it on Ubuntu.
125 |
126 | **Ubuntu 14.10 or earlier**. You can create an Upstart service by creating a file at `/etc/init/writefreely.conf`:
127 |
128 | ```
129 | description "WriteFreely Instance"
130 | author "Write.as "
131 |
132 | start on runlevel [2345]
133 | stop on runlevel [016]
134 | respawn
135 |
136 | chdir /var/www/example.com
137 | exec /var/www/example.com/writefreely >> /var/log/writefreely.log 2>&1
138 | ```
139 |
140 | Then start the service and you're live!
141 |
142 | ```
143 | sudo start writefreely
144 | ```
145 |
146 | Verify this by following the application log with:
147 |
148 | ```
149 | tail -F /var/log/writefreely.log
150 | ```
151 |
152 | **Ubuntu 15.04 or later**. You can create a Systemd service by creating a file at `/etc/systemd/system/writefreely.service`:
153 |
154 | ```
155 | [Unit]
156 | Description=WriteFreely Instance
157 | After=syslog.target network.target
158 | # If MySQL is running on the same machine, uncomment the following
159 | # line to use it, instead.
160 | #After=syslog.target network.target mysql.service
161 |
162 | [Service]
163 | Type=simple
164 | StandardOutput=syslog
165 | StandardError=syslog
166 | WorkingDirectory=/var/www/example.com
167 | ExecStart=/var/www/example.com/writefreely
168 | Restart=always
169 |
170 | [Install]
171 | WantedBy=multi-user.target
172 | ```
173 |
174 | Then start the service and you're live!
175 |
176 | ```
177 | sudo systemctl start writefreely
178 | ```
179 |
180 | Verify this by following the application log with:
181 |
182 | ```
183 | sudo journalctl -f -u writefreely
184 | ```
--------------------------------------------------------------------------------
/writer/additional-blogs.md:
--------------------------------------------------------------------------------
1 | # Creating an Additional Blog
2 |
3 | WriteFreely makes it easy to publish multiple blogs from a single account, so you can separate your writing by audience or topic, and express yourself more freely.
4 |
5 | ## Prerequisites
6 |
7 | First, your admin will need to configure your instance for multiple blogs (where `max_blogs` is either `0` or greater than `1`). If you're unable to create a blog with the instructions below, talk to your admin about increasing this limit.
8 |
9 | ## Getting Started
10 |
11 | First, navigate to your _Blogs_ page. Under the list of blogs on this page, click "New blog" to start the creation process.
12 |
13 | > **Note**: If there is no "New blog" link here, you've reached the limit of blogs you can create under one account. Talk to your admin about expanding that limit.
14 |
15 | Enter the title or slug you want for your blog, and click the _Create_ button. A slug will contain only lowercase letters, numbers, and hyphens, and will comprise the URL of this new blog (e.g. `https://example.com/myblog/`). A title can contain any combination of letters, numbers, and symbols, but will automatically be converted into a friendly slug.
16 |
17 | 
18 |
19 | Once you click _Create_, the new blog will appear on your _Blogs_ page. Now you can publish to it!
20 |
21 | 
22 |
23 | ## Blog relationships
24 |
25 | Each WriteFreely blog is publicly detached from its owner, meaning readers won't know that the same author owns any two blogs, by default. This enables writers to maintain separate real-name and pseudonymous blogs, blogs for personal and professional life, or simply different blogs for different topics.
26 |
27 | ### Privacy
28 |
29 | This design offers writers a highly convenient way to maintain multiple personas with reliable privacy from readers. However, it does not offer complete privacy from your instance admin, who can see the association between your blogs just as you can. If you need more assurance that this association won't be known, it's important that you either trust you admin with this information or take additional steps to protect yourself, such as creating multiple accounts.
30 |
--------------------------------------------------------------------------------
/writer/autosave.md:
--------------------------------------------------------------------------------
1 | # Auto-saving editor
2 |
3 | The WriteFreely editor automatically saves your writing, so if you leave without saving, you'll pick up where you left off when you come back.
4 |
5 | Writing automatically saves to the device you're writing on and works even with a spotty internet connection, so you'll always have access to your draft from the same device and browser that you started it on.
6 |
--------------------------------------------------------------------------------
/writer/css.md:
--------------------------------------------------------------------------------
1 | # Customizing CSS
2 |
3 | If you can write CSS, you can customize the appearance of your WriteFreely blog.
4 |
5 | ## Getting Started
6 |
7 | All you need to do is go to the _Customize_ settings of your blog. Scroll down to "Custom CSS" and customize your blog from there.
8 |
9 | The following stylesheet shows a few basic selectors you'll need in order to customize certain elements. You can just grab the selectors (e.g. `#blog-title a`) for your stylesheet — the properties are only there to illustrate what you can do.
10 |
11 | ```css
12 | /* Entire page background */
13 | body {
14 | background-color: #efefef;
15 | }
16 |
17 | /* Blog header on index and post pages */
18 | #blog-title a {
19 | color: #fff;
20 | background-color: #7a629d;
21 | }
22 | #blog-title a:hover {
23 | color: #eee;
24 | background-color: #7a629d;
25 | }
26 |
27 | /* Blog header on post pages ONLY */
28 | body#post #blog-title a {
29 | padding: 4px 8px;
30 | }
31 |
32 | /* Blog description (underneath title) on index page */
33 | header p.description {
34 | font-style: italic;
35 | }
36 |
37 | /* Post titles on blog index */
38 | .post-title {
39 | font-weight: normal;
40 | }
41 | .post-title a.u-url:link, .post-title a.u-url:visited {
42 | color: blue;
43 | }
44 |
45 | /* "Read more..." links */
46 | body#collection a.read-more {
47 | text-decoration: underline;
48 | }
49 |
50 | /* Links inside blog posts */
51 | article p a {
52 | color: #444;
53 | text-decoration: none;
54 | border-bottom: 2px solid orangered;
55 | }
56 | article p a:hover {
57 | background-color: orangered;
58 | color: white;
59 | text-decoration: none;
60 | }
61 | ```
62 |
63 | Copy these entire sections verbatim, as each is a complete customization you might want to make, like centering an image.
64 |
65 | ```css
66 | /* Center images */
67 | img {
68 | display: block;
69 | margin: 0 auto;
70 | }
71 |
72 | /* Disable post header fade effect */
73 | body#post header
74 | -moz-opacity: 1;
75 | -khtml-opacity: 1;
76 | -webkit-opacity: 1;
77 | opacity: 1;
78 | }
79 |
80 | /* Hide post views */
81 | header nav .views {
82 | display: none;
83 | }
84 |
85 | ```
86 |
87 | ## Themes
88 |
89 | If you want to see how you can customize beyond the basic elements of your blog, you can check out our [Themes blog](https://write.as/themes). Here you can find custom blogs from the WriteFreely/Write.as community whose themes you can use and remix for your own blog.
90 |
--------------------------------------------------------------------------------
/writer/display-format.md:
--------------------------------------------------------------------------------
1 | # Choosing a Display Format
2 |
3 | WriteFreely gives you full control over the ordering of your posts, and whether or not your blog shows publish dates.
4 |
5 | ## Overview
6 |
7 | First navigate to your _Blogs_ page, and click the _Customize_ button for the blog you want to modify. Next, scroll down to the _Display Format_ section. You'll select your preferred display format here, and click the "Save Changes" button at the bottom of the page to immediately update your blog.
8 |
9 | 
10 |
11 | ## Blog Format
12 |
13 | This is the classic format for a blog: latest posts first, with dates shown. It's perfect for timely writing, like updates for your friends and family or information on upcoming events.
14 |
15 | * **Order**: reverse-chronological
16 | * **Dates**: visible
17 |
18 | 
19 |
20 | ## Novel Format
21 |
22 | This format hides publish dates and orders your posts chronologically, so visitors can read in the order you first published in. This is perfect for serial forms of writing, where each post is an individual chapter or section of a larger piece.
23 |
24 | * **Order**: chronological
25 | * **Dates**: hidden
26 |
27 | 
28 |
29 | ## Notebook Format
30 |
31 | This format combines the ordering of a Blog with the date format of a Novel. It's made for poets and writers who regularly publish evergreen work, or people who just need a space to jot down ideas.
32 |
33 | * **Order**: reverse-chronological
34 | * **Dates**: hidden
35 |
36 | 
37 |
38 | ## Multiple Blogs
39 |
40 | If your WriteFreely instance supports multiple blogs, you can give each one its own format and function. Your first blog might be where your professional writing lives (Blog format), the second could be the place where you record interesting quotes (Notebook format), and the third might be for your NaNoWriMo novel (Novel format).
41 |
--------------------------------------------------------------------------------
/writer/drafts.md:
--------------------------------------------------------------------------------
1 | # Drafts
2 |
3 | WriteFreely allows you to publish without a blog with “Drafts”.
4 |
5 | ## Private
6 |
7 | Drafts are only identified by an unguessable ID, essentially making them private. Unlike posts connected to a blog, they aren't publicly connected to an identity unless you make it known in the post that you wrote it. A reader can't link your draft back to you.
8 |
9 | ## Sharing Drafts
10 |
11 | While private by default, drafts can be shared with others. They can be shared by ID on (instance config value) ```private = false``` instances.
12 |
13 | ## Difference between Drafts & Blog Posts
14 |
15 | Drafts differ from blog posts in a couple of ways.
16 |
17 | While blog posts can include custom slugs for posts, drafts only have randomly generated ID's. Hashtags also won't render in drafts as they do in blog posts. Finally, drafts don't have the ability to include custom CSS like blog posts.
18 |
--------------------------------------------------------------------------------
/writer/excerpt.md:
--------------------------------------------------------------------------------
1 | # Show an Excerpt
2 |
3 | You can show just the beginning of your post on your blog's home page with a special shortcode. For example, if you're on the "How to Use Write.as" blog's [home page](https://howto.write.as/), you'll see a "Read more..." link below each post.
4 |
5 | To separate your home page excerpt from the rest of your post, simply add this shortcode into your post at the spot you want:
6 |
7 | ```
8 |
9 | ```
10 |
11 | Then, readers will see a "Read more..." link displayed in its place on your home page.
12 |
13 | **Bonus**: just like the post date, the "Read more..." link will be translated into the language of your post, if it's one of the languages we support. For more information on this, check out the list of our [currently supported languages](https://poeditor.com/join/project/TIZ6HFRFdE).
14 |
--------------------------------------------------------------------------------
/writer/federation.md:
--------------------------------------------------------------------------------
1 | # Federation
2 |
3 | WriteFreely supports federation via ActivityPub, a protocol spoken by popular platforms like [Mastodon](https://joinmastodon.org) (an alternative to Twitter). This means that other people can directly follow your blog from the decentralized social network known as the "fediverse," if your WriteFreely admin has enabled federation.
4 |
5 | ## Finding your handle
6 |
7 | To find your handle, go into your blog's settings. Under _URL_ will be your blog's fediverse handle, following the format `@blog@instance.com`.
8 |
9 | ## Following your blog
10 |
11 | To follow your blog, open Mastodon, Pleroma, or your other favorite ActivityPub-powered platform, and search for the fediverse handle from your settings page. (**Note**: you can also search for the blog URL, instead of the handle.) Finally, click the "Follow" button. This will make sure you receive future posts in your timeline, where you can then _favorite_ or _boost_ them to your followers.
12 |
13 | ## Note about changing your username
14 |
15 | If you change your username, a new fediverse handle will be created. This means the previous fediverse handle will stop receiving new posts. Anyone who was following your previous fediverse handle will have to follow the new one in order to receive your most recent posts.
16 |
17 | ## Demo
18 |
19 | [Watch this video](https://video.writeas.org/videos/watch/cc55e615-d204-417c-9575-7b57674cc6f3) to see this feature in action.
20 |
21 | ## ActivityPub Mentions
22 |
23 | With federation enabled, you can mention users of Mastodon, Pleroma, and other ActivityPub platforms from your blog. To mention someone, insert `@handle@their.instance` in a blog post.
24 |
--------------------------------------------------------------------------------
/writer/following.md:
--------------------------------------------------------------------------------
1 | # Following a Blog
2 |
3 | WriteFreely supports a variety of open protocols to make it easy for people to follow your blog on the open web.
4 |
5 | ## RSS
6 |
7 | WriteFreely blogs publish RSS feeds, allowing people to follow your blog from an RSS content aggregator or reader.
8 |
9 | ### Following a Blog via RSS
10 |
11 | To access a WriteFreely blog's RSS feed, add "/feed/" to the end of the blog's URL.
12 |
13 | [blog.writefreely.org/feed/](https://blog.writefreely.org/feed/)
14 |
15 | You can also enter the normal blog URL into a feed reader, and most will automatically detect the RSS feed from the blog's metadata.
16 |
17 | ### Following a Blog's Hashtag via RSS
18 |
19 | If you want to filter a WriteFreely blog's content by hashtag, you can follow posts that only use those hashtags. All you need to do is add "/feed/" to the end a blog's hashtag URL.
20 |
21 | [blog.writefreely.org/tag:writefreely/feed/](https://blog.writefreely.org/tag:writefreely/feed/)
22 |
23 | ## ActivityPub
24 |
25 | WriteFreely supports federation via ActivityPub, allowing people to follow your blog from popular platforms like Mastodon (an alternative to Twitter). Learn more about how following works with ActivityPub in the [Federation guide](https://writefreely.org/docs/latest/writer/federation).
26 |
--------------------------------------------------------------------------------
/writer/hashtags.md:
--------------------------------------------------------------------------------
1 | # Hashtags
2 |
3 | WriteFreely lets you easily group your posts together with hashtags.
4 |
5 | Add one at any point in your post, like #this, and it'll automatically be linked to a special page that shows all posts in the blog containing that hashtag. You can also add as many hashtags as you want to your posts, with any kind of capitalization.
6 |
7 | _NOTE: Hashtags will only auto-link on blog posts, not Draft posts._
8 |
9 | ## Hashtags on Instance Reader
10 |
11 | If ```local_timeline``` is enabled, hashtags are searchable in your instance's reader. All you need to do is add "/t/hashtag" to the instance reader url to find public posts that use that hashtag. Here is an example:
12 |
13 | [personaljournal.ca/read/t/SingularityNet](https://personaljournal.ca/read/t/SingularityNet)
14 |
15 | ## Hashtags on Fediverse
16 |
17 | Hashtags are also sent to the Fediverse. So if your blog has federation enabled, your tagged blog posts will show up with all the other posts that people have similarly tagged when someone does a search in Mastodon, Pleroma, etc.
18 |
--------------------------------------------------------------------------------
/writer/html.md:
--------------------------------------------------------------------------------
1 | # Adding HTML
2 |
3 | Along with plaintext and Markdown, you can also format text on WriteFreely with HTML. Here is the supported HTML you can use in your posts.
4 |
5 |
6 | ## Text
7 |
8 | ```
9 |
Text
18 | 19 |This is a blockquote.32 | ``` 33 | 34 |
Text
43 | 44 |This is a blockquote.57 | 58 | ``` 59 | Bold 60 | 61 | Italics 62 | 63 | Underline 64 | 65 | Style 66 | 67 | Link 68 | 69 | ``` 70 | 71 | Bold 72 | 73 | Italics 74 | 75 | Underline 76 | 77 | Style 78 | 79 |
This paragraph is also inside the box...
82 |```go 154 | package main 155 | 156 | import "fmt" 157 | 158 | func main() { 159 | fmt.Println("Hello, world") 160 | } 161 | ```162 | 163 | ```go 164 | package main 165 | 166 | import "fmt" 167 | 168 | func main() { 169 | fmt.Println("Hello, world") 170 | } 171 | ``` 172 | --------------------------------------------------------------------------------