Coder Social home page Coder Social logo

motor-blog's Introduction

Motor-Blog

Blog platform based on Tornado, MongoDB, and Motor. To be used with MarsEdit.

Prequisites

Features

  • Frontend: Motor-Blog runs in Tornado. It is very fast.

  • Editing: Motor-Blog has no admin panel, but supports MarsEdit.

  • Comments: Motor-Blog does not support comments natively, I recommend a third-party Javascript comments API like Disqus.

  • Customization: Appearance is completely customizable.

Installation

  • Install MongoDB and run it on the default port on the same machine as Motor-Blog

  • pip install -r requirements.txt

  • To migrate from a prior WordPress blog with migrate_from_wordpress.py you'll need Pandoc

Deployment

Development Deployment

Start MongoDB

mkdir data
mongod --dbpath data --logpath data/mongod.log --fork --setParameter textSearchEnabled=true

Copy motor_blog.conf.example to motor_blog.conf, edit it as desired. Start the application:

python server.py --debug --config=motor_blog.conf --ensure-indexes

Visit http://localhost:8888/blog

Production Deployment

I run Motor-Blog on http://emptysquare.net/blog with Nginx at the front and four server.py processes. Those processes and MongoDB are managed by Supervisor. I've provided example config files in this repository in etc/. If you have an Nginx version with WebSocket support (1.3.13 or later) then draft posts will autoreload when you update them from MarsEdit.

Run python server.py --ensure-indexes at once when first installing Motor-Blog on a production MongoDB server. (If you see an error when using the search box, "Can't canonicalize query: IndexNotFound text index required for $text query", it means you haven't done this step. In any case, running Motor-Blog without indexes built will risk performance problems.)

MarsEdit setup

In MarsEdit, do "File -> New Blog." Give it a name and the URL of your Motor-Blog's home page. MarsEdit auto-detects the rest. You'll need to enter the username and password you put in motor_blog.conf. In the "General" tab of your blog's settings, I suggest setting "Download the 1000 most recent posts on refresh," since Motor-Blog can handle it. Under "Editing," set Preview Text Filter to "Markdown", and Image Size "Defaults To Full Size".

When you're editing a post, do "View -> Excerpt" to edit the post's meta-description. This text appears in Google results as a snippet, or when sharing a link to the post on Facebook. Motor-Blog refuses the post if the meta-description field is over 155 characters. Do "View -> Slug Field" to set a custom slug as the final part of the post's URL. If you leave the slug empty, Motor-Blog slugifies the title.

Finally, you'll want to customize how MarsEdit inserts images. This customization serves two purposes: first, we'll remove the width and height from img tags so Motor-Blog's responsive layout can fit them to the visitor's screen. Second, we'll set images' title text is set to the same value as their alt-text, since browsers display image titles as tooltips. Open the MarsEdit Media Manager and select an image. In the Media Manager's lower-right corner is a "Style" chooser, with the option "Customize...":

Alt text

Choose this and create a new image style with "opening markup" like this:

<img
    style="display:block; margin-left:auto; margin-right:auto;"
    src="#fileurl#"
    alt="#alttext#"
    title="#alttext#" />

Blogging

Motor-Blog renders Markdown with Python-Markdown. Plain inline code is surrounded by single backticks (``). Code blocks can be fenced in a number of styles, such as GitHub's:

```python
print "foo"
```

The list of languages is whatever Pygments supports. The following are of interest to Python coders like me: py, py3, pytb and py3tb for tracebacks, and pycon for console sessions.

Using a feature in the latest Python-Markdown, you can highlight specific lines in a code block:

```python hl_lines="2"
# Line 1.
# Line 2. This will have a yellow background
```

Customization

  • Set your theme directory in motor_blog.conf.
  • The theme directory should contain a templates subdir with the same set of filenames as the example theme. Tornado templates or Jade templates are both supported.
  • Follow the example theme for inspiration.
  • The setting() function is available to all templates, and gives access to values in motor_blog.conf.

A Tour of the Code

  • server.py: Web application server
  • motor_blog/: Package code
    • web/
      • handlers.py: RequestHandlers for the blog's website
      • admin-templates/: Templates for login/out and viewing drafts
    • theme/: Default theme for emptysquare.net, overridable with your theme
    • api/: The XML-RPC API that MarsEdit uses
    • models.py: schema definitions
    • text/
      • markup.py: convert from Markdown into HTML for display, including some custom syntax
      • wordpress_to_markdown.py: convert from the WordPress's particular HTML to markdown, for migrate_from_wordpress.py
      • abbrev.py: convert from HTML to truncated plain text for all-posts page
    • tools/:
      • migrate_from_wordpress.py: Tool for migrating from my old Wordpress blog to Motor-Blog. I wrote this tool when Motor didn't support GridFS, so it puts all media from Wordpress into single documents in the "media" collection, which brings us to...
      • migrate_media_to_gridfs.py: Tool to migrate media from a single document per image in the "media" collection to GridFS.
    • cache.py: Cache results from MongoDB, invalidate when events are emitted
    • indexes.py: Index definitions for server.py --ensure_indexes
    • options.py: Configuration parsing

motor-blog's People

Contributors

ajdavis avatar boylea avatar evgenypopov72 avatar puentesarrin avatar versionzhang avatar wsantos avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

motor-blog's Issues

Can't unpublish a post with MarsEdit

Seems like changing "Post Status" to "Draft" from "Published" on an already-published post in MarsEdit and re-sending to blog does not remove it from the home page or "All Posts" page.

Offline editing

There is any alternative to put post or can only acomplish with MarsEdit ?

regrads.

about search posts and option ensure_indexes

I want to search your post by search link, then I should use --ensure_indexes=true to provide your posts indexed, otherwise I will get Error message like this:

    OperationFailure: database error: Can't canonicalize query: IndexNotFound text index required for $text query

I suggest to add README.md about this.

ampersands in code blocks

An ampersand in a code block appears in the browser as "&". This is very irritating.

I'm considering migrating away from cMarkdown to Markdown for its features anyway, since HTMLification performance isn't important. Perhaps that will fix this.

Paging in all-posts

all-posts page is limited to 100 now, but doesn't page. Some other views could use paging, too, particularly in the admin area. Use this as an example of how to implement paging in MongoDB while avoiding the non-performant "skip".

Restore retina support

I had retina.js problems on Chrome. With the responsive new theme for emptysquare.net, perhaps just upload all images at least 1200px wide and it will Just Work?

Custom slugs don't work quite right

  • Draft a post with a custom slug in MarsEdit, post to site as draft
  • Slug disappears from MarsEdit edit window?
  • Update and re-post, now the custom slug is gone from post document in MongoDB, too

Multiple self.settings['db'] calls instead of "MotorBlogHandler" self.db

Hi!,

I don't know if this is really an issue (don't affect anything really...) but you have what seems like a base class: MotorBlogHandler in ../motor_blog/web/handlers.py line 39 I found that you have: self.db = self.settings['db'] but you don't use self.db on any of the classes that inherit MotorBlogHandler, not even inside MotorBlogHandler on line 71 you're calling again self.settings

Why are you calling self.settings again instead of self.db?

BTW, I really enjoy reading your code, thanks for sharing your projects with the world (=

Image's titles are set to filenames, obscuring alt tags

When I add an image via MarsEdit I enter some alt-text in the MarsEdit Media Manager. MarsEdit sets the title attribute to the filename. When I mouse over the image in Chrome its title (filename) appears rather than the alt text: apparently title gets priority.

If there's a way to change MarsEdit's title-attribute handling, document that. Otherwise strip titles server-side.

Exception: unexpected token "attrs" in file base.jade on line 47

Hello, I run the blog demo in my mac. But I visit localhost:8888/blog, I got some error.

The message error:

Traceback (most recent call last):
  File "/usr/local/lib/python2.7/site-packages/tornado/web.py", line 1445, in _execute
    result = yield result
  File "/usr/local/lib/python2.7/site-packages/tornado/gen.py", line 1008, in run
    value = future.result()
  File "/usr/local/lib/python2.7/site-packages/tornado/concurrent.py", line 232, in result
    raise_exc_info(self._exc_info)
  File "/usr/local/lib/python2.7/site-packages/tornado/gen.py", line 1014, in run
    yielded = self.gen.throw(*exc_info)
  File "/Users/fredzhang/tornado_blog_study/motor-blog-master/motor_blog/web/handlers.py", line 176, in get
    page_num=int(page_num))
  File "/usr/local/lib/python2.7/site-packages/tornado/gen.py", line 1008, in run
    value = future.result()
  File "/usr/local/lib/python2.7/site-packages/tornado/concurrent.py", line 232, in result
    raise_exc_info(self._exc_info)
  File "/usr/local/lib/python2.7/site-packages/tornado/gen.py", line 282, in wrapper
    yielded = next(result)
  File "/Users/fredzhang/tornado_blog_study/motor-blog-master/motor_blog/web/handlers.py", line 93, in render_async
    template_name, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/tornado/web.py", line 803, in render_string
    t = loader.load(template_name)
  File "/usr/local/lib/python2.7/site-packages/tornado/template.py", line 424, in load
    self.templates[name] = self._create_template(name)
  File "/usr/local/lib/python2.7/site-packages/tornado/template.py", line 452, in _create_template
    template = Template(f.read(), name=name, loader=self)
  File "/usr/local/lib/python2.7/site-packages/pyjade/ext/tornado/__init__.py", line 84, in __init__
    super(Template, self).__init__(template_string, name, *args,**kwargs)
  File "/usr/local/lib/python2.7/site-packages/tornado/template.py", line 304, in __init__
    self.code = self._generate_python(loader)
  File "/usr/local/lib/python2.7/site-packages/tornado/template.py", line 352, in _generate_python
    ancestors = self._get_ancestors(loader)
  File "/usr/local/lib/python2.7/site-packages/tornado/template.py", line 370, in _get_ancestors
    template = loader.load(chunk.name, self.name)
  File "/usr/local/lib/python2.7/site-packages/tornado/template.py", line 424, in load
    self.templates[name] = self._create_template(name)
  File "/usr/local/lib/python2.7/site-packages/tornado/template.py", line 452, in _create_template
    template = Template(f.read(), name=name, loader=self)
  File "/usr/local/lib/python2.7/site-packages/pyjade/ext/tornado/__init__.py", line 82, in __init__
    template_string = process(template_string,filename=name,compiler=Compiler)
  File "/usr/local/lib/python2.7/site-packages/pyjade/utils.py", line 228, in process
    block = _parser.parse()
  File "/usr/local/lib/python2.7/site-packages/pyjade/parser.py", line 46, in parse
    else: block.append(self.parseExpr())
  File "/usr/local/lib/python2.7/site-packages/pyjade/parser.py", line 84, in parseExpr
    return getattr(self,funcName)()
  File "/usr/local/lib/python2.7/site-packages/pyjade/parser.py", line 332, in parseTag
    block = self.block()
  File "/usr/local/lib/python2.7/site-packages/pyjade/parser.py", line 251, in block
    block.append(self.parseExpr())
  File "/usr/local/lib/python2.7/site-packages/pyjade/parser.py", line 84, in parseExpr
    return getattr(self,funcName)()
  File "/usr/local/lib/python2.7/site-packages/pyjade/parser.py", line 332, in parseTag
    block = self.block()
  File "/usr/local/lib/python2.7/site-packages/pyjade/parser.py", line 251, in block
    block.append(self.parseExpr())
  File "/usr/local/lib/python2.7/site-packages/pyjade/parser.py", line 84, in parseExpr
    return getattr(self,funcName)()
  File "/usr/local/lib/python2.7/site-packages/pyjade/parser.py", line 332, in parseTag
    block = self.block()
  File "/usr/local/lib/python2.7/site-packages/pyjade/parser.py", line 251, in block
    block.append(self.parseExpr())
  File "/usr/local/lib/python2.7/site-packages/pyjade/parser.py", line 87, in parseExpr
    (t, self.filename, self.line()))
Exception: unexpected token "attrs" in file base.jade on line 47

no module named xmlrpclib

λ  python server.py --debug --config=motor_blog.conf --ensure-indexes
Traceback (most recent call last):
  File "server.py", line 16, in <module>
    from motor_blog import indexes, cache, application
  File "E:\blogsys\motor-blog\motor_blog\application.py", line 9, in <module>
    from motor_blog.api.handlers import APIHandler, RSDHandler
  File "E:\blogsys\motor-blog\motor_blog\api\__init__.py", line 4, in <module>
    import xmlrpclib
ModuleNotFoundError: No module named 'xmlrpclib'

and tring to use pip install but error

E:\blogsys\motor-blog [master ≡ +0 ~3 -0 !]
λ  pip install xmlrpclib
Collecting xmlrpclib
  Could not find a version that satisfies the requirement xmlrpclib (from versions: )
No matching distribution found for xmlrpclib

Log lines are doubled

I recently changed how logging is configured, with the intention of making it easier to log to stderr while developing Motor-Blog. I appear to have caused my production server on emptysquare.net to log every line twice.

Unknown option _pool_class

I'm testing again, but raise this errors when execute:

Traceback (most recent call last):
  File "server.py", line 45, in <module>
    db = motor.MotorConnection().open_sync().motorblog
  File "/usr/local/lib/python2.7/dist-packages/motor/__init__.py", line 508, in open_sync
    raise outcome['error']
pymongo.errors.ConfigurationError: Unknown option _pool_class

Media filename escaping

Upload a file with a name like Screen Shot 2013-05-12 at 8.41.35 PM.png via MarsEdit. The media admin page links to:

http://localhost:8888/blog/media/2013%2F05%2FScreen%2520Shot%25202013-05-12%2520at%25208.41.35%2520PM.png

That works, but 2013/05 needn't be replaced with 2013%2F05. The blog post, meanwhile, has an image with this url:

http://localhost:8888/blog/media/2013/05/Screen%20Shot%202013-05-12%20at%208.41.35%20PM.png

That looks better but doesn't work; for some reason the spaces need to be replaced with %2520.

Make the image url work as it appears in the blog post, and make that the link from the media admin page instead of what it has now.

there are some decoding errors about supporting chinese display

@ajdavis
Hi, I have deploy the blog on my vps,I found some errors about decoding chinese chars.

If the blog description contains non-ascii chars, it will raise exceptions.

the error message:

Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/tornado/web.py", line 1445, in _execute
    result = yield result
  File "/usr/local/lib/python2.7/dist-packages/tornado/gen.py", line 1008, in run
    value = future.result()
  File "/usr/local/lib/python2.7/dist-packages/tornado/concurrent.py", line 232, in result
    raise_exc_info(self._exc_info)
  File "/usr/local/lib/python2.7/dist-packages/tornado/gen.py", line 1014, in run
    yielded = self.gen.throw(*exc_info)
  File "/home/tornado_blog/motor-blog/motor_blog/web/handlers.py", line 323, in get
    this_tag=tag, page_num=int(page_num))
  File "/usr/local/lib/python2.7/dist-packages/tornado/gen.py", line 1008, in run
    value = future.result()
  File "/usr/local/lib/python2.7/dist-packages/tornado/concurrent.py", line 232, in result
    raise_exc_info(self._exc_info)
  File "/usr/local/lib/python2.7/dist-packages/tornado/gen.py", line 282, in wrapper
    yielded = next(result)
  File "/home/tornado_blog/motor-blog/motor_blog/web/handlers.py", line 93, in render_async
    template_name, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/tornado/web.py", line 806, in render_string
    return t.generate(**namespace)
  File "/usr/local/lib/python2.7/dist-packages/tornado/template.py", line 345, in generate
    return execute()
  File "tag_jade.generated.py", line 5, in _tt_execute
    title = setting('author_display_name') + " | " + this_tag  # tag.jade:1 (via base.jade:1)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe7 in position 9: ordinal not in range(128)

And if your tags contain chinese chars,it will show these messages.

Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/tornado/web.py", line 1445, in _execute
    result = yield result
  File "/usr/local/lib/python2.7/dist-packages/tornado/gen.py", line 1008, in run
    value = future.result()
  File "/usr/local/lib/python2.7/dist-packages/tornado/concurrent.py", line 232, in result
    raise_exc_info(self._exc_info)
  File "/usr/local/lib/python2.7/dist-packages/tornado/gen.py", line 1014, in run
    yielded = self.gen.throw(*exc_info)
  File "/home/tornado_blog/motor-blog/motor_blog/web/handlers.py", line 253, in get
    categories=categories)
  File "/usr/local/lib/python2.7/dist-packages/tornado/gen.py", line 1008, in run
    value = future.result()
  File "/usr/local/lib/python2.7/dist-packages/tornado/concurrent.py", line 232, in result
    raise_exc_info(self._exc_info)
  File "/usr/local/lib/python2.7/dist-packages/tornado/gen.py", line 282, in wrapper
    yielded = next(result)
  File "/home/tornado_blog/motor-blog/motor_blog/web/handlers.py", line 93, in render_async
    template_name, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/tornado/web.py", line 806, in render_string
    return t.generate(**namespace)
  File "/usr/local/lib/python2.7/dist-packages/tornado/template.py", line 345, in generate
    return execute()
  File "single_jade.generated.py", line 231, in _tt_execute
    _tt_tmp = __pyjade_attrs(attrs=[('href',(reverse_url('tag', tag))), ('title',("All posts tagged "+"{}".format( tag )+""))])  # single.jade:5 (via base.jade:28)
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)

No such file or directory: 'motor_blog.conf'

Hi, I download the project and find the following error: "No such file or directory: 'motor_blog.conf'" when I run the "python server.py --debug --config=motor_blog.conf --ensure-indexes". from the .gitignore, I know the file is ignored by the git. but how can i deploy the project without the file 'motor_blog.conf'

Deploy pattern

Sorry create this thing as an issue, but I'm wondering what pattern use for a production deploy, what you use Jesse? You blog is a git clone of master branch ? Do you suggest any pattern ?

Handle non-ascii in titles

When I tried to post about Walter Kühr it rendered as something like "Kühr", seems to have been HTML-escaped twice instead of once. There's a problem with slugifying non-ascii chars, too.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.