diff --git a/README.html b/README.html index 7e4a1d9..1bfca98 100644 --- a/README.html +++ b/README.html @@ -1,160 +1,204 @@

a command-line based blogging platform running on tilde.town

-

ttbp stands for "tilde.town blogging platform", the original working name for this project.

- -

ttbp main menu screenshot

- -

ttbp entries view screenshot

- -

ttbp compose view screenshot

- +

ttbp main menu screenshot

+

ttbp entries view screenshot

+

ttbp compose view screenshot

ttbp runs from the command line, providing a hub for writing personal blog posts and reading posts written by other users of tilde.town. it's a little bit -like livejournal or dreamwidth or tumblr. you can opt to publish your posts to -a public html file hosted on your tilde page, or keep all your entries private -to the tilde.town server.

- +like livejournal or dreamwidth or tumblr. you can opt to publish your posts to a +public html file hosted on your tilde page, to tilde.town's gopher server, or +keep all your entries private to the tilde.town server.

to use, run feels while logged in to tilde.town

- -

(feels is a tilde.town specific command; if you're running this locally, or on -a different server, run ttbp from the command line)

- -

QUICK START

- +

this is a project that runs on tilde.town, so all users of this program are +expected to operate under the tilde.town code of +conduct. content/personal issues should be +worked out according to the CoC, with support from the administrative +team if needed.

+

QUICK START

no coding or html experience is necessary to get started. just log in to your tilde.town account and enter:

-

feels

-

ttbp will ask you a few questions to get you started. after that, writing and reading entries all happen within the program.

-

that's it!

- -

SUPPORT

- +

support

if you're having trouble getting started, or run into program errors or strange behavior, please send internal tilde.town mail to ~endorphant and i'll try to sort things out!

- -

writing entries

- -

entries are recorded as plaintext files in your ~/.ttbp/entries -directory. you can edit them there directly, or fix old entries, or -delete entries.

- -

warning: changing old entries might cause strange things to -happen with timestamps. the main program looks at the filename -first for setting the date, then the last modified time to sort -recent posts. it expects YYYMMDD.txt as the filename; anything else -won't show up as a valid entry. yes, this means you can post things out -of date order by creating files with any date you want.

- -

general entry-writing notes

- +

there's also a function from the main menu that lets you send feedback/inquiries +to me directly; this uses internal tilde.town mail, which is what i'll respond +to.

+

writing entries

+

entries are recorded as plaintext files in your ~/.ttbp/entries directory. +ttbp will use your selected editor to open and write files; each day is its +own entry, like a diary page. at midnight for whatever timezone you've set for +your user account on tilde.town, you'll get a fresh entry. if you don't write +any feels on a particular day, no entries will show up there.

+

when you save and quit the text editor, your entry will automatically propagate +to the global feels list; if you've opted to publish your feels to html/gopher, +those files will update immediately. you can always go back to the current day's +entry and edit/add as you'd like, but older entries will not be available for +editing from ttbp.

+

(since files are just stored as plaintext in your directory, it's possible to +edit and move old entries directly from the command line. however, changing old +entries might cause strange things to happen with timestamps. the main program +looks at the filename first for setting the date, then the last modified time to +sort recent posts. it expects YYYMMDD.txt as the filename; anything else won't +show up as a valid entry. yes, this means you can post things out of date order +by creating files with any date you want.)

+

general entry-writing notes

- -

privacy

- +

reading other feels

+

the browse global feels feature shows the ten most recent entries that anyone +has written on ttbp. this list is only accessible from within tilde.town, +although individual entries may be posted to html or gopher.

+

you can also pull up a list of a single user's feels through check out your +neighbors, which displays all users who are writing on ttbp based on their +most recently updated entry, and a link to their public html blog if they've +opted to publish their posts.

+

please note! entries written on ttbp should be considered sensitive, +private information, even if a particular user is publishing entries in a +world-viewable way! please be respectful about having access to other people's +feels, and do not copy/repeat any information without getting their explicit +permission. tilde.town operates on a high level of mutual trust, and ttbp is +designed to give individuals control over their content.

+

privacy

when you start your ttbp, you have the option of publishing or not publishing your blog.

- -

if you opt to publish, the program creates a directory ~/.ttbp/www -where it stores all html files it generates, and symlinks this from your -~/public_html with your chosen blog directory. your blog will also be listed -on the main ttbp page.

- -

if you opt to not publish, your entires will never be accessible from outside -of the tilde.town network; other tilde.town users will still be able to read -your entries through the ttbp interface, or by directly accessing your +

if you opt to not publish, your entires will never be accessible from outside of +the tilde.town network; other tilde.town users will still be able to read your +entries through the ttbp interface, or by directly accessing your ~/.ttbp/entries directory.

-

if you want to further protect your entries, you can chmod 700 your entries directory.

- -

changing your page layout

- -

you can modify how your blog looks by editing the stylesheet or -header and footer files. the program sets you up with basic -default. if you break your page somehow, you can force the program to -regenerate your configuration by deleting your ~/.ttbp directory entirely. -you might want to back up your ~/.ttbp/entries directory before you do -this.

- +

if you opt to publish, the program creates a directory ~/.ttbp/www where it +stores all html files it generates, and symlinks this from your ~/public_html +with your chosen blog directory. your blog will also be listed on the main ttbp +page.

+

you can also opt to publish to gopher, and the program will automatically +generate a gophermap of your feels.

+

you can set publishing status on individual entries, or bury individual feels; +see "data management" below for details.

+

data management

+

the manage your feels menu provides several tools for organizing your feels. +these are all actions you can perform manually from the command line, but doing +them from within the program can help keep your files properly linked up.

+

settings

+

the settings menu lets you change specific options for handling your feels and +using the interface.

- - -
  • to modify the page header, edit your ~/.ttbp/config/header.txt - - +
  • editor--set your text editor
  • +
  • gopher--opt in or out of automatically posting to gopher
  • +
  • post as nopub--set whether posts default to being published or not + published (if you're not publishing your feels, this doesn't matter)
  • +
  • publish dir--set the directory under you public_html where feels will be + published (if you're not publishing your feels, this defaults to None)
  • +
  • publishing--opt in or out of automatically publishing entries to a + world-readable html page
  • +
  • rainbows--opt in or out of having multicolored menu text
  • + +

    changing your page layout

    +

    you can modify how your blog looks by editing the stylesheet or header and +footer files. the program sets you up with basic default. if you break your page +somehow, you can force the program to regenerate your configuration by deleting +your ~/.ttbp directory entirely. you might want to back up your +~/.ttbp/entries directory before you do this.

    - - +
  • to modify your stylesheet, edit your ~/.ttbp/config/style.css
  • +
  • to modify the page header, edit your ~/.ttbp/config/header.txt
  • +
  • there's a place marked off in the default header where you can safely put + custom HTML elements!
  • to modify the page footer, edit your ~/.ttbp/config/footer.txt
  • - -

    general tips/troubleshooting

    - +

    general tips/troubleshooting

    - -

    future features

    - +

    future features

    these are a few ideas being kicked around, or under active development:

    - - -

    dependencies

    - +

    other ideas are listed on github as +upcoming features or feature requests!

    +

    dependencies

    (this section is only relevant if you plan on forking the repo and running an instance of this yourself)

    - - -

    contributing

    - +

    contributing

    please check out my contributor guidelines on github if you'd like to get involved with development!

    -

    if you find any bugs or strange behavior, please message me locally on tildemail or open a github issue and i'll get back to you as soon as i can.

    - -

    if you're interested in helping with the code, please drop me some tildemail!

    \ No newline at end of file +

    if you're interested in helping with the code, please drop me some tildemail!

    +

    i accept tips for development work on +liberapay

    +

    contributor shout-outs

    +

    thanks to:

    + \ No newline at end of file diff --git a/README.md b/README.md index 790585f..f32c4f4 100644 --- a/README.md +++ b/README.md @@ -11,14 +11,18 @@ this project. `ttbp` runs from the command line, providing a hub for writing personal blog posts and reading posts written by other users of tilde.town. it's a little bit -like livejournal or dreamwidth or tumblr. you can opt to publish your posts to -a public html file hosted on your tilde page, or keep all your entries private -to the tilde.town server. +like livejournal or dreamwidth or tumblr. you can opt to publish your posts to a +public html file hosted on your tilde page, to tilde.town's gopher server, or +keep all your entries private to the tilde.town server. to use, run `feels` while logged in to tilde.town -(`feels` is a tilde.town specific command; if you're running this locally, or on -a different server, run `ttbp` from the command line) +this is a project that runs on tilde.town, so all users of this program are +expected to operate under the tilde.town [code of +conduct](http://tilde.town/wiki/conduct.html). content/personal issues should be +worked out according to the CoC, with support from the [administrative +team](http://tilde.town/wiki/administration/index.html) if needed. + ### QUICK START @@ -32,84 +36,184 @@ reading entries all happen within the program. that's it! -### SUPPORT +### support if you're having trouble getting started, or run into program errors or strange behavior, please send internal tilde.town mail to ~endorphant and i'll try to sort things out! +there's also a function from the main menu that lets you send feedback/inquiries +to me directly; this uses internal tilde.town mail, which is what i'll respond +to. + ### writing entries -entries are recorded as plaintext files in your ~/.ttbp/entries -directory. you can edit them there directly, or fix old entries, or -delete entries. +entries are recorded as plaintext files in your `~/.ttbp/entries` directory. +`ttbp` will use your selected editor to open and write files; each day is its +own entry, like a diary page. at midnight for whatever timezone you've set for +your user account on tilde.town, you'll get a fresh entry. if you don't write +any feels on a particular day, no entries will show up there. -*warning*: changing old entries might cause strange things to -happen with timestamps. the main program looks at the filename -first for setting the date, then the last modified time to sort -recent posts. it expects YYYMMDD.txt as the filename; anything else -won't show up as a valid entry. yes, this means you can post things out -of date order by creating files with any date you want. +when you save and quit the text editor, your entry will automatically propagate +to the global feels list; if you've opted to publish your feels to html/gopher, +those files will update immediately. you can always go back to the current day's +entry and edit/add as you'd like, but older entries will not be available for +editing from `ttbp`. + +*(since files are just stored as plaintext in your directory, it's possible to +edit and move old entries directly from the command line. however, changing old +entries might cause strange things to happen with timestamps. the main program +looks at the filename first for setting the date, then the last modified time to +sort recent posts. it expects YYYMMDD.txt as the filename; anything else won't +show up as a valid entry. yes, this means you can post things out of date order +by creating files with any date you want.)* #### general entry-writing notes * you can use [markdown](https://daringfireball.net/projects/markdown/syntax) * you can use html * you can also put things between `` to have them show up -in the feed but not render in a browser (but people can still read -them with view-source) + in the feed but not render in a browser (but people can still read + them with view-source) + +### reading other feels + +the `browse global feels` feature shows the ten most recent entries that anyone +has written on ttbp. this list is only accessible from within tilde.town, +although individual entries may be posted to html or gopher. + +you can also pull up a list of a single user's feels through `check out your +neighbors`, which displays all users who are writing on `ttbp` based on their +most recently updated entry, and a link to their public html blog if they've +opted to publish their posts. + +**please note!** entries written on `ttbp` should be considered sensitive, +private information, even if a particular user is publishing entries in a +world-viewable way! please be respectful about having access to other people's +feels, and do not copy/repeat any information without getting their explicit +permission. tilde.town operates on a high level of mutual trust, and `ttbp` is +designed to give individuals control over their content. ### privacy when you start your ttbp, you have the option of publishing or not publishing your blog. -if you opt to publish, the program creates a directory `~/.ttbp/www` -where it stores all html files it generates, and symlinks this from your -`~/public_html` with your chosen blog directory. your blog will also be listed -on the [main ttbp page](https://tilde.town/~endorphant/ttbp). - -if you opt to not publish, your entires will never be accessible from outside -of the tilde.town network; other tilde.town users will still be able to read -your entries through the ttbp interface, or by directly accessing your +if you opt to not publish, your entires will never be accessible from outside of +the tilde.town network; other tilde.town users will still be able to read your +entries through the ttbp interface, or by directly accessing your `~/.ttbp/entries` directory. if you want to further protect your entries, you can `chmod 700` your entries directory. +if you opt to publish, the program creates a directory `~/.ttbp/www` where it +stores all html files it generates, and symlinks this from your `~/public_html` +with your chosen blog directory. your blog will also be listed on the [main ttbp +page](https://tilde.town/~endorphant/ttbp). + +you can also opt to publish to gopher, and the program will automatically +generate a gophermap of your feels. + +you can set publishing status on individual entries, or bury individual feels; +see "data management" below for details. + +### data management + +the `manage your feels` menu provides several tools for organizing your feels. +these are all actions you can perform manually from the command line, but doing +them from within the program can help keep your files properly linked up. + +* **read over feels**--a list of all your entries, which you can open and + read like any other feel +* **modify feels publishing**--this lets you toggle privacy on individual + posts. entries marked `(nopub)` will not get written to html or gopher, + and toggling them from this menu will immediately publish or unpublish + that entry (if you're not publishing your posts at all, these settings + won't matter, since your feels will never show up outside of tilde.town) +* **backup your feels**--makes a .tar.gz of all your entries, saving one + copy to `~/.ttbp/backups/` with the current date, and a second copy to + your home directory for safekeeping. +* **import a feels backup**--unpacks a backup file into your current feels + list. this tool checks the `~/.ttbp/backups` directory for archives, and + expects a file created by the above backup utility. if it detects any file + collisions, it will preserve your current live copy and leave the backup + verison in a temp directory, and notify you that this happened. also, any + entries that were previously marked as `(nopub)` will retain their nopub + status. +* **bury some feels**--hides individual feels from viewing; entries are + moved to `~/.ttbp/buried` (and marked with a unique timestamp to prevent + file collision) with permissions set to 600, meaning no one except you + will be able to open that file. these entries are also hidden from your + own view from `read over feels`, and you'll have to open the files from + the command line if you want to see them. this is intended to be a + permament action, so you'll be asked to type the entry date once to load + the feel, then shown a preview of that feel, and then type the date again + to confirm burying. +* **delete feels by day**--*permanently removes individual entries*, + including deleting published html/gopher files if needed. this action is + not recoverable, unless you have a backup to restore; you'll be asked to + type the entry date once to load the feel, then shown a preview of that + feel, and then type the date again to confirm deletion. +* **purge all feels**--*permanently removes all feels*, including deleting + all published html/gopher files if needed. this action is not recoverable, + unless you have a backup to restore. you'll be asked to type a + one-time-use purge code to confirm this action. +* **wipe feels account**--*permanently removes all data associated with + feels*, including deleting any published hmtl/gopher files and removing + your `~/.ttbp` directory. any backups that you have in `~/.ttbp/backups` + will also be deleted with this action (which is why the backup function + makes a second copy for safekeeping in your home directory). you will no + longer show up in any lists as a user. + +### settings + +the settings menu lets you change specific options for handling your feels and +using the interface. + +* **editor**--set your text editor +* **gopher**--opt in or out of automatically posting to gopher +* **post as nopub**--set whether posts default to being published or not + published (if you're not publishing your feels, this doesn't matter) +* **publish dir**--set the directory under you `public_html` where feels will be + published (if you're not publishing your feels, this defaults to `None`) +* **publishing**--opt in or out of automatically publishing entries to a + world-readable html page +* **rainbows**--opt in or out of having multicolored menu text + ### changing your page layout -you can modify how your blog looks by editing the stylesheet or -header and footer files. the program sets you up with basic -default. if you break your page somehow, you can force the program to -regenerate your configuration by deleting your ~/.ttbp directory entirely. -**you might want to back up your ~/.ttbp/entries directory before you do -this.** +you can modify how your blog looks by editing the stylesheet or header and +footer files. the program sets you up with basic default. if you break your page +somehow, you can force the program to regenerate your configuration by deleting +your ~/.ttbp directory entirely. **you might want to back up your +~/.ttbp/entries directory before you do this.** * to modify your stylesheet, edit your ~/.ttbp/config/style.css - * (future feature: having multiple stylesheets you can select) * to modify the page header, edit your ~/.ttbp/config/header.txt - * you might note that there's a place marked off in the default header where - you can safely put custom HTML elements! + * there's a place marked off in the default header where you can safely put + custom HTML elements! * to modify the page footer, edit your ~/.ttbp/config/footer.txt ### general tips/troubleshooting -* add `alias ttbp="~endorphant/bin/ttbp"` to your .bash_aliases for fewer keystrokes -* (similarly, `alias ttbp-beta="~endorphant/bin/ttbp-beta"`) * if the date looks like it's ahead or behind, it's because you haven't set your local timezone yet. here are some [timezone setting instructions](http://www.cyberciti.biz/faq/linux-unix-set-tz-environment-variable/) +* the feels burying tool will effectively clear your post for the day; you can + use this feature to start a fresh entry on a particular day by burying the + current day's feels and then editing a new file ### future features these are a few ideas being kicked around, or under active development: -* better entry privacy/publish control options * stylesheet/theme selector -* responding to entries -* paginated list view -* better entry display within ttbp +* better entry display within ttbp (currently just offloads to `less`) +* buried feels browser + +other ideas are listed on github as +[upcoming features](https://github.com/modgethanc/ttbp/issues?q=is%3Aissue+is%3Aopen+label%3A"upcoming+features") or [feature requests](https://github.com/modgethanc/ttbp/issues?q=is%3Aissue+is%3Aopen+label%3A"feature+request")! ### dependencies @@ -118,6 +222,7 @@ instance of this yourself) * [mistune](https://pypi.python.org/pypi/mistune) * [inflect](https://pypi.python.org/pypi/inflect) +* [six](https://pypi.python.org/pypi/six) ### contributing @@ -129,3 +234,14 @@ if you find any bugs or strange behavior, please message me locally on tildemail or open a github issue and i'll get back to you as soon as i can. if you're interested in helping with the code, please drop me some tildemail! + +i accept tips for development work on +[liberapay](https://liberapay.com/modgethanc) + +### contributor shout-outs + +thanks to: + +* ~vilmibm, packaging help and gopher support +* ~sanqui, the bug swatter +* ~sinacutie, for css updates diff --git a/doc/manual.html b/doc/manual.html new file mode 100644 index 0000000..3fe7787 --- /dev/null +++ b/doc/manual.html @@ -0,0 +1,170 @@ +

    FEELS MANUAL

    +

    ttbp stands for "tilde.town blogging platform", the original working name for +this project. the complete codebase is available on +github.

    +

    ttbp runs from the command line, providing a hub for writing personal blog +posts and reading posts written by other users of tilde.town. it's a little bit +like livejournal or dreamwidth or tumblr. you can opt to publish your posts to a +public html file hosted on your tilde page, to tilde.town's gopher server, or +keep all your entries private to the tilde.town server.

    +

    this is a project that runs on tilde.town, so all users of this program are +expected to operate under the tilde.town code of +conduct. content/personal issues should be +worked out according to the CoC, with support from the administrative +team if needed.

    +

    support

    +

    if you're having trouble getting started, or run into program errors or strange +behavior, please send internal tilde.town mail to ~endorphant and i'll try to +sort things out!

    +

    there's also a function from the main menu that lets you send feedback/inquiries +to me directly; this uses internal tilde.town mail, which is what i'll respond +to.

    +

    writing entries

    +

    entries are recorded as plaintext files in your ~/.ttbp/entries directory. +ttbp will use your selected editor to open and write files; each day is its +own entry, like a diary page. at midnight for whatever timezone you've set for +your user account on tilde.town, you'll get a fresh entry. if you don't write +any feels on a particular day, no entries will show up there.

    +

    when you save and quit the text editor, your entry will automatically propagate +to the global feels list; if you've opted to publish your feels to html/gopher, +those files will update immediately. you can always go back to the current day's +entry and edit/add as you'd like, but older entries will not be available for +editing from ttbp.

    +

    (since files are just stored as plaintext in your directory, it's possible to +edit and move old entries directly from the command line. however, changing old +entries might cause strange things to happen with timestamps. the main program +looks at the filename first for setting the date, then the last modified time to +sort recent posts. it expects YYYMMDD.txt as the filename; anything else won't +show up as a valid entry. yes, this means you can post things out of date order +by creating files with any date you want.)

    +

    general entry-writing notes

    + +

    reading other feels

    +

    the browse global feels feature shows the ten most recent entries that anyone +has written on ttbp. this list is only accessible from within tilde.town, +although individual entries may be posted to html or gopher.

    +

    you can also pull up a list of a single user's feels through check out your +neighbors, which displays all users who are writing on ttbp based on their +most recently updated entry, and a link to their public html blog if they've +opted to publish their posts.

    +

    please note! entries written on ttbp should be considered sensitive, +private information, even if a particular user is publishing entries in a +world-viewable way! please be respectful about having access to other people's +feels, and do not copy/repeat any information without getting their explicit +permission. tilde.town operates on a high level of mutual trust, and ttbp is +designed to give individuals control over their content.

    +

    privacy

    +

    when you start your ttbp, you have the option of publishing or not publishing +your blog.

    +

    if you opt to not publish, your entires will never be accessible from outside of +the tilde.town network; other tilde.town users will still be able to read your +entries through the ttbp interface, or by directly accessing your +~/.ttbp/entries directory.

    +

    if you want to further protect your entries, you can chmod 700 your entries +directory.

    +

    if you opt to publish, the program creates a directory ~/.ttbp/www where it +stores all html files it generates, and symlinks this from your ~/public_html +with your chosen blog directory. your blog will also be listed on the main ttbp +page.

    +

    you can also opt to publish to gopher, and the program will automatically +generate a gophermap of your feels.

    +

    you can set publishing status on individual entries, or bury individual feels; +see "data management" below for details.

    +

    data management

    +

    the manage your feels menu provides several tools for organizing your feels. +these are all actions you can perform manually from the command line, but doing +them from within the program can help keep your files properly linked up.

    + +

    settings

    +

    the settings menu lets you change specific options for handling your feels and +using the interface.

    + +

    changing your page layout

    +

    you can modify how your blog looks by editing the stylesheet or header and +footer files. the program sets you up with basic default. if you break your page +somehow, you can force the program to regenerate your configuration by deleting +your ~/.ttbp directory entirely. you might want to back up your +~/.ttbp/entries directory before you do this.

    + +

    general tips/troubleshooting

    + +

    future features

    +

    these are a few ideas being kicked around, or under active development:

    + +

    other ideas are listed on github as +upcoming features or feature requests!

    \ No newline at end of file diff --git a/doc/manual.md b/doc/manual.md new file mode 100644 index 0000000..a09ec22 --- /dev/null +++ b/doc/manual.md @@ -0,0 +1,196 @@ +# FEELS MANUAL # + +`ttbp` stands for "tilde.town blogging platform", the original working name for +this project. the complete codebase is available on +[github](https://github.com/modgethanc/ttbp). + +`ttbp` runs from the command line, providing a hub for writing personal blog +posts and reading posts written by other users of tilde.town. it's a little bit +like livejournal or dreamwidth or tumblr. you can opt to publish your posts to a +public html file hosted on your tilde page, to tilde.town's gopher server, or +keep all your entries private to the tilde.town server. + +this is a project that runs on tilde.town, so all users of this program are +expected to operate under the tilde.town [code of +conduct](http://tilde.town/wiki/conduct.html). content/personal issues should be +worked out according to the CoC, with support from the [administrative +team](http://tilde.town/wiki/administration/index.html) if needed. + +### support + +if you're having trouble getting started, or run into program errors or strange +behavior, please send internal tilde.town mail to ~endorphant and i'll try to +sort things out! + +there's also a function from the main menu that lets you send feedback/inquiries +to me directly; this uses internal tilde.town mail, which is what i'll respond +to. + +### writing entries + +entries are recorded as plaintext files in your `~/.ttbp/entries` directory. +`ttbp` will use your selected editor to open and write files; each day is its +own entry, like a diary page. at midnight for whatever timezone you've set for +your user account on tilde.town, you'll get a fresh entry. if you don't write +any feels on a particular day, no entries will show up there. + +when you save and quit the text editor, your entry will automatically propagate +to the global feels list; if you've opted to publish your feels to html/gopher, +those files will update immediately. you can always go back to the current day's +entry and edit/add as you'd like, but older entries will not be available for +editing from `ttbp`. + +*(since files are just stored as plaintext in your directory, it's possible to +edit and move old entries directly from the command line. however, changing old +entries might cause strange things to happen with timestamps. the main program +looks at the filename first for setting the date, then the last modified time to +sort recent posts. it expects YYYMMDD.txt as the filename; anything else won't +show up as a valid entry. yes, this means you can post things out of date order +by creating files with any date you want.)* + +#### general entry-writing notes + +* you can use [markdown](https://daringfireball.net/projects/markdown/syntax) +* you can use html +* you can also put things between `` to have them show up + in the feed but not render in a browser (but people can still read them with + view-source) + +### reading other feels + +the `browse global feels` feature shows the ten most recent entries that anyone +has written on ttbp. this list is only accessible from within tilde.town, +although individual entries may be posted to html or gopher. + +you can also pull up a list of a single user's feels through `check out your +neighbors`, which displays all users who are writing on `ttbp` based on their +most recently updated entry, and a link to their public html blog if they've +opted to publish their posts. + +**please note!** entries written on `ttbp` should be considered sensitive, +private information, even if a particular user is publishing entries in a +world-viewable way! please be respectful about having access to other people's +feels, and do not copy/repeat any information without getting their explicit +permission. tilde.town operates on a high level of mutual trust, and `ttbp` is +designed to give individuals control over their content. + +### privacy + +when you start your ttbp, you have the option of publishing or not publishing +your blog. + +if you opt to not publish, your entires will never be accessible from outside of +the tilde.town network; other tilde.town users will still be able to read your +entries through the ttbp interface, or by directly accessing your +`~/.ttbp/entries` directory. + +if you want to further protect your entries, you can `chmod 700` your entries +directory. + +if you opt to publish, the program creates a directory `~/.ttbp/www` where it +stores all html files it generates, and symlinks this from your `~/public_html` +with your chosen blog directory. your blog will also be listed on the [main ttbp +page](https://tilde.town/~endorphant/ttbp). + +you can also opt to publish to gopher, and the program will automatically +generate a gophermap of your feels. + +you can set publishing status on individual entries, or bury individual feels; +see "data management" below for details. + +### data management + +the `manage your feels` menu provides several tools for organizing your feels. +these are all actions you can perform manually from the command line, but doing +them from within the program can help keep your files properly linked up. + +* **read over feels**--a list of all your entries, which you can open and + read like any other feel +* **modify feels publishing**--this lets you toggle privacy on individual + posts. entries marked `(nopub)` will not get written to html or gopher, + and toggling them from this menu will immediately publish or unpublish + that entry (if you're not publishing your posts at all, these settings + won't matter, since your feels will never show up outside of tilde.town) +* **backup your feels**--makes a .tar.gz of all your entries, saving one + copy to `~/.ttbp/backups/` with the current date, and a second copy to + your home directory for safekeeping. +* **import a feels backup**--unpacks a backup file into your current feels + list. this tool checks the `~/.ttbp/backups` directory for archives, and + expects a file created by the above backup utility. if it detects any file + collisions, it will preserve your current live copy and leave the backup + verison in a temp directory, and notify you that this happened. also, any + entries that were previously marked as `(nopub)` will retain their nopub + status. +* **bury some feels**--hides individual feels from viewing; entries are + moved to `~/.ttbp/buried` (and marked with a unique timestamp to prevent + file collision) with permissions set to 600, meaning no one except you + will be able to open that file. these entries are also hidden from your + own view from `read over feels`, and you'll have to open the files from + the command line if you want to see them. this is intended to be a + permament action, so you'll be asked to type the entry date once to load + the feel, then shown a preview of that feel, and then type the date again + to confirm burying. +* **delete feels by day**--*permanently removes individual entries*, + including deleting published html/gopher files if needed. this action is + not recoverable, unless you have a backup to restore; you'll be asked to + type the entry date once to load the feel, then shown a preview of that + feel, and then type the date again to confirm deletion. +* **purge all feels**--*permanently removes all feels*, including deleting + all published html/gopher files if needed. this action is not recoverable, + unless you have a backup to restore. you'll be asked to type a + one-time-use purge code to confirm this action. +* **wipe feels account**--*permanently removes all data associated with + feels*, including deleting any published hmtl/gopher files and removing + your `~/.ttbp` directory. any backups that you have in `~/.ttbp/backups` + will also be deleted with this action (which is why the backup function + makes a second copy for safekeeping in your home directory). you will no + longer show up in any lists as a user. + +### settings + +the settings menu lets you change specific options for handling your feels and +using the interface. + +* **editor**--set your text editor +* **gopher**--opt in or out of automatically posting to gopher +* **post as nopub**--set whether posts default to being published or not + published (if you're not publishing your feels, this doesn't matter) +* **publish dir**--set the directory under you `public_html` where feels will be + published (if you're not publishing your feels, this defaults to `None`) +* **publishing**--opt in or out of automatically publishing entries to a + world-readable html page +* **rainbows**--opt in or out of having multicolored menu text + +### changing your page layout + +you can modify how your blog looks by editing the stylesheet or header and +footer files. the program sets you up with basic default. if you break your page +somehow, you can force the program to regenerate your configuration by deleting +your ~/.ttbp directory entirely. **you might want to back up your +~/.ttbp/entries directory before you do this.** + +* to modify your stylesheet, edit your ~/.ttbp/config/style.css +* to modify the page header, edit your ~/.ttbp/config/header.txt + * there's a place marked off in the default header where you can safely put + custom HTML elements! +* to modify the page footer, edit your ~/.ttbp/config/footer.txt + +### general tips/troubleshooting + +* if the date looks like it's ahead or behind, it's because you haven't set + your local timezone yet. here are some + [timezone setting instructions](http://www.cyberciti.biz/faq/linux-unix-set-tz-environment-variable/) +* the feels burying tool will effectively clear your post for the day; you can + use this feature to start a fresh entry on a particular day by burying the + current day's feels and then editing a new file + +### future features + +these are a few ideas being kicked around, or under active development: + +* stylesheet/theme selector +* better entry display within ttbp (currently just offloads to `less`) +* buried feels browser + +other ideas are listed on github as +[upcoming features](https://github.com/modgethanc/ttbp/issues?q=is%3Aissue+is%3Aopen+label%3A"upcoming+features") or [feature requests](https://github.com/modgethanc/ttbp/issues?q=is%3Aissue+is%3Aopen+label%3A"feature+request")! diff --git a/setup.py b/setup.py index 3d49f33..a6e251a 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ from setuptools import setup setup( name='ttbp', - version='0.11.2', + version='0.12.0', description='command line social blogging tool used on tilde.town', url='https://github.com/modgethanc/ttbp', author='~endorphant', diff --git a/ttbp/config/__init__.py b/ttbp/config/__init__.py index 495f874..7a00748 100644 --- a/ttbp/config/__init__.py +++ b/ttbp/config/__init__.py @@ -1,6 +1,7 @@ from __future__ import absolute_import import os import sys +import time from .. import util @@ -67,11 +68,14 @@ USER_HOME = os.path.expanduser('~') PATH = os.path.join(USER_HOME, '.ttbp') PUBLIC = os.path.join(USER_HOME, 'public_html') WWW = os.path.join(PATH, 'www') +GOPHER_ENTRIES = os.path.join(PATH, 'gopher') GOPHER_PATH = os.path.join(USER_HOME, 'public_gopher', 'feels') USER_CONFIG = os.path.join(PATH, 'config') TTBPRC = os.path.join(USER_CONFIG, 'ttbprc') -USER_DATA = os.path.join(PATH, 'entries') -NOPUB = os.path.join(USER_CONFIG, "nopub") +MAIN_FEELS = os.path.join(PATH, 'entries') +BURIED_FEELS = os.path.join(PATH, 'buried') +NOPUB = os.path.join(USER_CONFIG, 'nopub') +BACKUPS = os.path.join(PATH, 'backups') ## UI @@ -82,9 +86,102 @@ ___________________________________________________________ | ____ ____ ____ _ ____ ____ _ _ ____ _ _ _ ____ | | |___ |___ |___ | [__ |___ |\ | | __ | |\ | |___ | | | |___ |___ |___ ___] |___ | \| |__] | | \| |___ | -| ver 0.11.2 (rainbows) | +| ver 0.12.0 | |__________________________________________________________| '''.lstrip() +# ~ u n s t a b l e e x p e r i m e n t a l b r a n c h ~ +#'''.lstrip() + +## page texts + +intro_prompt = """ +i don't recognize you, stranger. let's make friends. + +the feels engine is an internal blogging platform on tilde.town. it assists you +in recording your feels, giving you the option to publish to html or gopher, and +read the feels of other users on tilde.town. + +press to set up an account, or to quit. +""".lstrip() + +credits = """ +ttbp was written for tilde.town by ~endorphant in python. the codebase is +publicly available on github at https://github.com/modgethanc/ttbp + +tips for development are accepted at https://liberapay.com/modgethanc/ + +other contributors: + ~vilmibm, packaging help and gopher support + ~sanqui, the bug swatter + ~sinacutie, for css updates + +if you have ideas for ttbp, you are welcome to contact me to discuss them; +please send me tildemail or open a github issue. i am not a very experienced +developer, and ttbp is one of my first public-facing projects, so i appreciate +your patience while i learn how to be a better developer! + +i'd love to hear about your ideas and brainstorm about new features! + +thanks to everyone who reads, listens, writes, and feels.""" + +recording = """ +feels will be recorded for today, {today}. + +if you've already started recording feels for this day, you +can pick up where you left off. + +you can write your feels in plaintext, markdown, html, or a mixture of +these. + +press to begin recording your feels in your chosen text +editor. + +""".format(today=time.strftime("%d %B %Y")) + +bury_feels_prompt = """\ +burying a feel removes it from view, including your own. buried feels are +stashed in a private directory at: + + {buried_dir} + +you can visit your feels there from the command line, but no one else can view +those files. + +(a buried feels browser is in the works; for now, you'll have to use the +command line to view your buried feels) + +which day's feels do you want to bury? + +YYYYMMDD (or 'q' to cancel)> """.format(buried_dir=BURIED_FEELS) + +account_wipe_prompt = """\ +warning! ! ! this action is irreversible!!! + +this tool will remove your entire presence from the feels engine. this includes +all posts, settings, and published html/gopher feels. you will no longer be +listed anywhere as a user here. + +there is no way for me to help you recover any part of your feels acccount. i +respect your need to do this from time to time, so please be sure you're ready! + +i recommend that you make a backup of your feels and stash them somewhere safe, +just in case a future version of you still wants to look them over.""" + +feels_purge_prompt = """\ +warning! ! ! this action is irreversible!!! + +there is no way for me to help you recover your feels if you purge them all. i +respect your need to do this from time to time, so please be sure you're ready! + +i recommend that you make a backup of your feels and stash them somewhere safe, +just in case a future version of you still wants to look them over. +""" + +mystery_error = """\ +sorry, something went wrong! please try to address the error and try again. +if you need help, ask in IRC or send mail to ~endorphant and we'll try to +figure it out! +""".lstrip() ## update announcements @@ -152,6 +249,47 @@ version 0.9.3 features: * added a new option to allow setting entries to default to either public or non-public on posting; this option only really makes sense if you're already publishing to html/gopher, but is available either way! - - you can find this option under 'settings' as 'post as nopub'.""" + + you can find this option under 'settings' as 'post as nopub'.""", + "0.11.3": """ +~[version 0.11.3 update]~ + + * thanks to ~sinacutie, you can now set custom css for the permalink text + styling on your html page. the default permalink style has been added to + your current css file, and shouldn't change the appearance of your page. + + if you're not using custom css, don't worry about this!""", + "0.12.0": """ +~[version 0.12.0 update]~ + + a lot of new stuff this time! from the main menu, option (1) is now called + "manage your feels", which contains an expanded set of tools for organizing + your feels: + * read over feels (a list of all your entries) + * modify feels publishing (toggle privacy on individual entries) + * backup your feels (makes a .tar.gz of all your entries) + * import a feels backup (unpacks a backup into your feels) + * bury some feels (hide individual entries from view) + * delete feels by day (permanently remove individual entries) + * purge all feels (permanently remove all entries) + * wipe feels account (permanently remove everything associated with + ttbp) + + each of these tools has expanded descriptions and instructions from the + menu, so check them out if you're curious! all of the irreversibl data + management actions have confirmation actions, so it's easy to cancel if you + accidentally access a tool. + + also, i've updated the documentation file to reflect recent feature changes, + including some more details about how things work under the hood, and some + clarifications on how existing features work. please give it a read through + if you have a chance, and let me know if there's anything else i can + improve! + + lastly, i just wanted to mention that i do accept tips for my dev work at + https://liberapay.com/modgethanc/ but there's no pressure to donate at all, + i'm just making this option available for anyone whose financially stable + and wants to kick some spare change my way; this is a labor of love, and i'm + happy to work on it regardless :) +""" } diff --git a/ttbp/config/defaults/style.css b/ttbp/config/defaults/style.css index c14edc6..204eec7 100644 --- a/ttbp/config/defaults/style.css +++ b/ttbp/config/defaults/style.css @@ -28,6 +28,12 @@ body { padding: 1em; } +.entry p.permalink { + font-size: .6em; + font-color: #808080; + text-align: right; +} + .entry h5 { text-align: right; margin-top: .2em; diff --git a/ttbp/core.py b/ttbp/core.py index 7084fdd..d35db94 100644 --- a/ttbp/core.py +++ b/ttbp/core.py @@ -41,6 +41,8 @@ import json from . import chatter from . import config +from . import gopher +from . import util FEED = os.path.join("/home", "endorphant", "public_html", "ttbp", "index.html") SETTINGS = {} @@ -75,40 +77,42 @@ def reload_ttbprc(ttbprc={}): SETTINGS = ttbprc +def get_files(feelsdir=config.MAIN_FEELS): + """Returns a list of user's feels in the given directory (defaults to main + feels dir)""" -def get_files(): - """Returns a list of user's feels.""" files = [] - for filename in os.listdir(config.USER_DATA): + for filename in os.listdir(feelsdir): if nopub(filename): - link = os.path.join(config.WWW, - os.path.splitext( - os.path.basename(filename))[0]+".html") - if os.path.exists(link): - subprocess.call(["rm", link]) - continue - filename = os.path.join(config.USER_DATA, filename) - if os.path.isfile(filename) and valid(filename): - files.append(filename) + unpublish_feel(filename) + else: + filename = os.path.join(feelsdir, filename) + if os.path.isfile(filename) and valid(filename): + files.append(filename) files.sort() files.reverse() return files - -def load_files(): +def load_files(feelsdir=config.MAIN_FEELS): ''' file loader * reads user's nopub file - * loads all valid filenames that are not excluded in nopub to global files list + * calls get_files() to load all files for given directory + * re-renders main html file and/or gopher if needed ''' global FILES load_nopubs() - FILES = get_files() + FILES = get_files(feelsdir) + + if publishing(): + write_html("index.html") + if SETTINGS.get('gopher'): + gopher.publish_gopher('feels', FILES) def load_nopubs(): """Load a list of the user's nopub entries. @@ -127,7 +131,7 @@ def load_nopubs(): ## html outputting -def write(outurl="default.html"): +def write_html(outurl="default.html"): ''' main page renderer @@ -167,7 +171,7 @@ def write_page(filename): url ''' - outurl = os.path.join(config.WWW, "".join(parse_date(filename))+".html") + outurl = os.path.join(config.WWW, "".join(util.parse_date(filename))+".html") outfile = open(outurl, "w") outfile.write("\n\n") @@ -197,7 +201,7 @@ def write_entry(filename): * return as list of strings ''' - date = parse_date(filename) + date = util.parse_date(filename) entry = [ "\t\t



    \n", @@ -207,7 +211,7 @@ def write_entry(filename): ] raw = [] - rawfile = open(os.path.join(config.USER_DATA, filename), "r") + rawfile = open(os.path.join(config.MAIN_FEELS, filename), "r") for line in rawfile: raw.append(line) @@ -221,7 +225,7 @@ def write_entry(filename): # entry.append("

    \n\t\t\t

    ") #entry.append("

    \n") - entry.append("\t\t\t

    permalink

    \n") + entry.append("\t\t\t

    permalink

    \n") entry.append("\n\t\t\n") return entry @@ -316,7 +320,7 @@ def meta(entries = FILES): except subprocess.CalledProcessError: wc = "???" timestamp = time.strftime("%Y-%m-%d at %H:%M", time.localtime(mtime)) - date = "-".join(parse_date(filename)) + date = "-".join(util.parse_date(filename)) author = os.path.split(os.path.split(os.path.split(os.path.split(filename)[0])[0])[0])[1] meta.append([filename, mtime, wc, timestamp, date, author]) @@ -345,23 +349,6 @@ def valid(filename): return True -def parse_date(file): - ''' - parses date out of pre-validated filename - - * assumes a filename of YYYYMMDD.txt - * returns a list: - [0] 'YYYY' - [1] 'MM' - [2] 'DD' - ''' - - rawdate = os.path.splitext(os.path.basename(file))[0] - - date = [rawdate[0:4], rawdate[4:6], rawdate[6:]] - - return date - def find_ttbps(): ''' returns a list of users with a ttbp by checking for a valid ttbprc @@ -457,12 +444,7 @@ def toggle_nopub(filename): NOPUBS.remove(filename) else: NOPUBS.append(filename) - live_html = os.path.join(config.WWW, filename.split(".")[0]+".html") - if os.path.exists(live_html): - subprocess.call(["rm", live_html]) - live_gopher = os.path.join(config.GOPHER_PATH, filename) - if os.path.exists(live_gopher): - subprocess.call(["rm", live_gopher]) + unpublish_feel(filename) nopub_file = open(config.NOPUB, 'w') nopub_file.write("""\ @@ -478,6 +460,93 @@ def toggle_nopub(filename): return action +def bury_feel(filename): + """buries given filename; this removes the feel from any publicly-readable + location, and moves the textfile to user's private feels directory. + + timestring will be added to the filename to disambiguate and prevent + filename collisions. + + creates buried feels dir if it doesn't exist. + + regenerates feels list and republishes.""" + + if not os.path.exists(config.BURIED_FEELS): + os.mkdir(config.BURIED_FEELS) + subprocess.call(["chmod", "700", config.BURIED_FEELS]) + + buryname = os.path.splitext(os.path.basename(filename))[0]+"-"+str(int(time.time()))+".txt" + + subprocess.call(["mv", os.path.join(config.MAIN_FEELS, filename), os.path.join(config.BURIED_FEELS, buryname)]) + subprocess.call(["chmod", "600", os.path.join(config.BURIED_FEELS, buryname)]) + + if publishing(): + unpublish_feel(filename) + + load_files() + + return os.path.join(config.BURIED_FEELS, buryname) + +def delete_feel(filename): + """deletes given filename; removes the feel from publicly-readable + locations, then deletes the original file.""" + + feel = os.path.join(config.MAIN_FEELS, filename) + if os.path.exists(feel): + subprocess.call(["rm", feel]) + unpublish_feel(filename) + load_files(config.MAIN_FEELS) + +def unpublish_feel(filename): + """takes given filename and removes it from public_html and gopher_html, if + those locations exists. afterwards, regenerate index files appropriately.""" + + live_html = os.path.join(config.WWW, + os.path.splitext(os.path.basename(filename))[0]+".html") + if os.path.exists(live_html): + subprocess.call(["rm", live_html]) + live_gopher = os.path.join(config.GOPHER_PATH, filename) + if os.path.exists(live_gopher): + subprocess.call(["rm", live_gopher]) + +def process_backup(filename): + """takes given filename and unpacks it into a temp directory, then returns a + list of filenames with collisions filtered out. + + ignores any invalidly named files or files that already exist, to avoid + clobbering current feels. ignored files are left in the archive directory + for the user to manually sort out.""" + + backup_dir = os.path.splitext(os.path.splitext(os.path.basename(filename))[0])[0] + backup_path = os.path.join(config.BACKUPS, backup_dir) + + if not os.path.exists(backup_path): + subprocess.call(["mkdir", backup_path]) + + subprocess.call(["chmod", "700", backup_path]) + subprocess.call(["tar", "-C", backup_path, "-xf", filename]) + backup_entries = os.path.join(backup_path, "entries") + + backups = os.listdir(backup_entries) + current = os.listdir(config.MAIN_FEELS) + + imported = [] + + for feel in backups: + if os.path.basename(feel) not in current: + imported.append(os.path.join(backup_entries, feel)) + + imported.sort() + return imported + +def import_feels(backups): + """takes a list of filepaths and copies those to current main feels. + + this does not check for collisions. + """ + + pass + ############# ############# diff --git a/ttbp/gopher.py b/ttbp/gopher.py index 00cfffd..17f02a3 100644 --- a/ttbp/gopher.py +++ b/ttbp/gopher.py @@ -7,7 +7,8 @@ import time import subprocess from . import util -from .core import parse_date +from . import config +#from .core import parse_date GOPHER_PROMPT = """ @@ -73,7 +74,7 @@ def publish_gopher(gopher_path, entry_filenames): if not os.path.exists(gopher_entry_symlink): subprocess.call(["ln", "-s", entry_filename, gopher_entry_symlink]) - label = "-".join(parse_date(entry_filename)) + label = "-".join(util.parse_date(entry_filename)) gophermap.write('0{file_label}\t{filename}\n'.format( file_label=label, filename=filename)) @@ -107,3 +108,11 @@ def setup_gopher(gopher_path): os.makedirs(gopher_entries) subprocess.call(["ln", "-s", gopher_entries, ttbp_gopher]) + +def unpublish(): + """blanks all gopher things and recreates the directories.""" + + subprocess.call(["rm", "-rf", config.GOPHER_PATH]) + subprocess.call(["rm", "-rf", config.GOPHER_ENTRIES]) + os.mkdir(config.GOPHER_ENTRIES) + subprocess.call(["ln", "-s", config.GOPHER_ENTRIES, config.GOPHER_PATH]) diff --git a/ttbp/ttbp.py b/ttbp/ttbp.py index ebaea39..6ee3b26 100644 --- a/ttbp/ttbp.py +++ b/ttbp/ttbp.py @@ -34,6 +34,7 @@ https://github.com/modgethanc/ttbp from __future__ import absolute_import import os +import sys import tempfile import subprocess import time @@ -50,7 +51,7 @@ from . import chatter from . import gopher from . import util -__version__ = "0.11.2" +__version__ = "0.12.0" __author__ = "endorphant to begin, or to get out of here.""") + input(config.intro_prompt) except KeyboardInterrupt: print("\n\nthanks for checking in! i'll always be here.\n\n") quit() @@ -259,7 +259,7 @@ press to begin, or to get out of here.""") time.sleep(1) print("...") - time.sleep(1) + time.sleep(.5) ## record user in source list users = open(config.USERFILE, 'a') @@ -272,7 +272,7 @@ press to begin, or to get out of here.""") print("\ngenerating feels at {path}...".format(path=config.PATH).rstrip()) subprocess.call(["mkdir", config.PATH]) subprocess.call(["mkdir", config.USER_CONFIG]) - subprocess.call(["mkdir", config.USER_DATA]) + subprocess.call(["mkdir", config.MAIN_FEELS]) versionFile = os.path.join(config.PATH, "version") open(versionFile, "w").write(__version__) @@ -291,13 +291,21 @@ press to begin, or to get out of here.""") f.write(config.DEFAULT_STYLE) ## run user-interactive setup and load core engine - time.sleep(1) + time.sleep(0.5) print("done setting up feels!") print("\nthese are the default settings. you can change any of them now, or change them later at any time!!") setup() core.load(SETTINGS) - input("\nyou're all good to go, "+chatter.say("friend")+"! hit to continue.\n\n") + input(""" + +you're all good to go, {friend}! if you have any questions about how things +work here, check out the documentation from the main menu, ask in IRC, or +drop ~endorphant a line! + +hit to continue. +""".format(friend=chatter.say("friend"))) + return "" def gen_header(): @@ -406,7 +414,7 @@ def setup_repair(): save_settings() print("...") - time.sleep(1) + time.sleep(0.5) input("\nyou're all good to go, "+chatter.say("friend")+"! hit to continue.\n\n") def setup(): @@ -436,68 +444,74 @@ def setup(): redraw(EJECT) return SETTINGS - if choice in QUITS: + if choice is not "": + + if choice in QUITS: + redraw() + return SETTINGS + + # editor selection + if settingList[int(choice)] == "editor": + SETTINGS.update({"editor": select_editor()}) + redraw("text editor set to: {editor}".format(editor=SETTINGS["editor"])) + save_settings() + return setup() + + # publishing selection + elif settingList[int(choice)] == "publishing": + SETTINGS.update({"publishing":select_publishing()}) + core.reload_ttbprc(SETTINGS) + update_publishing() + redraw("publishing set to {publishing}".format(publishing=SETTINGS.get("publishing"))) + save_settings() + return setup() + + # publish dir selection + elif settingList[int(choice)] == "publish dir": + publish_dir = select_publish_dir() + SETTINGS.update({"publish dir": publish_dir}) + #update_publishing() + + if publish_dir is None: + redraw("sorry, i can't set a publish directory for you if you don't have html publishing enabled. please enable publishing to continue.") + else: + redraw("publishing your entries to {url}/index.html".format( + url="/".join([config.LIVE+config.USER, + str(SETTINGS.get("publish dir"))]))) + save_settings() + return setup() + + # gopher opt-in + elif settingList[int(choice)] == "gopher": + SETTINGS.update({'gopher': gopher.select_gopher()}) + redraw('gopher publishing set to: {gopher}'.format(gopher=SETTINGS['gopher'])) + update_gopher() + save_settings() + return setup() + + # rainbow menu selection + elif settingList[int(choice)] == "rainbows": + SETTINGS.update({"rainbows": toggle_rainbows()}) + redraw("rainbow menus set to {rainbow}".format(rainbow=SETTINGS.get("rainbows"))) + save_settings() + return setup() + + #nopub toggling + elif settingList[int(choice)] == "post as nopub": + SETTINGS.update({"post as nopub": toggle_pub_default()}) + redraw("posting default set to {nopub}".format(nopub=SETTINGS.get("post as nopub"))) + save_settings() + return setup() + + input("\nyou're all good to go, {friend}! hit to continue.\n\n".format(friend=chatter.say("friend"))) redraw() + return SETTINGS - # editor selection - if settingList[int(choice)] == "editor": - SETTINGS.update({"editor": select_editor()}) - redraw("text editor set to: {editor}".format(editor=SETTINGS["editor"])) - save_settings() + else: + redraw("now changing your settings. press if you didn't mean to do this.") return setup() - # publishing selection - elif settingList[int(choice)] == "publishing": - SETTINGS.update({"publishing":select_publishing()}) - core.reload_ttbprc(SETTINGS) - update_publishing() - redraw("publishing set to {publishing}".format(publishing=SETTINGS.get("publishing"))) - save_settings() - return setup() - - # publish dir selection - elif settingList[int(choice)] == "publish dir": - publish_dir = select_publish_dir() - SETTINGS.update({"publish dir": publish_dir}) - #update_publishing() - - if publish_dir is None: - redraw("sorry, i can't set a publish directory for you if you don't have html publishing enabled. please enable publishing to continue.") - else: - redraw("publishing your entries to {url}/index.html".format( - url="/".join([config.LIVE+config.USER, - str(SETTINGS.get("publish dir"))]))) - save_settings() - return setup() - - # gopher opt-in - elif settingList[int(choice)] == "gopher": - SETTINGS.update({'gopher': gopher.select_gopher()}) - redraw('gopher publishing set to: {gopher}'.format(gopher=SETTINGS['gopher'])) - update_gopher() - save_settings() - return setup() - - # rainbow menu selection - elif settingList[int(choice)] == "rainbows": - SETTINGS.update({"rainbows": toggle_rainbows()}) - redraw("rainbow menus set to {rainbow}".format(rainbow=SETTINGS.get("rainbows"))) - save_settings() - return setup() - - #nopub toggling - elif settingList[int(choice)] == "post as nopub": - SETTINGS.update({"post as nopub": toggle_pub_default()}) - redraw("posting default set to {nopub}".format(nopub=SETTINGS.get("post as nopub"))) - save_settings() - return setup() - - input("\nyou're all good to go, {friend}! hit to continue.\n\n".format(friend=chatter.say("friend"))) - redraw() - - return SETTINGS - def save_settings(): """ Save current settings. @@ -516,7 +530,7 @@ def main_menu(): menuOptions = [ "record your feels", - "review your feels", + "manage your feels", "check out your neighbors", "browse global feels", "scribble some graffiti", @@ -537,18 +551,13 @@ def main_menu(): if choice == '0': redraw() today = time.strftime("%Y%m%d") - write_entry(os.path.join(config.USER_DATA, today+".txt")) + write_entry(os.path.join(config.MAIN_FEELS, today+".txt")) core.www_neighbors() elif choice == '1': - if core.publishing(): - intro = "here are some options for reviewing your feels:" - redraw(intro) - review_menu(intro) - core.load_files() - core.write("index.html") - else: - redraw("your recorded feels, listed by date:") - view_feels(config.USER) + intro = "here are some options for managing your feels:" + redraw(intro) + review_menu(intro) + core.load_files() elif choice == '2': users = core.find_ttbps() prompt = "the following {usercount} {are} recording feels on ttbp:".format( @@ -571,7 +580,7 @@ def main_menu(): redraw() show_credits() elif choice == '8': - subprocess.call(["lynx", os.path.join(config.INSTALL_PATH, "..", "README.html")]) + subprocess.call(["lynx", os.path.join(config.INSTALL_PATH, "..", "doc", "manual.html")]) redraw() elif choice in QUITS: return stop() @@ -614,26 +623,72 @@ def review_menu(intro=""): menuOptions = [ "read over feels", - "modify feels publishing" + "modify feels publishing", + "backup your feels", + "import a feels backup", + "bury some feels", + "delete feels by day", + "purge all feels", + "wipe feels account" ] util.print_menu(menuOptions, SETTINGS.get("rainbows", False)) - choice = util.list_select(menuOptions, "what would you like to do with your feels? (or 'back' to return home) ") + choice = util.list_select(menuOptions, "what would you like to do with your feels? (or 'q' to return home) ") + + top = "" + hasfeels = len(os.listdir(config.MAIN_FEELS)) > 0 + nofeels = "you don't have any feels to work with, "+chatter.say("friend")+"\n\n> " if choice is not False: if choice == 0: - redraw("your recorded feels, listed by date:") - view_feels(config.USER) + if hasfeels: + redraw("your recorded feels, listed by date:") + view_feels(config.USER) + else: + top = nofeels elif choice == 1: - redraw("publishing status of your feels:") - list_nopubs(config.USER) + if hasfeels: + redraw("publishing status of your feels:") + list_nopubs(config.USER) + else: + top = nofeels + elif choice == 2: + if hasfeels: + redraw("FEELS BACKUP") + backup_feels() + else: + top = nofeels + elif choice == 3: + redraw("loading feels backup") + load_backup() + elif choice == 4: + if hasfeels: + redraw("burying feels") + bury_feels() + else: + top = nofeels + elif choice == 5: + if hasfeels: + redraw("deleting feels") + delete_feels() + else: + top = nofeels + elif choice == 6: + if hasfeels: + redraw("!!!PURGING ALL FEELS!!!") + purge_feels() + else: + top = nofeels + elif choice == 7: + redraw("!!! WIPING FEELS ACCOUNT !!!") + wipe_account() else: redraw() return - redraw(intro) - return review_menu() + redraw(top+intro) + return review_menu(intro) def view_neighbors(users, prompt): ''' @@ -694,7 +749,7 @@ def view_neighbors(users, prompt): sortedUsers.append(user[0]) userIndex.append(user[2]) - choice = menu_handler(sortedUsers, "pick a townie to browse their feels, or type 'back' or 'q' to go home: ", 15, SETTINGS.get("rainbows", False), prompt) + choice = menu_handler(sortedUsers, "pick a townie to browse their feels, or type 'q' to go home: ", 15, SETTINGS.get("rainbows", False), prompt) if choice is not False: redraw("~{user}'s recorded feels, listed by date: \n".format(user=userIndex[choice])) @@ -708,7 +763,6 @@ def view_feels(townie): ''' generates a list of all feels by given townie and displays in date order; allows selection of one feel to read. - ''' metas, owner = generate_feels_list(townie) @@ -733,7 +787,7 @@ def generate_feels_list(user): showpub = False if user == config.USER: - entryDir = config.USER_DATA + entryDir = config.MAIN_FEELS owner = "your" if core.publishing(): showpub = True @@ -749,29 +803,320 @@ def generate_feels_list(user): return metas, owner +def backup_feels(): + """creates a tar.gz of user's entries directory""" + + backupfile = os.path.join(os.path.expanduser('~'), "feels-backup-"+time.strftime("%Y%m%d-%H%M%S")+".tar.gz") + + print("""\ +i'm preparing all of your entries for backup.""") + + print("...") + time.sleep(1) + + print(""" +ready to go! a backup file will be saved to your home directory at: +{backuploc}""".format(backuploc=backupfile)) + + ans = util.input_yn("""\ + +would you like to create this backup? + +please enter""") + + if ans: + if not subprocess.call(["tar", "-C", config.PATH, "-czf", backupfile, "entries"]): + subprocess.call(["chmod", "600", backupfile]) + if not os.path.exists(config.BACKUPS): + subprocess.call(["mkdir", config.BACKUPS]) + subprocess.call(["chmod", "700", config.BACKUPS]) + subprocess.call(["cp", backupfile, config.BACKUPS]) + print("\nbackup saved! i also put a copy at {backup_dir} for you.".format(backup_dir = config.BACKUPS)) + else: + print(config.mystery_error) + else: + print("no problem, {friend}; come back whenever if you want a backup!".format(friend=chatter.say("friend"))) + + input("\n\npress to go back to managing your feels.\n\n") + redraw() + + return + +def delete_feels(): + """handles deleting feels one at a time""" + + feel = input("""which day's feels do you want to load for deletion? + +YYYYMMDD (or 'q' to cancel)> """) + + if feel in util.BACKS: + return + + print("...") + time.sleep(0.1) + print("""\ +here's a preview of that feel. press when you're done reviewing! +-------------------------------------------------------------""") + + if subprocess.call(["less", os.path.join(config.MAIN_FEELS, feel+".txt")]): + redraw("deleting feels") + print("""\ +sorry, i couldn't find feels for {date}! + +please try again, or type to cancel. +""".format(date=feel)) + return delete_feels() + + print(""" +------------------------------------------------------------- + +feels deletion is irreversible! if you're sure you want to delete this feel, +type the date again to confirm, or 'q' to cancel.""") + + confirm = input("[{feeldate}]> ".format(feeldate=feel)) + + if confirm == feel: + print("...") + time.sleep(0.5) + core.delete_feel(feel+".txt") + print("feels deleted!") + else: + print("deletion canceled!") + + ans = util.input_yn("""do you want to delete a different feel? +please enter""") + + if ans: + redraw("deleting feels") + return delete_feels() + else: + print("okay! please come back any time if you want to delete old feels!") + input("\n\npress to go back to managing your feels.\n\n") + redraw() + + return + + +def purge_feels(): + """handles deleting all feels""" + + print(config.feels_purge_prompt) + + print("...") + time.sleep(0.5) + print("...loading feels...") + time.sleep(1) + print("...") + + feelscount = len(os.listdir(config.MAIN_FEELS)) + + if feelscount > 0: + purgecode = util.genID(5) + + print(""" + +i've loaded up all {count} of your feels for purging. if you're ready, carefully +type the following purge code: + _________ + | | + | {purgecode} | + |_______| +""".format(purgecode=purgecode, count=feelscount)) + + ans = input("(leave blank or type anything else to cancel) > ") + + if ans == purgecode: + print("...") + time.sleep(0.5) + unpublish() + + if not subprocess.call(["rm", "-rf", config.MAIN_FEELS]): + subprocess.call(["mkdir", config.MAIN_FEELS]) + core.load_files() + print("ALL FEELS PURGED! you're ready to start fresh!") + else: + print(config.mystery_error) + else: + print("\nfeels purge canceled! you're welcome to come back again.") + else: + print("you don't have any feels to purge, "+chatter.say("friend")) + + input("\n\npress to go back to managing your feels.\n\n") + redraw() + + return + +def wipe_account(): + """handles wiping feels account""" + + print(config.account_wipe_prompt) + + print("...") + time.sleep(0.5) + print("...packaging up all your feels...") + time.sleep(1) + print("...") + purgecode = util.genID(5) + + print(""" +your account is all packed up! if you're ready, carefully type the following +purge code: + _________ + | | + | {purgecode} | + |_______| +""".format(purgecode=purgecode)) + + ans = input("(leave blank or type anything else to cancel) > ") + + if ans == purgecode: + print("...") + time.sleep(0.5) + unpublish() + + if core.publishing(): + publishDir = os.path.join(config.PUBLIC, SETTINGS.get("publish dir")) + make_publish_dir(publishDir) + + + if not subprocess.call(["rm", "-rf", config.PATH]): + print(""" +account deleted! if you ever want to come back, you're always welcome to start +fresh :) + +thank you for sharing your feels!""") + input("\n\npress to exit the feels engine.\n\n") + sys.exit(stop()) + else: + print(config.mystery_error) + else: + print("\naccount deletion canceled! you're welcome to come back again.") + + input("\n\npress to go back to managing your feels.\n\n") + redraw() + + return + +def load_backup(): + """ + scans for archive files (prompting for a file location if not found), opens, + copies files to main feels directory (skipping ones that already exist) + """ + + print("scanning backup directory at {directory}...".format(directory=config.BACKUPS)) + + time.sleep(.5) + print("...\n") + + backups = [] + + try: + for filename in os.listdir(config.BACKUPS): + if "feels-backup" in filename and ".tar" in filename: + backups.append(filename) + except FileNotFoundError: + subprocess.call(["mkdir", config.BACKUPS]) + + if len(backups) < 1: + print(""" +sorry, i didn't find any feels backups! if you have a backup file handy, please +move it to {directory} and try running this tool again.\ +""".format(directory=config.BACKUPS)) + else: + print("backup files found:\n") + choice = menu_handler(backups, "pick a backup file to load (or 'q' to cancel): ", 15, SETTINGS.get("rainbows", False), "backup files found:") + + if choice is not False: + imports = core.process_backup(os.path.join(config.BACKUPS, backups[choice])) + for feel in imports: + print("importing {entry}".format(entry="-".join(util.parse_date(feel)))) + subprocess.call(["mv", feel, config.MAIN_FEELS]) + time.sleep(.01) + + core.load_files() + + tempdir = os.path.join(config.BACKUPS, os.path.splitext(os.path.splitext(os.path.basename(backups[choice]))[0])[0], "entries") + + time.sleep(.5) + print("...\n") + + if len(os.listdir(tempdir)) == 0: + os.rmdir(tempdir) + print("congrats! your feels archive has been unloaded.") + else: + print("""\ +i've unloaded as much as i can, but there are still some feels i didn't copy +over. this is probably because you have current feels on the same days, and i +didn't want to overwrite them. + +you can check out the leftover feels yourself at: + +{directory}""".format(directory=tempdir)) + else: + return + + input("\n\npress to go back to managing your feels.\n\n") + return + +def bury_feels(): + """queries for a feel to bury, then calls the feels burying handler. + """ + + feel = input(config.bury_feels_prompt) + + if feel in util.BACKS: + return + + print("...") + time.sleep(0.1) + print("""\ +here's a preview of that feel. press when you're done reviewing! +-------------------------------------------------------------""") + + if subprocess.call(["less", os.path.join(config.MAIN_FEELS, feel+".txt")]): + redraw("burying feels") + print("""\ +sorry, i couldn't find feels for {date}! + +please try again, or type to cancel. +""".format(date=feel)) + return delete_feels() + + print(""" +------------------------------------------------------------- + +feels burying is irreversible! if you're sure you want to bury this feel, +type the date again to confirm, or 'q' to cancel. +""") + + confirm = input("[{feeldate}]> ".format(feeldate=feel)) + + if confirm == feel: + print("...") + time.sleep(0.5) + core.bury_feel(feel+".txt") + print("feels buried!") + else: + print("burying canceled!") + + ans = util.input_yn("""do you want to bury a different feel? please enter""") + + if ans: + redraw("burying feels") + return bury_feels() + else: + print("okay! please come back any time if you want to bury your feels!") + input("\n\npress to go back to managing your feels.\n\n") + redraw() + + return + def show_credits(): ''' prints author acknowledgements and commentary ''' - print(""" -ttbp was written for tilde.town by ~endorphant in python. the codebase is -publicly available on github at https://github.com/modgethanc/ttbp - -other contributors: - ~vilmibm, packaging help and gopher support - ~sanqui, the bug swatter - -if you have ideas for ttbp, you are welcome to contact me to discuss them; -please send me tildemail or open a github issue. i am not a very experienced -developer, and ttbp is one of my first public-facing projects, so i appreciate -your patience while i learn how to be a better developer! - -i'd love to hear about your ideas and brainstorm about new features! - -thanks to everyone who reads, listens, writes, and feels.\ - """) - + print(config.credits) input("\n\npress to go back home.\n\n") redraw() @@ -779,24 +1124,12 @@ thanks to everyone who reads, listens, writes, and feels.\ ## handlers -def write_entry(entry=os.path.join(config.USER_DATA, "test.txt")): +def write_entry(entry=os.path.join(config.MAIN_FEELS, "test.txt")): ''' main feels-recording handler ''' - entered = input(""" -feels will be recorded for today, {today}. - -if you've already started recording feels for this day, you -can pick up where you left off. - -you can write your feels in plaintext, markdown, html, or a mixture of -these. - -press to begin recording your feels in your chosen text -editor. - -""".format(today=time.strftime("%d %B %Y"))) + entered = input(config.recording) if entered: entryFile = open(entry, "a") @@ -806,22 +1139,24 @@ editor. left = "" + core.load_files() + if SETTINGS.get("post as nopub"): core.toggle_nopub(os.path.basename(entry)) else: if core.publishing(): - core.write("index.html") - left = "posted to {url}/index.html\n\n>".format( + core.write_html("index.html") + left = "posted to {url}/index.html\n\n> ".format( url="/".join( [config.LIVE+config.USER, str(SETTINGS.get("publish dir"))])) if SETTINGS.get('gopher'): - gopher.publish_gopher('feels', core.get_files()) - left += " also posted to your ~/public_gopher!\n" + gopher.publish_gopher('feels', core.FILES) + left += "also posted to your ~/public_gopher!\n\n> " - core.load_files() - redraw(left + " thanks for sharing your feels!") + #core.load_files() + redraw(left + "thanks for sharing your feels!") return @@ -841,6 +1176,14 @@ def set_nopubs(metas, user, prompt): """displays a list of entries for pub/nopub toggling. """ + if core.publishing(): + nopub_note = "" + else: + nopub_note = """\ +(since you're not publishing your entries, these settings don't really matter; +none of your feels will be viewable outside of this server)""" + print(nopub_note + "\n") + entries = [] for entry in metas: pub = "" @@ -848,14 +1191,13 @@ def set_nopubs(metas, user, prompt): pub = "(nopub)" entries.append(""+entry[4]+" ("+p.no("word", entry[2])+") "+"\t"+pub) - choice = menu_handler(entries, "pick an entry from the list, or type 'q' to go back: ", 10, SETTINGS.get("rainbows", False), prompt) + choice = menu_handler(entries, "pick an entry from the list to toggle nopub status, or type 'q' to go back: ", 10, SETTINGS.get("rainbows", False), prompt+"\n\n"+nopub_note) if choice is not False: target = os.path.basename(metas[choice][0]) action = core.toggle_nopub(target) redraw(prompt) - core.write("index.html") if SETTINGS["gopher"]: gopher.publish_gopher('feels', core.get_files()) @@ -1107,14 +1449,14 @@ def select_publish_dir(): publishDir = os.path.join(config.PUBLIC, choice) while os.path.exists(publishDir): - second = input("\n"+publishDir+"""\ - already exists! + second = input(""" +{pDir} already exists! setting this as your publishing directory means this program may delete or overwrite file there! if you're sure you want to use it, hit to confirm. -otherwise, pick another location: """) +otherwise, pick another location: """.format(pDir=publishDir)) if second == "": break @@ -1159,11 +1501,14 @@ def unpublish(): if directory: publishDir = os.path.join(config.PUBLIC, directory) - subprocess.call(["rm", publishDir]) + if os.path.exists(publishDir): + subprocess.call(["rm", "-rf", publishDir]) + subprocess.call(["rm", "-rf", config.WWW]) + make_publish_dir(SETTINGS.get("publish dir")) + #SETTINGS.update({"publish dir": None}) if SETTINGS.get("gopher"): - SETTINGS.update({"gopher": False}) - subprocess.call(["rm", config.GOPHER_PATH]) + gopher.unpublish() def update_publishing(): ''' @@ -1180,14 +1525,13 @@ def update_publishing(): subprocess.call(["rm", os.path.join(config.PUBLIC, oldDir)]) make_publish_dir(newDir) core.load_files() - core.write("index.html") + #core.write_html("index.html") else: unpublish() - SETTINGS.update({"publish dir": None}) core.load(SETTINGS) -def make_publish_dir(dir): +def make_publish_dir(publish_dir): ''' setup helper to create publishing directory ''' @@ -1200,13 +1544,18 @@ def make_publish_dir(dir): index.write("

    ttbp blog placeholder

    ") index.close() - publishDir = os.path.join(config.PUBLIC, dir) - if os.path.exists(publishDir): - subprocess.call(["rm", publishDir]) + if core.publishing(): + live = os.path.join(config.PUBLIC, publish_dir) + if os.path.exists(live): + subprocess.call(["rm", live]) - subprocess.call(["ln", "-s", config.WWW, publishDir]) + subprocess.call(["ln", "-s", config.WWW, live]) - print("\n\tpublishing to "+config.LIVE+config.USER+"/"+SETTINGS.get("publish dir")+"/\n\n") + return "\n\tpublishing to "+config.LIVE+config.USER+"/"+SETTINGS.get("publish dir")+"/\n\n" + + else: + return "" + #print("\n\tpublishing to "+config.LIVE+config.USER+"/"+SETTINGS.get("publish dir")+"/\n\n") def update_gopher(): ''' @@ -1255,7 +1604,7 @@ def update_user_version(): time.sleep(1) print("...") - time.sleep(1) + time.sleep(0.5) userVersion = "" (x, y, z) = [0, 0, 0] @@ -1284,7 +1633,7 @@ def update_user_version(): # repopulate html files core.load_files() - core.write("index.html") + #core.write_html("index.html") # add publishing setting print("\nnew feature!\n") @@ -1335,6 +1684,19 @@ def update_user_version(): SETTINGS.update({"post as nopub": False}) save_settings() + if z < 3: + # update permalink css + style = open(os.path.join(config.USER_CONFIG, 'style.css'), 'r').read() + if "permalink" not in style: + print("adding new css...") + with open(os.path.join(config.USER_CONFIG, 'style.css'), 'a') as f: + f.write(""" +.entry p.permalink { + font-size: .6em; + font-color: #808080; + text-align: right; +}""") + print(""" you're all good to go, """+chatter.say("friend")+"""! please contact ~endorphant if something strange happened to you during this update. @@ -1356,6 +1718,14 @@ something strange happened to you during this update. # version 0.11.2 patch notes print(config.UPDATES["0.11.2"]) + if y < 11 or z < 3: + # version 0.11.3 patch notes + print(config.UPDATES["0.11.3"]) + + if y < 12: + # version 0.12.0 patch notes + print(config.UPDATES["0.12.0"]) + confirm = "" while confirm not in ("x", "", "X", ""): diff --git a/ttbp/util.py b/ttbp/util.py index 5168018..b7f5e62 100644 --- a/ttbp/util.py +++ b/ttbp/util.py @@ -26,12 +26,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import random import time from six.moves import input +import os import colorama import inflect ## misc globals -BACKS = ['back', 'b', 'q'] +BACKS = ['back', 'b', 'q', ''] NAVS = ['u', 'd'] ## color stuff @@ -214,3 +215,21 @@ def input_yn(query): ans = input("'y' or 'n' please: ") return ans == "y" + +def parse_date(file): + ''' + parses date out of pre-validated filename + + * assumes a filename of YYYYMMDD.txt + * returns a list: + [0] 'YYYY' + [1] 'MM' + [2] 'DD' + ''' + + rawdate = os.path.splitext(os.path.basename(file))[0] + + date = [rawdate[0:4], rawdate[4:6], rawdate[6:]] + + return date +