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.
@@ -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()
@@ -0,0 +1,2 @@
import COMET
COMET.main()
@@ -0,0 +1,2 @@
Flask==0.10.1
Flask-Login==0.2.11
File renamed without changes.
File renamed without changes.

This file was deleted.

Oops, something went wrong.

This file was deleted.

Oops, something went wrong.

This file was deleted.

Oops, something went wrong.
Oops, something went wrong.

0 comments on commit 1d4c91e

Please sign in to comment.