├── .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 | ![write freely](https://writefreely.org/img/writefreely.svg) 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 | ![The blog creation form: a text box and "Create" button](https://i.snap.as/dXfBD3F.png) 18 | 19 | Once you click _Create_, the new blog will appear on your _Blogs_ page. Now you can publish to it! 20 | 21 | ![An example blog listed on the Blogs page](https://i.snap.as/jN0D362.png) 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 | ![The Display Format section with three choices: Blog, Novel, Notebook](https://i.snap.as/3jXkk7N.png) 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 | ![Example blog in the Blog format](https://i.snap.as/3d2nAqU.png) 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 | ![Example blog in the Novel format](https://i.snap.as/NCswNiR.png) 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 | ![Example blog in the Notebook format](https://i.snap.as/SLtj1b9.png) 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 |

Header

10 | 11 |

Header

12 | 13 |

Header

14 | 15 |

Header

16 | 17 |

Text

18 | 19 | 24 | 25 |
    26 |
  1. List Item
  2. 27 |
  3. List Item
  4. 28 |
  5. List Item
  6. 29 |
30 | 31 |
This is a blockquote.
32 | ``` 33 | 34 |

Header

35 | 36 |

Header

37 | 38 |

Header

39 | 40 |

Header

41 | 42 |

Text

43 | 44 | 49 | 50 |
    51 |
  1. List Item
  2. 52 |
  3. List Item
  4. 53 |
  5. List Item
  6. 54 |
55 | 56 |
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 |
80 |
Subtitle in the box
81 |

This paragraph is also inside the box...

82 |
83 | 84 | Link 85 | 86 | ## Video 87 | 88 | ``` 89 | 90 | ``` 91 | 92 | 93 | 94 | ## Iframe 95 | 96 | 97 | ``` 98 | 99 | ``` 100 | 101 | 102 | 103 | ## Audio 104 | 105 | 106 | ``` 107 | 108 | ``` 109 | 110 | 111 | 112 | ## Image 113 | 114 | ``` 115 | 116 | ``` 117 | 118 | 119 | -------------------------------------------------------------------------------- /writer/publicity.md: -------------------------------------------------------------------------------- 1 | # Publicity 2 | 3 | Whether you want to keep your writing completely private, limited to a certain audience, or sent out to the world wide web, WriteFreely can help with flexible blog publicity settings. 4 | 5 | ## Getting Started 6 | 7 | If you go to your blog's _Customize_ page, you will see a section labeled _Publicity_. This is where you can configure who gets to see your blog. To change, all you need to do is click the checkbox of your preferred publicity setting and then scroll down to select "Save Changes." 8 | 9 | ![Publicity settings for your WriteFreely blog](https://i.snap.as/4eFNafN.png) 10 | 11 | Let's explore the different options provided. 12 | 13 | ## Unlisted 14 | 15 | The Unlisted setting makes your posts available only to people who have your blog's URL. This is perfect if you're concerned about future employers looking at your blog, or if you mainly distribute your posts through social media or your personal site. 16 | 17 | ## Private 18 | 19 | What if you want your blog to be a digital journal? A private space where you can write without anyone looking over your shoulder? Make your blog “Private”. This means that nobody else can read the blog but you, and only when you're logged in. 20 | 21 | ## Password Protected 22 | 23 | What if you want your blog to be read only by some people? A place for drafts that only trusted confidants could see? Make your blog “Password Protected”. This meets somewhere in-between “Unlisted” and “Private”. Only readers who have the password you set can access your blog. 24 | 25 | ## Public 26 | 27 | What if you want your blog to be part of the larger community? A place where you not only share your writing but can read the writing shared by others? Make your blog “Public”. While still sharable by link, your posts also go into the WriteFreely site's Reader, the built-in writing hub. All posts from blogs on the site that are set to "Public" will show in the Reader. 28 | 29 | ## Defaults 30 | 31 | To find the default publicity blog setting for your WriteFreely site, go to your blogs page (`/me/c`), click the _Customize_ link under your first blog, and then scroll down to _Publicity_. This is the default publicity setting for blogs on the WriteFreely site. You can always change the publicity setting for your blog if needed. 32 | 33 | ## Multiple Blogs 34 | 35 | Some WriteFreely sites support multiple blogs. For example, if your site supports three blogs per user, you can have these blogs coexist with distinct or overlapping publicity types. Imagine having one blog that is your daily journal (“Private”), one blog for updates that only one person in your team can read (“Password Protected”), and then another where your articles on bug fixes can live for fellow programmers (“Unlisted”). 36 | -------------------------------------------------------------------------------- /writer/scheduling.md: -------------------------------------------------------------------------------- 1 | # Scheduling Posts 2 | 3 | WriteFreely supports scheduling posts to be published at a future date. 4 | 5 | To schedule a post from the web application: 6 | 7 | 1. Publish a post as a _Draft_ 8 | 1. At the top of the page, click _Edit_ 9 | 1. Notice the new "i" icon at the top of the editor; click that 10 | 1. Change the "Created" time to a date in the future, and save your settings 11 | 1. Finally, go to your Drafts page 12 | 1. Click "move to _your blog_" 13 | 14 | **Advanced.** To schedule a post from the API: 15 | 16 | 1. When [publishing a blog post](https://developers.write.as/docs/api/#publish-a-collection-post), include the `created` property 17 | 18 | ## Notes 19 | 20 | * Scheduled posts are hidden from your blog until the _published_ date has passed 21 | * Scheduled posts won't be sent out via federation / ActivityPub (to be fixed with [T567](https://writefreely.org/tasks/567)) 22 | -------------------------------------------------------------------------------- /writer/static.md: -------------------------------------------------------------------------------- 1 | # Creating a Static Page 2 | 3 | You can permanently _pin_ any WriteFreely blog post to your blog's navigation bar (like an "About" or "Contact" page) by following these steps: 4 | 5 | 1. Publish a post to your blog 6 | 1. Go to your blog home page by clicking your blog name in the top-left corner 7 | 1. Hover over the post 8 | 1. Click **Pin** 9 | 10 | That's it! You'll see a link to that post from every page on your blog. 11 | 12 | Here's what it looks like in practice: 13 | 14 | 15 | 16 | ## Types of Static Pages 17 | 18 | Now that we know how pinning works, we can create static pages. But what kind of static pages should be on your blog? Much like a table of contents or index for a book, static pages act as a frame of reference for your blog. With that in mind, let's examine four static pages you could create 19 | 20 | **About** 21 | 22 | Your readers could be asking themselves, "What is this blog about?" This is the static page where people can learn more about the "what" and "who" of your blog. Be as specific or vague as you'd like - it is your blog. Choose who you want to be and what you want your blog to be about. 23 | 24 | **Contact** 25 | 26 | Once people learn more about you and your blog, perhaps they want to get in touch - to leave positive feedback or make a suggestion. How can they do that? This is the page where you can list the ways people can contact you, everything from an email address to a contact form. 27 | 28 | **Social** 29 | 30 | This blog is just one part of your online presence. Where can others find you? Link to your social media, your videos, your code repos, anywhere else where readers can find you on the web. 31 | 32 | **Subscribe** 33 | 34 | Someone enjoys your blog and wants to subscribe to it. Create a static page that gives them the ways they can do that. WriteFreely already provides built-in methods for subscription: 35 | 36 | - Provide a link to your RSS feed (simply add /feed to your blog's url). 37 | - Share your blog's Fediverse handle so that people can follow your blog from Mastodon (learn about enabling Federation [here](https://video.writeas.org/videos/watch/cc55e615-d204-417c-9575-7b57674cc6f3?start=)). 38 | -------------------------------------------------------------------------------- /writer/writing.md: -------------------------------------------------------------------------------- 1 | # Writing Posts 2 | 3 | Learn how to do more than publish plain text in this guide for writers. 4 | 5 | ## Adding a title 6 | 7 | Titles on WriteFreely are optional, but easy to add by including them in the body of your post. 8 | 9 | **Option 1.** Explicitly add a title by starting your post with a Markdown header. That is, type a hash symbol (**#**), a space, and then your title. _The title will show up as a large heading._ 10 | 11 | ```text 12 | # Title of my Post 13 | 14 | By starting a line with the hash symbol (#) and a space immediately 15 | after it, Write.as knows that you wanted to use the following text on 16 | that line ("Title of my Post") as the true title of your post. 17 | 18 | Now, not only will "Title of my Post" show up in the browser's title 19 | bar, but it will also show in big letters at the top of this post's page. 20 | ``` 21 | 22 | **Option 2.** Write it on the first line, separated from the rest of the content. _The title will show up the same size as the rest of the post._ 23 | 24 | ```text 25 | Title of my Post 26 | 27 | Content begins here, which was started after the blank line above. Since 28 | there was a blank line, "Title" will be the title of the post that shows 29 | up in the top of the browser window. But since we didn't go out of our 30 | way to indicate that was our title, it will also display normally with 31 | the rest of the text on the post itself. 32 | ``` 33 | 34 | ## Formatting text 35 | 36 | You can format text on WriteFreely with a special kind of syntax called _Markdown_, which uses special characters to indicate bold, italic, and other text. If you've used Markdown before, you'll be right at home here. 37 | 38 | ### Headers 39 | ```markdown 40 | # This is the biggest header (h1) 41 | ## This is still a big header (h2) 42 | ### This is a smaller header (h3) 43 | ###### This is the smallest header you can make (h6) 44 | ``` 45 | 46 | # This is the biggest header (h1) 47 | ## This is still a big header (h2) 48 | ### This is a smaller header (h3) 49 | ###### This is the smallest header you can make (h6) 50 | 51 | ### Emphasis 52 | ```markdown 53 | *This is italic* 54 | _This is italic, too_ 55 | 56 | **This is bold** 57 | __This is bold, too__ 58 | 59 | _Here's some **emphatic** text._ 60 | ``` 61 | 62 | *This is italic* 63 | _This is italic, too_ 64 | 65 | **This is bold** 66 | __This is bold, too__ 67 | 68 | _Here's some **emphatic** text._ 69 | 70 | ### Lists 71 | 72 | **Bulleted**: 73 | ```markdown 74 | * Hello 75 | * Goodbye 76 | * Ciao 77 | * Au revoir 78 | * Auf Wiedersehen 79 | * Arrivederci 80 | ``` 81 | * Hello 82 | * Goodbye 83 | * Ciao 84 | * Au revoir 85 | * Auf Wiedersehen 86 | * Arrivederci 87 | 88 | **Numbered**: 89 | ```markdown 90 | 1. First, this 91 | 2. Then that 92 | 3. Lastly, this 93 | 94 | 1. First this 95 | 1. Then a second thing 96 | 1. Finally a third thing 97 | 1. And so on 98 | ``` 99 | 100 | 1. First, this 101 | 2. Then that 102 | 3. Lastly, this 103 | 104 | 1. First this 105 | 1. Then a second thing 106 | 1. Finally a third thing 107 | 1. And so on 108 | 109 | ### Images 110 | ```markdown 111 | ![Cosmic radiation](https://i.snap.as/T05UTpx.jpg) 112 | ``` 113 | 114 | ![Cosmic radiation](https://i.snap.as/T05UTpx.jpg) 115 | 116 | ### Links 117 | 118 | ```markdown 119 | https://writefreely.org 120 | [A user guide](https://writefreely.org/docs) 121 | ``` 122 | https://writefreely.org 123 | [A user guide](https://writefreely.org/docs) 124 | 125 | Link to your email by putting `mailto:` in front of it: 126 | 127 | ```markdown 128 | [Contact me](mailto:hello@example.com) 129 | ``` 130 | 131 | [Contact me](mailto:hello@example.com) 132 | 133 | ### Quotes 134 | 135 | ```markdown 136 | > Wherever you go, 137 | > there you are. 138 | ``` 139 | 140 | > Wherever you go, 141 | > there you are. 142 | 143 | ### Inline Code 144 | 145 | ```markdown 146 | Download the command-line client and run `./writeas new` 147 | ``` 148 | 149 | Download the command-line client and run `./writeas new` 150 | 151 | ### Syntax-highlighted Code Blocks 152 | 153 |
```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 | --------------------------------------------------------------------------------