Permalink
Browse files

Rewrite to Flask.

Signed-off-by: Chris Warrick <kwpolska@gmail.com>
  • Loading branch information...
Kwpolska committed Jan 5, 2015
1 parent 8fda81f commit 1d4c91ebd3bd1431fe7e4cda79fa404f428aa8cb
Showing with 339 additions and 372 deletions.
  1. 0 {plugins/webapp → COMET}/README.md
  2. +333 −0 COMET/__init__.py
  3. +2 −0 COMET/__main__.py
  4. 0 {plugins/webapp → COMET}/bower_components/wysihtml/.bower.json
  5. 0 {plugins/webapp → COMET}/bower_components/wysihtml/CHANGELOG.textile
  6. 0 {plugins/webapp → COMET}/bower_components/wysihtml/Gruntfile.js
  7. 0 {plugins/webapp → COMET}/bower_components/wysihtml/LICENSE
  8. 0 {plugins/webapp → COMET}/bower_components/wysihtml/README.markdown
  9. 0 {plugins/webapp → COMET}/bower_components/wysihtml/bower.json
  10. 0 {plugins/webapp → COMET}/bower_components/wysihtml/dist/wysihtml5x-toolbar.js
  11. 0 {plugins/webapp → COMET}/bower_components/wysihtml/dist/wysihtml5x-toolbar.min.js
  12. 0 {plugins/webapp → COMET}/bower_components/wysihtml/dist/wysihtml5x-toolbar.min.map
  13. 0 {plugins/webapp → COMET}/bower_components/wysihtml/dist/wysihtml5x.js
  14. 0 {plugins/webapp → COMET}/bower_components/wysihtml/dist/wysihtml5x.min.js
  15. 0 {plugins/webapp → COMET}/bower_components/wysihtml/dist/wysihtml5x.min.map
  16. 0 {plugins/webapp → COMET}/bower_components/wysihtml/lib/base/base.js
  17. 0 {plugins/webapp → COMET}/bower_components/wysihtml/package.json
  18. 0 {plugins/webapp → COMET}/bower_components/wysihtml/parser_rules/advanced.js
  19. 0 {plugins/webapp → COMET}/bower_components/wysihtml/parser_rules/advanced_and_extended.js
  20. 0 {plugins/webapp → COMET}/bower_components/wysihtml/parser_rules/advanced_unwrap.js
  21. 0 {plugins/webapp → COMET}/bower_components/wysihtml/parser_rules/simple.js
  22. +2 −0 COMET/requirements.txt
  23. 0 {plugins/webapp → COMET}/static/css/wysihtml5x.css
  24. 0 {plugins/webapp → COMET}/static/js/jPages.min.js
  25. 0 {plugins/webapp → COMET}/templates/mako/webapp_index.tmpl
  26. 0 {plugins/webapp → COMET}/templates/mako/webapp_post_edit.tmpl
  27. 0 {plugins/webapp → COMET}/templates/mako/webapp_profile.tmpl
  28. 0 {plugins/webapp → COMET}/templates/mako/webapp_users.tmpl
  29. 0 {plugins/webapp → COMET}/templates/mako/webapp_users_delete.tmpl
  30. 0 {plugins/webapp → COMET}/templates/mako/webapp_users_edit.tmpl
  31. 0 {plugins/webapp → COMET}/users.json
  32. 0 {plugins/webapp → COMET}/users.json.bak
  33. +0 −1 plugins/__init__.py
  34. +0 −1 plugins/webapp/requirements.txt
  35. +0 −12 plugins/webapp/webapp.plugin
  36. +0 −358 plugins/webapp/webapp.py
  37. +2 −0 requirements.txt
File renamed without changes.
View
@@ -0,0 +1,333 @@
# -*- coding: utf-8 -*-
# Copyright © 2014-2015 Roberto Alsina, Henry Hirsch, Chris Warrick.
# Permission is hereby granted, free of charge, to any
# person obtaining a copy of this software and associated
# documentation files (the "Software"), to deal in the
# Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the
# Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice
# shall be included in all copies or substantial portions of
# the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
# PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
# OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
# OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
from __future__ import print_function, unicode_literals
import json
import os
import webbrowser
import io
import hashlib
import nikola.__main__
from flask import Flask, Blueprint, request, redirect, send_from_directory
from flask.ext.login import LoginManager, login_required
_site = None
app = None
TITLE = 'webapp'
USERNAME = ''
REALNAME = ''
USERS = {}
auth_title = 'Comet CMS Login'
json_path = os.path.join(os.path.dirname(__file__), 'users.json')
def auth_check(user, passwd):
global USERNAME, REALNAME, USERS
passwd = passwd.encode('utf-8')
passwd = passwd_hash(passwd)
status = user in USERS and USERS[user]['password'] == passwd
if status:
USERNAME = user
REALNAME = USERS[user]['name']
return status
def init_site():
_site.scan_posts(really=True)
def passwd_hash(passwd):
# safer algorithm?
return hashlib.sha512(passwd).hexdigest()
def read_users():
global USERS
with io.open(json_path, 'rb') as fh:
USERS = json.load(fh)
def write_users():
global USERS
with io.open(json_path, 'wb') as fh:
json.dump(USERS, fh, indent=4)
def generate_menu_alt():
REALNAME = "TEMPORARILY DISABLED"
USERNAME = "admin"
if USERS[USERNAME]['can_edit_users']:
edit_entry = '<li><a href="/users">Manage users</a></li>'
else:
edit_entry = ''
return """
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">{0} [{1}] <span class="caret"></span></a>
<ul class="dropdown-menu" role="menu">
<li><a href="/profile">Profile</a></li>
{2}
</ul>
</li>""".format(REALNAME, USERNAME, edit_entry)
def render(template_name, context=None):
if context is None:
context = {}
context['USERNAME'] = USERNAME
context['REALNAME'] = REALNAME
return _site.render_template(template_name, None, context)
read_users()
# FIXME
login_required = lambda _: _
app = Flask('webapp')
@app.route('/')
@login_required
def index():
context = {}
context['site'] = _site
context['title'] = 'Posts & Pages'
context['permalink'] = '/'
return render('webapp_index.tmpl', context)
@app.route('/edit/<path:path>', methods=['GET', 'POST'])
@login_required
def edit(path):
context = {'path': path}
context['site'] = _site
context['json'] = json
post = None
for p in _site.timeline:
if p.source_path == path:
post = p
break
if post is None:
return "No such post or page.", 404
context['post'] = post
context['title'] = 'Editing {0}'.format(post.title())
context['permalink'] = '/edit/' + path
return render('webapp_post_edit.tmpl', context)
@app.route('/save/<path:path>', methods=['POST'])
@login_required
def save(path):
# FIXME insecure pending defnull/bottle#411
context = {'path': path}
context['site'] = _site
post = None
for p in _site.timeline:
if p.source_path == path:
post = p
break
if post is None:
return "No such post or page.", 404
meta = request.form
meta.pop('_wysihtml5_mode', '')
post.compiler.create_post(post.source_path, onefile=True, is_page=False, **meta)
init_site()
return redirect('/edit/' + path)
@app.route('/delete', methods=['POST'])
@login_required
def delete():
path = request.form['path']
for p in _site.timeline:
if p.source_path == path:
post = p
break
if post is None:
return "No such post or page.", 404
os.unlink(path)
init_site()
return redirect('/')
@app.route('/wysihtml/<path:path>')
def server_wysihtml(path):
return send_from_directory(os.path.join(os.path.dirname(__file__), 'bower_components', 'wysihtml'), path)
@app.route('/assets/<path:path>')
def server_assets(path):
return send_from_directory(os.path.join(_site.config["OUTPUT_FOLDER"], 'assets'), path)
@app.route('/new/post', methods=['POST'])
@login_required
def new_post():
title = request.forms['title']
try:
_site.commands.new_post(title=title, author=REALNAME, content_format='html')
except SystemExit:
return "This post already exists!", 500
# reload post list and go to index
init_site()
return redirect('/')
@app.route('/new/page', methods=['POST'])
@login_required
def new_page():
title = request.form['title']
try:
_site.commands.new_page(title=title, author=REALNAME, content_format='html')
except SystemExit:
return "This post already exists!", 500
# reload post list and go to index
init_site()
return redirect('/')
@app.route('/profile')
@login_required
def acp_profile():
return render('webapp_profile.tmpl',
context={'title': 'Edit profile',
'permalink': '/profile'})
@app.route('/profile/save', methods=['POST'])
@login_required
def acp_profile_save():
global USERS
read_users()
data = request.form
if data['password'].strip():
USERS[USERNAME]['password'] = passwd_hash(data['password'])
USERS[USERNAME]['name'] = data['name']
write_users()
return redirect('/profile')
@app.route('/users')
@login_required
def acp_users():
global USERS
if not USERS[USERNAME]['can_edit_users']:
return "Not authorized to edit users.", 401
else:
return render('webapp_users.tmpl',
context={'title': 'Edit users',
'permalink': '/users',
'USERS': USERS})
@app.route('/users/<name>')
@login_required
def acp_users_edit(name):
global USERS
if not USERS[USERNAME]['can_edit_users']:
return "Not authorized to edit users.", 401
else:
if name in USERS:
new = False
user = USERS[name]
else:
new = True
user = {'name': '', 'password': '', 'can_edit_users': False}
return render('webapp_users_edit.tmpl',
context={'title': 'Edit user ' + name,
'permalink': '/users/' + name,
'user': user,
'name': name,
'new': new})
@app.route('/users/<name>/save', methods=['POST'])
@login_required
def acp_users_save(name):
global USERS
if not USERS[USERNAME]['can_edit_users']:
return "Not authorized to edit users.", 401
else:
read_users()
data = request.form
if name not in USERS:
USERS[name] = {'name': '', 'password': '', 'can_edit_users': False}
if data['password'].strip():
USERS[name]['password'] = passwd_hash(data['password'])
USERS[name]['name'] = data['name']
if name != USERNAME:
USERS[name]['can_edit_users'] = 'can_edit_users' in data
write_users()
return redirect('/users')
@app.route('/users/create/new', methods=['POST'])
@login_required
def acp_users_create_new():
data = request.form
return redirect('/users/' + data['name'])
@app.route('/users/<name>/delete')
@login_required
def acp_users_delete(name):
global USERS
if not USERS[USERNAME]['can_edit_users']:
return "Not authorized to edit users.", 401
else:
if name not in USERS:
return "User does not exist.", 404
return render('webapp_users_delete.tmpl',
context={'title': 'Deleting ' + name,
'permalink': '/users/{0}/delete'.format(name),
'user': name})
@app.route('/users/<name>/really_delete')
@login_required
def acp_users_really_delete(name):
global USERS
if not USERS[USERNAME]['can_edit_users']:
return "Not authorized to edit users.", 401
else:
read_users()
del USERS[name]
write_users()
return redirect('/users')
def main():
global _site, app
nikola.__main__._RETURN_SITE = True
_site = nikola.__main__.main([])
init_site()
port = 8001
_site.template_hooks['menu_alt'].append(generate_menu_alt)
site = _site.config['SITE_URL']
_site.config['SITE_URL'] = 'http://localhost:{0}/'.format(port)
_site.config['BASE_URL'] = 'http://localhost:{0}/'.format(port)
_site.GLOBAL_CONTEXT['blog_url'] = 'http://localhost:{0}/'.format(port)
_site.config['NAVIGATION_LINKS'] = {'en': ((site, 'Back to {0}'.format(_site.GLOBAL_CONTEXT['blog_title']('en'))),)}
_site.GLOBAL_CONTEXT['navigation_links'] = {'en':((site, 'Back to {0}'.format(_site.GLOBAL_CONTEXT['blog_title']('en'))),)}
_site.config['SOCIAL_BUTTONS'] = ''
_site.GLOBAL_CONTEXT['social_buttons_code'] = lambda _: ''
TITLE = _site.GLOBAL_CONTEXT['blog_title']('en') + ' Administration'
_site.config['BLOG_TITLE'] = lambda _: TITLE
_site.GLOBAL_CONTEXT['blog_title'] = lambda _: TITLE
_site.GLOBAL_CONTEXT['lang'] = 'en'
mod_dir = os.path.dirname(__file__)
tmpl_dir = os.path.join(
mod_dir, 'templates', _site.template_system.name
)
if os.path.isdir(tmpl_dir):
# Inject tmpl_dir low in the theme chain
_site.template_system.inject_directory(tmpl_dir)
#if options and options.get('browser'):
#webbrowser.open('http://localhost:{0}'.format(port))
app.run('localhost', port, debug=True)
if __name__ == '__main__':
main()
View
@@ -0,0 +1,2 @@
import COMET
COMET.main()
View
@@ -0,0 +1,2 @@
Flask==0.10.1
Flask-Login==0.2.11
File renamed without changes.
File renamed without changes.
View
@@ -1 +0,0 @@
# Plugin modules go here.
@@ -1 +0,0 @@
bottle
@@ -1,12 +0,0 @@
[Core]
Name = webapp
Module = webapp
[Nikola]
MinVersion = 7.3.0
[Documentation]
Author = Roberto Alsina, Henry Hirsch, Chris Warrick
Version = 0.1
Website = http://plugins.getnikola.com/#webapp
Description = Turn Nikola into a webapp
Oops, something went wrong.

0 comments on commit 1d4c91e

Please sign in to comment.