added more tests
include img in manifest disclaimer in root cli script add blinker to dev tools version file included in package cli bug fixes
This commit is contained in:
parent
38e5ef85c0
commit
bf3b3d3d74
|
@ -1,4 +1,5 @@
|
|||
include requirements.txt VERSION LICENSE README.md
|
||||
include requirements.txt LICENSE README.md
|
||||
recursive-include realms/static/img *
|
||||
recursive-include realms/static/fonts *
|
||||
recursive-include realms/static/css *
|
||||
recursive-include realms/static/js *
|
||||
|
|
12
realms-wiki
12
realms-wiki
|
@ -1,6 +1,14 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from realms.commands import cli
|
||||
import pip
|
||||
from realms.cli import cli
|
||||
|
||||
if __name__ == '__main__':
|
||||
print
|
||||
print "----------------------------------"
|
||||
print "This script is for development.\n" \
|
||||
"If you installed via pip, " \
|
||||
"you should have realms-wiki in your PATH that should be used instead."
|
||||
print "----------------------------------"
|
||||
print
|
||||
|
||||
cli()
|
||||
|
|
|
@ -18,9 +18,10 @@ from flask.ext.assets import Environment, Bundle
|
|||
from werkzeug.routing import BaseConverter
|
||||
from werkzeug.exceptions import HTTPException
|
||||
|
||||
from realms.lib.util import to_canonical, remove_ext, mkdir_safe, gravatar_url, to_dict
|
||||
from realms.lib.hook import HookModelMeta
|
||||
from realms.lib.util import is_su, in_virtualenv
|
||||
from .lib.util import to_canonical, remove_ext, mkdir_safe, gravatar_url, to_dict
|
||||
from .lib.hook import HookModelMeta
|
||||
from .lib.util import is_su, in_virtualenv
|
||||
from .version import __version__
|
||||
|
||||
|
||||
class Application(Flask):
|
||||
|
@ -79,7 +80,7 @@ class Application(Flask):
|
|||
if hasattr(sources.hooks, 'before_first_request'):
|
||||
self.before_first_request(sources.hooks.before_first_request)
|
||||
|
||||
# print >> sys.stderr, ' * Ready in %.2fms' % (1000.0 * (time.time() - start_time))
|
||||
# print >> sys.stderr, ' * Ready in %.2fms' % (1000.0 * (time.time() - start_time))
|
||||
|
||||
def make_response(self, rv):
|
||||
if rv is None:
|
||||
|
@ -200,17 +201,6 @@ def create_app(config=None):
|
|||
return app
|
||||
|
||||
|
||||
@click.group()
|
||||
@click.pass_context
|
||||
def cli(ctx):
|
||||
# This could probably done better
|
||||
if ctx.invoked_subcommand in ['setup', 'setup_upstart', 'pip']:
|
||||
if not in_virtualenv() and not is_su():
|
||||
# This does not account for people the have user level python installs
|
||||
# that aren't virtual environments! Should be rare I think
|
||||
click.secho("This command requires root privileges, use sudo or run as root.", fg='red')
|
||||
sys.exit()
|
||||
|
||||
# Init plugins here if possible
|
||||
login_manager = LoginManager()
|
||||
login_manager.login_view = 'auth.login'
|
||||
|
@ -242,9 +232,6 @@ assets.register('main.css',
|
|||
'css/style.css')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@click.group()
|
||||
def cli():
|
||||
pass
|
||||
|
|
|
@ -1,14 +1,40 @@
|
|||
from realms import config, create_app, db, cli
|
||||
from realms.lib.util import random_string, in_virtualenv, green, yellow, red
|
||||
from realms import config, create_app, db, cli as cli_, __version__
|
||||
from realms.lib.util import is_su, random_string, in_virtualenv, green, yellow, red
|
||||
from subprocess import call, Popen
|
||||
from multiprocessing import cpu_count
|
||||
import click
|
||||
import json
|
||||
import sys
|
||||
import os
|
||||
import pip
|
||||
|
||||
|
||||
app = create_app()
|
||||
def print_version(ctx, param, value):
|
||||
if not value or ctx.resilient_parsing:
|
||||
return
|
||||
green(__version__)
|
||||
ctx.exit()
|
||||
|
||||
|
||||
@click.group()
|
||||
@click.option('--version', is_flag=True, callback=print_version,
|
||||
expose_value=False, is_eager=True)
|
||||
@click.pass_context
|
||||
def cli(ctx):
|
||||
# This could probably done better
|
||||
if ctx.invoked_subcommand in ['setup', 'pip']:
|
||||
if not in_virtualenv() and not is_su():
|
||||
# This does not account for people the have user level python installs
|
||||
# that aren't virtual environments! Should be rare I think
|
||||
click.secho("This command requires root privileges, use sudo or run as root.", fg='red')
|
||||
sys.exit()
|
||||
|
||||
if ctx.invoked_subcommand in ['setup_upstart']:
|
||||
if not is_su():
|
||||
click.secho("This command requires root privileges, use sudo or run as root.", fg='red')
|
||||
sys.exit()
|
||||
|
||||
cli.add_command(cli_)
|
||||
|
||||
|
||||
def get_user():
|
||||
|
@ -117,37 +143,28 @@ def get_prefix():
|
|||
return sys.prefix
|
||||
|
||||
|
||||
def get_pip():
|
||||
""" Get virtualenv path for pip
|
||||
"""
|
||||
if in_virtualenv():
|
||||
return get_prefix() + '/bin/pip'
|
||||
else:
|
||||
return 'pip'
|
||||
|
||||
|
||||
@cli.command()
|
||||
@cli.command(name='pip')
|
||||
@click.argument('cmd', nargs=-1)
|
||||
def pip(cmd):
|
||||
def pip_(cmd):
|
||||
""" Execute pip commands, useful for virtualenvs
|
||||
"""
|
||||
call(get_pip() + ' ' + ' '.join(cmd), shell=True)
|
||||
pip.main(cmd)
|
||||
|
||||
|
||||
def install_redis():
|
||||
call([get_pip(), 'install', 'redis'])
|
||||
pip.main(['install', 'redis'])
|
||||
|
||||
|
||||
def install_mysql():
|
||||
call([get_pip(), 'install', 'MySQL-Python'])
|
||||
pip.main(['install', 'MySQL-Python'])
|
||||
|
||||
|
||||
def install_postgres():
|
||||
call([get_pip(), 'install', 'psycopg2'])
|
||||
pip.main(['install', 'psycopg2'])
|
||||
|
||||
|
||||
def install_memcached():
|
||||
call([get_pip(), 'install', 'python-memcached'])
|
||||
pip.main(['install', 'python-memcached'])
|
||||
|
||||
|
||||
@click.command()
|
||||
|
@ -220,9 +237,9 @@ def dev(port):
|
|||
""" Run development server
|
||||
"""
|
||||
green("Starting development server")
|
||||
app.run(host="0.0.0.0",
|
||||
port=port,
|
||||
debug=True)
|
||||
create_app().run(host="0.0.0.0",
|
||||
port=port,
|
||||
debug=True)
|
||||
|
||||
|
||||
def start_server():
|
||||
|
@ -235,7 +252,7 @@ def start_server():
|
|||
green("Server started. Port: %s" % config.PORT)
|
||||
|
||||
Popen("gunicorn 'realms:create_app()' -b 0.0.0.0:%s -k gevent %s" %
|
||||
(config.PORT, flags), shell=True, executable='/bin/bash')
|
||||
(config.PORT, flags), shell=True, executable='/bin/bash')
|
||||
|
||||
|
||||
def stop_server():
|
||||
|
@ -308,21 +325,20 @@ def drop_db():
|
|||
def test():
|
||||
""" Run tests
|
||||
"""
|
||||
for mod in [('flask.ext.testing', 'Flask-Testing'), ('nose', 'nose')]:
|
||||
for mod in [('flask.ext.testing', 'Flask-Testing'), ('nose', 'nose'), ('blinker', 'blinker')]:
|
||||
if not module_exists(mod[0]):
|
||||
call([get_pip(), 'install', mod[1]])
|
||||
pip.main(['install', mod[1]])
|
||||
|
||||
nosetests = get_prefix() + "/bin/nosetests" if in_virtualenv() else "nosetests"
|
||||
|
||||
call([nosetests, config.APP_PATH])
|
||||
call([nosetests, 'realms'])
|
||||
|
||||
|
||||
@cli.command()
|
||||
def version():
|
||||
""" Output version
|
||||
"""
|
||||
with open(os.path.join(config.APP_PATH, 'VERSION')) as f:
|
||||
click.echo(f.read().strip())
|
||||
green(__version__)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
|
@ -12,8 +12,12 @@ class BaseTest(TestCase):
|
|||
app.config['PRESERVE_CONTEXT_ON_EXCEPTION'] = False
|
||||
app.config['WIKI_PATH'] = '/tmp/%s' % random_string(12)
|
||||
app.config['DB_URI'] = 'sqlite:////tmp/%s.db' % random_string(12)
|
||||
app.config.update(self.configure())
|
||||
return app
|
||||
|
||||
def configure(self):
|
||||
return {}
|
||||
|
||||
def tearDown(self):
|
||||
call(['rm', '-rf', self.app.config['WIKI_PATH']])
|
||||
call(['rm', '-f', self.app.config['DB_URI'][10:]])
|
|
@ -20,7 +20,7 @@ def cname_to_filename(cname):
|
|||
:return: str -- Filename
|
||||
|
||||
"""
|
||||
return cname.lower() + ".md"
|
||||
return cname + ".md"
|
||||
|
||||
|
||||
def filename_to_cname(filename):
|
||||
|
@ -71,25 +71,26 @@ class Wiki(HookMixin):
|
|||
|
||||
return username, email
|
||||
|
||||
def revert_page(self, name, commit_sha, message, username):
|
||||
def revert_page(self, name, commit_sha, message, username, email):
|
||||
"""Revert page to passed commit sha1
|
||||
|
||||
:param name: Name of page to revert.
|
||||
:param commit_sha: Commit Sha1 to revert to.
|
||||
:param message: Commit message.
|
||||
:param username:
|
||||
:param username: Committer name.
|
||||
:param email: Committer email.
|
||||
:return: Git commit sha1
|
||||
|
||||
"""
|
||||
page = self.get_page(name, commit_sha)
|
||||
if not page:
|
||||
raise PageNotFound()
|
||||
raise PageNotFound('Commit not found')
|
||||
|
||||
if not message:
|
||||
commit_info = gittle.utils.git.commit_info(self.gittle[commit_sha.encode('latin-1')])
|
||||
message = commit_info['message']
|
||||
|
||||
return self.write_page(name, page['data'], message=message, username=username)
|
||||
return self.write_page(name, page['data'], message=message, username=username, email=email)
|
||||
|
||||
def write_page(self, name, content, message=None, create=False, username=None, email=None):
|
||||
"""Write page to git repo
|
||||
|
|
|
@ -1,39 +1,72 @@
|
|||
import json
|
||||
from nose.tools import *
|
||||
from flask import url_for
|
||||
from realms.modules.wiki.models import Wiki, cname_to_filename, filename_to_cname
|
||||
from realms.modules.wiki.models import cname_to_filename, filename_to_cname
|
||||
from realms.lib.test import BaseTest
|
||||
|
||||
|
||||
class WikiTest(BaseTest):
|
||||
class WikiBaseTest(BaseTest):
|
||||
def write_page(self, name, message=None, content=None):
|
||||
return self.client.post(url_for('wiki.page_write', name=name),
|
||||
data=dict(message=message, content=content))
|
||||
|
||||
def test_wiki_routes(self):
|
||||
|
||||
self.assert_200(self.client.get(url_for("wiki.create")))
|
||||
|
||||
""" Create a test page first!
|
||||
for route in ['page', 'edit', 'history', 'index']:
|
||||
rv = self.client.get(url_for("wiki.%s" % route, name='test'))
|
||||
self.assert_200(rv, "wiki.%s: %s" % (route, rv.status_code))
|
||||
"""
|
||||
|
||||
def test_write_page(self):
|
||||
self.assert_200(
|
||||
self.client.post(url_for('wiki.page_write', name='test'), data=dict(
|
||||
content='testing',
|
||||
message='test message'
|
||||
)))
|
||||
|
||||
self.assert_200(self.client.get(url_for('wiki.page', name='test')))
|
||||
|
||||
def test_delete_page(self):
|
||||
self.assert_200(self.client.delete(url_for('wiki.page_write', name='test')))
|
||||
self.assert_status(self.client.get(url_for('wiki.page', name='test')), 302)
|
||||
|
||||
def test_revert(self):
|
||||
pass
|
||||
|
||||
class UtilTest(WikiBaseTest):
|
||||
def test_cname_to_filename(self):
|
||||
eq_(cname_to_filename('test'), 'test.md')
|
||||
|
||||
def test_filename_to_cname(self):
|
||||
eq_(filename_to_cname('test-1-2-3.md'), 'test-1-2-3')
|
||||
eq_(filename_to_cname('test-1-2-3.md'), 'test-1-2-3')
|
||||
|
||||
|
||||
class WikiTest(WikiBaseTest):
|
||||
def test_routes(self):
|
||||
self.assert_200(self.client.get(url_for("wiki.create")))
|
||||
self.write_page('test', message='test message', content='testing')
|
||||
|
||||
for route in ['page', 'edit', 'history']:
|
||||
rv = self.client.get(url_for("wiki.%s" % route, name='test'))
|
||||
self.assert_200(rv, "wiki.%s: %s" % (route, rv.status_code))
|
||||
|
||||
self.assert_200(self.client.get(url_for('wiki.index')))
|
||||
|
||||
def test_write_page(self):
|
||||
self.assert_200(self.write_page('test', message='test message', content='testing'))
|
||||
|
||||
rv = self.client.get(url_for('wiki.page', name='test'))
|
||||
self.assert_200(rv)
|
||||
|
||||
self.assert_context('name', 'test')
|
||||
eq_(self.get_context_variable('page')['info']['message'], 'test message')
|
||||
eq_(self.get_context_variable('page')['data'], 'testing')
|
||||
|
||||
def test_history(self):
|
||||
self.assert_200(self.client.get(url_for('wiki.history', name='test')))
|
||||
|
||||
def test_delete_page(self):
|
||||
self.app.config['WIKI_LOCKED_PAGES'] = ['test']
|
||||
self.assert_403(self.client.delete(url_for('wiki.page_write', name='test')))
|
||||
self.app.config['WIKI_LOCKED_PAGES'] = []
|
||||
|
||||
self.assert_200(self.client.delete(url_for('wiki.page_write', name='test')))
|
||||
|
||||
rv = self.client.get(url_for('wiki.page', name='test'))
|
||||
self.assert_status(rv, 302)
|
||||
|
||||
def test_revert(self):
|
||||
rv1 = self.write_page('test', message='test message', content='testing_old')
|
||||
self.write_page('test', message='test message', content='testing_new')
|
||||
data = json.loads(rv1.data)
|
||||
self.client.post(url_for('wiki.revert'), data=dict(name='test', commit=data['sha']))
|
||||
self.client.get(url_for('wiki.page', name='test'))
|
||||
eq_(self.get_context_variable('page')['data'], 'testing_old')
|
||||
self.assert_404(self.client.post(url_for('wiki.revert'), data=dict(name='test', commit='does not exist')))
|
||||
|
||||
self.app.config['WIKI_LOCKED_PAGES'] = ['test']
|
||||
self.assert_403(self.client.post(url_for('wiki.revert'), data=dict(name='test', commit=data['sha'])))
|
||||
self.app.config['WIKI_LOCKED_PAGES'] = []
|
||||
|
||||
|
||||
class RelativePathTest(WikiTest):
|
||||
def configure(self):
|
||||
return dict(RELATIVE_PATH='wiki')
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from flask import abort, g, render_template, request, redirect, Blueprint, flash, url_for, current_app
|
||||
from flask.ext.login import login_required, current_user
|
||||
from realms.lib.util import to_canonical, remove_ext
|
||||
|
||||
from .models import PageNotFound
|
||||
|
||||
blueprint = Blueprint('wiki', __name__)
|
||||
|
||||
|
@ -28,19 +28,22 @@ def compare(name, fsha, dots, lsha):
|
|||
@blueprint.route("/_revert", methods=['POST'])
|
||||
@login_required
|
||||
def revert():
|
||||
name = request.form.get('name')
|
||||
cname = to_canonical(request.form.get('name'))
|
||||
commit = request.form.get('commit')
|
||||
cname = to_canonical(name)
|
||||
message = request.form.get('message', "Reverting %s" % cname)
|
||||
|
||||
if cname in current_app.config.get('WIKI_LOCKED_PAGES'):
|
||||
return dict(error=True, message="Page is locked")
|
||||
return dict(error=True, message="Page is locked"), 403
|
||||
|
||||
try:
|
||||
sha = g.current_wiki.revert_page(cname,
|
||||
commit,
|
||||
message=message,
|
||||
username=current_user.username,
|
||||
email=current_user.email)
|
||||
except PageNotFound as e:
|
||||
return dict(error=True, message=e.message), 404
|
||||
|
||||
sha = g.current_wiki.revert_page(name,
|
||||
commit,
|
||||
message=message,
|
||||
username=current_user.username,
|
||||
email=current_user.email)
|
||||
if sha:
|
||||
flash("Page reverted")
|
||||
|
||||
|
@ -104,7 +107,7 @@ def page_write(name):
|
|||
if request.method == 'POST':
|
||||
# Create
|
||||
if cname in current_app.config.get('WIKI_LOCKED_PAGES'):
|
||||
return dict(error=True, message="Page is locked")
|
||||
return dict(error=True, message="Page is locked"), 403
|
||||
|
||||
sha = g.current_wiki.write_page(cname,
|
||||
request.form['content'],
|
||||
|
@ -117,7 +120,7 @@ def page_write(name):
|
|||
edit_cname = to_canonical(request.form['name'])
|
||||
|
||||
if edit_cname in current_app.config.get('WIKI_LOCKED_PAGES'):
|
||||
return dict(error=True, message="Page is locked")
|
||||
return dict(error=True, message="Page is locked"), 403
|
||||
|
||||
if edit_cname != cname.lower():
|
||||
g.current_wiki.rename_page(cname, edit_cname)
|
||||
|
@ -132,6 +135,9 @@ def page_write(name):
|
|||
|
||||
else:
|
||||
# DELETE
|
||||
if cname in current_app.config.get('WIKI_LOCKED_PAGES'):
|
||||
return dict(error=True, message="Page is locked"), 403
|
||||
|
||||
sha = g.current_wiki.delete_page(name,
|
||||
username=current_user.username,
|
||||
email=current_user.email)
|
||||
|
|
1
realms/version.py
Normal file
1
realms/version.py
Normal file
|
@ -0,0 +1 @@
|
|||
__version__ = '0.3.24'
|
|
@ -1,3 +1,4 @@
|
|||
-r requirements.txt
|
||||
Flask-Testing==0.4.2
|
||||
nose==1.3.4
|
||||
blinker==1.3
|
||||
|
|
|
@ -1,18 +1 @@
|
|||
Flask==0.10.1
|
||||
Flask-Assets==0.10
|
||||
Flask-Cache==0.13.1
|
||||
Flask-Login==0.2.11
|
||||
Flask-SQLAlchemy==2.0
|
||||
Flask-WTF==0.10.2
|
||||
PyYAML==3.11
|
||||
bcrypt==1.0.2
|
||||
beautifulsoup4==4.3.2
|
||||
click==3.3
|
||||
gevent==1.0.1
|
||||
ghdiff==0.4
|
||||
gittle==0.4.0
|
||||
gunicorn==19.1.1
|
||||
itsdangerous==0.24
|
||||
lxml==3.4.0
|
||||
markdown2==2.3.0
|
||||
simplejson==3.6.3
|
||||
-e .
|
||||
|
|
28
setup.py
28
setup.py
|
@ -12,8 +12,8 @@ with open('README.md') as f:
|
|||
with open('requirements.txt') as f:
|
||||
required = f.read().splitlines()
|
||||
|
||||
with open('VERSION') as f:
|
||||
VERSION = f.read().strip()
|
||||
__version__ = None
|
||||
exec(open('realms/version.py').read())
|
||||
|
||||
CLASSIFIERS = [
|
||||
'Intended Audience :: Developers',
|
||||
|
@ -23,10 +23,28 @@ CLASSIFIERS = [
|
|||
'Topic :: Internet :: WWW/HTTP :: Dynamic Content']
|
||||
|
||||
setup(name='realms-wiki',
|
||||
version=VERSION,
|
||||
version=__version__,
|
||||
packages=find_packages(),
|
||||
install_requires=required,
|
||||
#scripts=['realms-wiki'],
|
||||
install_requires=[
|
||||
'Flask==0.10.1',
|
||||
'Flask-Assets==0.10',
|
||||
'Flask-Cache==0.13.1',
|
||||
'Flask-Login==0.2.11',
|
||||
'Flask-SQLAlchemy==2.0',
|
||||
'Flask-WTF==0.10.2',
|
||||
'PyYAML==3.11',
|
||||
'bcrypt==1.0.2',
|
||||
'beautifulsoup4==4.3.2',
|
||||
'click==3.3',
|
||||
'gevent==1.0.1',
|
||||
'ghdiff==0.4',
|
||||
'gittle==0.4.0',
|
||||
'gunicorn==19.1.1',
|
||||
'itsdangerous==0.24',
|
||||
'lxml==3.4.0',
|
||||
'markdown2==2.3.0',
|
||||
'simplejson==3.6.3'
|
||||
],
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'realms-wiki = realms.cli:cli'
|
||||
|
|
Loading…
Reference in a new issue