From 772943acb09db64743bd3c3c30a26749d768b3f3 Mon Sep 17 00:00:00 2001 From: Raoul Snyman <raoul@snyman.info> Date: Tue, 26 Jan 2021 22:38:37 -0700 Subject: [PATCH] Drag this up into the current technology --- .gitignore | 3 + setup.py | 49 +++++++ stickynotes.cfg | 2 +- stickynotes/__init__.py | 33 +++-- stickynotes/db.py | 15 ++ stickynotes/models.py | 32 +---- stickynotes/static/custom.css | 31 ++++- .../templates/{about.mako => about.html} | 4 +- stickynotes/templates/base.html | 51 +++++++ stickynotes/templates/base.mako | 71 ---------- .../templates/{index.mako => index.html} | 37 +++-- stickynotes/templates/raw.html | 1 + stickynotes/templates/raw.mako | 1 - stickynotes/templates/view.html | 16 +++ stickynotes/templates/view.mako | 9 -- stickynotes/views.py | 129 +++++++++--------- wsgiapp.py => wsgi.py | 3 +- 17 files changed, 273 insertions(+), 214 deletions(-) create mode 100644 .gitignore create mode 100644 setup.py create mode 100644 stickynotes/db.py rename stickynotes/templates/{about.mako => about.html} (81%) create mode 100644 stickynotes/templates/base.html delete mode 100644 stickynotes/templates/base.mako rename stickynotes/templates/{index.mako => index.html} (62%) create mode 100644 stickynotes/templates/raw.html delete mode 100644 stickynotes/templates/raw.mako create mode 100644 stickynotes/templates/view.html delete mode 100644 stickynotes/templates/view.mako rename wsgiapp.py => wsgi.py (70%) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cf1b9d7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +__pycache__ +*.egg-info +*.sqlite diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..506840a --- /dev/null +++ b/setup.py @@ -0,0 +1,49 @@ +from setuptools import setup + + +setup( + name='StickyNotes', + version='0.2', + author='Raoul Snyman', + description='A simple pastebin', + url='https://bin.snyman.info', + license='GPLv3+', + packages=['stickynotes'], + include_package_data=True, + platforms='any', + python_requires='>=3.5', + install_requires=[ + 'Flask', + 'Flask-SQLAlchemy', + 'Pygments', + 'requests', + 'short_url' + ], + extras_require={ + 'dev': [ + 'pytest>=3', + 'pytest-cov', + ], + }, + classifiers=[ + 'Development Status :: 2 - Pre-Alpha', + 'Environment :: Web Environment', + 'Framework :: Flask', + 'Intended Audience :: Other Audience', + 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', + 'Natural Language :: English', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3 :: Only', + 'Topic :: Internet :: WWW/HTTP', + 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', + 'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: Content Management System', + 'Topic :: Internet :: WWW/HTTP :: WSGI', + 'Topic :: Internet :: WWW/HTTP :: WSGI :: Application', + ], +) diff --git a/stickynotes.cfg b/stickynotes.cfg index 2c7ebb6..89a739f 100644 --- a/stickynotes.cfg +++ b/stickynotes.cfg @@ -1,5 +1,5 @@ [stickynotes] -database_url = sqlite:///stickynotes.sqlite +sqlalchemy_database_uri = sqlite:///stickynotes.sqlite secret_key = yoursecretkeyhere recaptcha_site_key = recaptcha_secret_key = diff --git a/stickynotes/__init__.py b/stickynotes/__init__.py index e79ee85..2c3124e 100644 --- a/stickynotes/__init__.py +++ b/stickynotes/__init__.py @@ -2,39 +2,42 @@ """ StickyNotes, yet another paste bin """ -import os -from ConfigParser import SafeConfigParser +from configparser import ConfigParser +from pathlib import Path from flask import Flask -from flask.ext.mako import MakoTemplates -from models import init_db -from views import views +from stickynotes.db import db +from stickynotes.views import views -def read_config(): +def read_config(config_path=None): """ Read the configuration file and return the values in a dictionary """ - config_file = os.path.abspath(os.path.join(os.path.dirname(__file__), u'..', u'stickynotes.cfg')) - config_parser = SafeConfigParser() + if config_path: + config_file = config_path / 'stickynotes.cfg' + else: + config_file = Path(__file__).parent / '..' / 'stickynotes.cfg' + config_parser = ConfigParser() config_parser.read(config_file) config = {} - for option in config_parser.options(u'stickynotes'): - config[option.upper()] = config_parser.get(u'stickynotes', option) - print(config) + for option in config_parser.options('stickynotes'): + config[option.upper()] = config_parser.get('stickynotes', option) return config -def make_app(): +def make_app(config_path=None): """ Create the application object """ app = Flask(__name__) # Load the config file - config = read_config() + config = read_config(config_path) app.config.update(config) - MakoTemplates(app) - init_db(config[u'DATABASE_URL']) + app.config.update({'SQLALCHEMY_TRACK_MODIFICATIONS': False}) + db.init_app(app) + with app.app_context(): + db.create_all() app.register_blueprint(views) return app diff --git a/stickynotes/db.py b/stickynotes/db.py new file mode 100644 index 0000000..dc6f272 --- /dev/null +++ b/stickynotes/db.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +""" +The basics of the database +""" +from flask_sqlalchemy import SQLAlchemy + +db = SQLAlchemy() +session = db.session +Model = db.Model +Column = db.Column +Integer = db.Integer +String = db.String +Text = db.String +DateTime = db.DateTime +Boolean = db.Boolean diff --git a/stickynotes/models.py b/stickynotes/models.py index 9b07bef..4e55d8c 100644 --- a/stickynotes/models.py +++ b/stickynotes/models.py @@ -4,44 +4,20 @@ The models in use """ from datetime import datetime -from sqlalchemy import Column, Integer, String, Text, DateTime, create_engine, Boolean -from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy.orm import sessionmaker, scoped_session - -BaseModel = declarative_base() -db_session = None +from stickynotes.db import Model, Column, Integer, String, Text, DateTime, Boolean -class StickyNote(BaseModel): +class StickyNote(Model): """ The main (only?) table in the system """ - __tablename__ = u'sticky_notes' + __tablename__ = 'sticky_notes' id = Column(Integer, autoincrement=True, primary_key=True) title = Column(String(255)) source = Column(Text) - lexer = Column(String(255), default=u'text') + lexer = Column(String(255), default='text') created = Column(DateTime, default=datetime.now, index=True) expiry = Column(DateTime, default=None, index=True) url = Column(String(255), index=True) private = Column(Boolean, default=False, index=True) - - -def init_db(database_url): - """ - Initialise the database connection - - :param database_url: The database connection URL - """ - global db_session - engine = create_engine(database_url, pool_recycle=3600) - db_session = scoped_session(sessionmaker(bind=engine))() - BaseModel.metadata.create_all(engine, checkfirst=True) - - -def get_session(): - """ - Get the current database session - """ - return db_session diff --git a/stickynotes/static/custom.css b/stickynotes/static/custom.css index d4bbeaf..b370cca 100644 --- a/stickynotes/static/custom.css +++ b/stickynotes/static/custom.css @@ -8,7 +8,7 @@ body { } body > .container { - padding: 70px 15px 0; + padding: 1rem 0; } .container .text-muted { @@ -27,18 +27,41 @@ body > .container { padding-left: 15px; } -code { - font-size: 80%; +code, kbd, pre, samp { + font-family: 'PT Mono', 'Hack', SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; } .sourcetable { - width: 100%; + width: 100%; } .linenos { text-align: right; } +td.linenos pre { + color: var(--light); + background-color: var(--secondary); +} + +.source { + color: var(--light); + background-color: var(--dark); +} + .note-links { margin-bottom: 10px; } + +.form-control, +.form-control:focus { + background-color: var(--dark); +} + +.form-control { + color: var(--light); +} + +.form-control:focus { + color: var(--white); +} diff --git a/stickynotes/templates/about.mako b/stickynotes/templates/about.html similarity index 81% rename from stickynotes/templates/about.mako rename to stickynotes/templates/about.html index 5ab1fd7..badad55 100644 --- a/stickynotes/templates/about.mako +++ b/stickynotes/templates/about.html @@ -1,7 +1,9 @@ -<%inherit file="base.mako"/> +{% extends "base.html" %} + {% block content %} <div class="row"> <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12"> <h2>About StickyNotes</h2> <p>StickyNotes is a quick code paste application written in Python with Flask, SQLAlchemy, Mako, Pygments and a few other Python libraries.</p> </div> </div> + {% endblock %} diff --git a/stickynotes/templates/base.html b/stickynotes/templates/base.html new file mode 100644 index 0000000..3d02ef9 --- /dev/null +++ b/stickynotes/templates/base.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html> + <head lang="en"> + <meta charset="utf-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <title>StickyNotes</title> + <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@4.6.0/dist/darkly/bootstrap.min.css" type="text/css"> + <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-select@1.13.14/dist/css/bootstrap-select.min.css" type="text/css"> + <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" type="text/css"> + <link rel="preconnect" href="https://fonts.gstatic.com"> + <link href="https://fonts.googleapis.com/css2?family=PT+Mono&display=swap" rel="stylesheet"> + <link href="/pygments.css" rel="stylesheet" type="text/css"> + <link href="/static/custom.css" rel="stylesheet" type="text/css"> + </head> + <body> + <nav class="navbar navbar-expand-lg navbar-dark bg-primary navbar-fixed-top"> + <div class="container"> + <a href="/" class="navbar-brand">StickyNotes</a> + <button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> + <span class="navbar-toggler-icon"></span> + </button> + <div id="navbar" class="collapse navbar-collapse"> + <ul class="navbar-nav mr-auto"> + <li class="nav-item{% if request.path == '/' %} active{% endif %}"><a class="nav-link" href="/">New</a></li> + {# <li class="nav-item{% if request.path == '/notes' %} active{% endif %}"><a class="nav-link" href="/notes">Notes</a></li> #} + <li class="nav-item{% if request.path == '/about' %} active{% endif %}"><a class="nav-link" href="/about">About</a></li> + </ul> + </div> + </div> + </nav> + <div class="container"> + {% for category, message in get_flashed_messages(True) %} + <div class="alert alert-{{category}}" role="alert"> + {{message}} + </div> + {% endfor %} + {% block content %} + {% endblock %} + </div> + <footer class="footer"> + <div class="container"> + <p class="text-muted">Copyright © 2015 Raoul Snyman.</p> + </div> + </footer> + <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> + <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-Piv4xVNRyMGpqkS2by6br4gNJ7DXjqk09RmUpJ8jgGtD7zP9yug3goQfGII0yAns" crossorigin="anonymous"></script> + <script src="https://cdn.jsdelivr.net/npm/bootstrap-select@1.13.14/dist/js/bootstrap-select.min.js"></script> + <script src="https://www.google.com/recaptcha/api.js" async defer></script> + </body> +</html> diff --git a/stickynotes/templates/base.mako b/stickynotes/templates/base.mako deleted file mode 100644 index c2bfdad..0000000 --- a/stickynotes/templates/base.mako +++ /dev/null @@ -1,71 +0,0 @@ -<!DOCTYPE html> -<html> -<head lang="en"> - <meta charset="utf-8"> - <meta http-equiv="X-UA-Compatible" content="IE=edge"> - <meta name="viewport" content="width=device-width, initial-scale=1"> - <title>StickyNotes</title> - <link href="//cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.9.3/css/bootstrap-select.min.css" rel="stylesheet" type="text/css"> - <link href="//maxcdn.bootstrapcdn.com/bootswatch/3.3.6/lumen/bootstrap.min.css" rel="stylesheet" type="text/css"> - <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css" rel="stylesheet"> - <link href="/static/custom.css" rel="stylesheet" type="text/css"> - <link href="/pygments.css" rel="stylesheet" type="text/css"> -</head> -<body> - <nav class="navbar navbar-default navbar-fixed-top"> - <div class="container"> - <div class="navbar-header"> - <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> - <span class="sr-only">Toggle navigation</span> - <span class="icon-bar"></span> - <span class="icon-bar"></span> - <span class="icon-bar"></span> - </button> - <a href="/" class="navbar-brand">StickyNotes</a> - </div> - <div id="navbar" class="collapse navbar-collapse"> - <ul class="nav navbar-nav"> -% if self.name == u'self:index.mako': - <li class="active"><a href="/">New</a></li> -%else: - <li><a href="/">New</a></li> -% endif -<%doc> -% if self.name == u'self:list.mako': - <li class="active"><a href="/notes">Notes</a></li> -%else: - <li><a href="/notes">Notes</a></li> -% endif -</%doc> -% if self.name == u'self:about.mako': - <li class="active"><a href="/about">About</a></li> -%else: - <li><a href="/about">About</a></li> -% endif - </ul> - </div> - </div> - </nav> - <div class="container"> -<% messages = get_flashed_messages(True) %> -% if messages: - % for category, message in messages: - <div class="alert alert-${category}" role="alert"> - ${message} - </div> - % endfor -% endif - ${self.body()} - </div> - <footer class="footer"> - <div class="container"> - <p class="text-muted">Copyright © 2015 Raoul Snyman.</p> - </div> - </footer> - <script src="//code.jquery.com/jquery-1.11.3.min.js"></script> - <script src="//code.jquery.com/jquery-migrate-1.2.1.min.js"></script> - <script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js" type="application/javascript"></script> - <script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.9.3/js/bootstrap-select.min.js"></script> - <script src="https://www.google.com/recaptcha/api.js" async defer></script> -</body> -</html> diff --git a/stickynotes/templates/index.mako b/stickynotes/templates/index.html similarity index 62% rename from stickynotes/templates/index.mako rename to stickynotes/templates/index.html index a240eea..1a1a9ac 100644 --- a/stickynotes/templates/index.mako +++ b/stickynotes/templates/index.html @@ -1,7 +1,8 @@ -<%inherit file="base.mako"/> +{% extends "base.html" %} + {% block content %} <div class="row"> <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12"> - <form role="form" action="/save" method="post" form> + <form role="form" action="/" method="post"> <div class="form-group"> <textarea name="source" id="source" class="form-control" rows="20"></textarea> </div> @@ -10,19 +11,15 @@ </div> <div class="form-group"> <label for="language">Language</label> - <select name="language" id="language" class="form-control selectpicker" data-live-search="true" data-size="15"> -% for language in lexers: - % if language[0] == 'text': - <option value="${language[0]}" selected>${language[1]}</option> - % else: - <option value="${language[0]}">${language[1]}</option> - % endif -% endfor + <select name="language" id="language" class="form-control selectpicker" data-live-search="true" data-size="15" data-style="btn-default"> + {% for language in lexers %} + <option value="{{language[0]}}"{% if language[0] == 'text' %} selected{% endif %}>{{language[1]}}</option> + {% endfor %} </select> </div> <div class="form-group"> <label for="expiry">Expiry</label> - <select name="expiry" id="expiry" class="form-control selectpicker"> + <select name="expiry" id="expiry" class="form-control selectpicker" data-style="btn-default"> <option value="never" selected>Never</option> <option value="10min">10 Minutes</option> <option value="1h">1 Hour</option> @@ -32,17 +29,19 @@ <option value="1m">1 Month</option> </select> </div> - <div class="checkbox"> - <label> - <input type="checkbox" name="private"> Unlisted (doesn't appear in the list on the notes page) - </label> - </div> -% if recaptcha_site_key: <div class="form-group"> - <div class="g-recaptcha" data-sitekey="${recaptcha_site_key}"></div> + <div class="custom-control custom-checkbox"> + <input type="checkbox" name="private" class="custom-control-input" id="private"> + <label for="private" class="custom-control-label">Unlisted (doesn't appear in the list on the notes page)</label> + </div> </div> -% endif + {% if recaptcha_site_key %} + <div class="form-group"> + <div class="g-recaptcha" data-sitekey="{{recaptcha_site_key}}"></div> + </div> + {% endif %} <button type="submit" class="btn btn-primary">Submit</button> </form> </div> </div> + {% endblock %} diff --git a/stickynotes/templates/raw.html b/stickynotes/templates/raw.html new file mode 100644 index 0000000..3a73287 --- /dev/null +++ b/stickynotes/templates/raw.html @@ -0,0 +1 @@ +{{source}} diff --git a/stickynotes/templates/raw.mako b/stickynotes/templates/raw.mako deleted file mode 100644 index fc64b76..0000000 --- a/stickynotes/templates/raw.mako +++ /dev/null @@ -1 +0,0 @@ -${source} diff --git a/stickynotes/templates/view.html b/stickynotes/templates/view.html new file mode 100644 index 0000000..39d6183 --- /dev/null +++ b/stickynotes/templates/view.html @@ -0,0 +1,16 @@ +{% extends "base.html" %} + {% block content %} + <div class="row"> + <div class="col-md-10 col-sm-12"> + <h4>{{note.title}}</h4> + </div> + <div class="col-md-2 col-sm-12 text-right"> + <a href="/raw/{{note.url}}" class="btn btn-secondary btn-sm"><i class="fa fa-fw fa-file-text"></i> Raw</a> + </div> + </div> + <div class="row"> + <div class="col-12"> + {{source | safe}} + </div> + </div> + {% endblock %} diff --git a/stickynotes/templates/view.mako b/stickynotes/templates/view.mako deleted file mode 100644 index d773c6f..0000000 --- a/stickynotes/templates/view.mako +++ /dev/null @@ -1,9 +0,0 @@ -<%inherit file="base.mako"/> - <div class="row"> - <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 text-right note-links"> - <a href="/raw/${note.url}" class="btn btn-default btn-sm"><i class="fa fa-fw fa-file-text"></i> Raw</a> - </div> - <div class="col-xs-12 col-sm-12 col-md-12 col-lg-12"> - ${source} - </div> - </div> diff --git a/stickynotes/views.py b/stickynotes/views.py index bb0689f..f5a41b3 100644 --- a/stickynotes/views.py +++ b/stickynotes/views.py @@ -2,29 +2,31 @@ """ The views """ -from datetime import timedelta, datetime import logging +from datetime import timedelta, datetime -from flask import Blueprint, redirect, request, flash, make_response, current_app -from flask.ext.mako import render_template +import requests +import short_url +from flask import Blueprint, redirect, request, flash, make_response, current_app, render_template from pygments import highlight from pygments.formatters.html import HtmlFormatter from pygments.lexers import get_lexer_by_name, get_all_lexers from sqlalchemy import or_ -import requests -import short_url -from models import StickyNote, get_session +from stickynotes.db import session +from stickynotes.models import StickyNote + log = logging.getLogger(__name__) -views = Blueprint(u'views', __name__) +views = Blueprint('views', __name__) + EXPIRY_DELTAS = { - u'10min': timedelta(minutes=10), - u'1d': timedelta(days=1), - u'1w': timedelta(days=7), - u'2w': timedelta(days=14), - u'1m': timedelta(days=30) + '10min': timedelta(minutes=10), + '1d': timedelta(days=1), + '1w': timedelta(days=7), + '2w': timedelta(days=14), + '1m': timedelta(days=30) } @@ -32,79 +34,78 @@ def _is_recaptcha_valid(secret, response, remote_ip=None): """ POST to the recaptcha service to check if the recaptcha is valid """ - data = {u'secret': secret, u'response': response} + data = {'secret': secret, 'response': response} if remote_ip: - data[u'remoteip'] = remote_ip - response = requests.post(u'https://www.google.com/recaptcha/api/siteverify', data=data) + data['remoteip'] = remote_ip + response = requests.post('https://www.google.com/recaptcha/api/siteverify', data=data) try: json_response = response.json() - print(json_response) - return json_response[u'success'] + return json_response['success'] except ValueError: - print response return False -@views.route('/', methods=[u'GET']) +@views.route('/', methods=['GET']) def index(): """ Add a new sticky note """ - all_lexers = [(lexer[1][0], lexer[0]) for lexer in get_all_lexers()] + all_lexers = [(lexer[1][0], lexer[0]) for lexer in get_all_lexers() if len(lexer) > 1 and len(lexer[1]) > 0] all_lexers.sort(key=lambda x: x[1].lower()) - now = datetime.utcnow() - recaptcha_site_key = current_app.config.get(u'RECAPTCHA_SITE_KEY') - return render_template(u'index.mako', lexers=all_lexers, recaptcha_site_key=recaptcha_site_key) + recaptcha_site_key = current_app.config.get('RECAPTCHA_SITE_KEY') + return render_template('index.html', lexers=all_lexers, recaptcha_site_key=recaptcha_site_key) -@views.route('/notes', methods=[u'GET']) +@views.route('/notes', methods=['GET']) def notes(): """ Show a list of recent notes """ - recent_notes = get_session().query(StickyNote)\ - .filter(or_(StickyNote.expiry == None, StickyNote.expiry < now))\ + recent_notes = StickyNote.query\ + .filter(or_(StickyNote.expiry == None, StickyNote.expiry < datetime.utcnow()))\ .filter(~StickyNote.private)\ .order_by(StickyNote.created.desc())\ - .limit(10) - return render_template(u'notes.mako', recent=recent_notes) + .limit(10) # noqa + return render_template('notes.html', recent=recent_notes) -@views.route('/about', methods=[u'GET']) +@views.route('/about', methods=['GET']) def about(): """ Show the about page """ - return render_template(u'about.mako') + return render_template('about.html') -@views.route('/save', methods=[u'POST']) +@views.route('/', methods=['POST']) def save(): """ Save a sticky note """ # Check if the recaptcha is valid - recaptcha_secret_key = current_app.config.get(u'RECAPTCHA_SECRET_KEY') - is_recaptcha_valid = False - try: - is_recaptcha_valid = _is_recaptcha_valid(recaptcha_secret_key, request.form[u'g-recaptcha-response']) - except KeyError: - flash(u'Unable to verify you, don\'t forget to complete the captcha.', u'danger') - print(u'No form variable') + recaptcha_secret_key = current_app.config.get('RECAPTCHA_SECRET_KEY') + if recaptcha_secret_key: + is_recaptcha_valid = False + try: + is_recaptcha_valid = _is_recaptcha_valid(recaptcha_secret_key, request.form['g-recaptcha-response']) + except KeyError: + flash('Unable to verify you, don\'t forget to complete the captcha.', 'danger') + print('No form variable') + else: + is_recaptcha_valid = True if not is_recaptcha_valid: - return redirect(u'/') + return redirect('/') # Save the note - db_session = get_session() try: created = datetime.utcnow() - expiry = EXPIRY_DELTAS.get(request.form[u'expiry']) + expiry = EXPIRY_DELTAS.get(request.form['expiry']) if expiry: expiry = created + expiry - source = request.form[u'source'] - lexer = request.form[u'language'] - title = request.form.get(u'title', u'') - private = True if request.form.get(u'private') else False - string_id = u''.join([source, lexer, title, created.isoformat()]) + source = request.form['source'] + lexer = request.form['language'] + title = request.form.get('title', '') + private = True if request.form.get('private') else False + string_id = ''.join([source, lexer, title, created.isoformat()]) url = short_url.encode_url(sum([ord(char) for char in string_id]), min_length=8) note = StickyNote( title=title, @@ -115,52 +116,52 @@ def save(): private=private, url=url ) - db_session.add(note) - db_session.commit() - return redirect(u'/' + note.url) + session.add(note) + session.commit() + return redirect('/' + note.url) except Exception as e: flash(str(e), 'danger') - db_session.rollback() - return redirect(u'/') + session.rollback() + return redirect('/') -@views.route('/<string:note_url>', methods=[u'GET']) +@views.route('/<string:note_url>', methods=['GET']) def view(note_url): """ Show a sticky note :param note_url: The note to show """ - note = get_session().query(StickyNote).filter(StickyNote.url == note_url).scalar() + note = StickyNote.query.filter(StickyNote.url == note_url).scalar() if not note: - flash(u'That note does not exist', 'danger') - return redirect(u'/') + flash('That note does not exist', 'danger') + return redirect('/') lexer = get_lexer_by_name(note.lexer) - formatter = HtmlFormatter(linenos=True, cssclass=u'source') + formatter = HtmlFormatter(linenos=True, cssclass='source') result = highlight(note.source, lexer, formatter) - return render_template(u'view.mako', note=note, source=result) + return render_template('view.html', note=note, source=result) -@views.route('/raw/<string:note_url>', methods=[u'GET']) +@views.route('/raw/<string:note_url>', methods=['GET']) def raw(note_url): """ Show the raw version of a sticky note :param note_url: The note to show """ - note = get_session().query(StickyNote).filter(StickyNote.url == note_url).scalar() + note = StickyNote.query.filter(StickyNote.url == note_url).scalar() if not note: - flash(u'That note does not exist', 'danger') - return redirect(u'/') - return render_template(u'raw.mako', source=note.source), 200, {'Content-Type': 'text/plain; charset=utf-8'} + flash('That note does not exist', 'danger') + return redirect('/') + return render_template('raw.html', source=note.source), 200, {'Content-Type': 'text/plain; charset=utf-8'} -@views.route(u'/pygments.css', methods=[u'GET']) +@views.route('/pygments.css', methods=['GET']) def pygments_css(): """ Return the Pygments CSS to the browser """ - response = make_response(HtmlFormatter().get_style_defs()) + response = make_response(HtmlFormatter(style='nord').get_style_defs()) response.headers['Content-Type'] = 'text/css' return response diff --git a/wsgiapp.py b/wsgi.py similarity index 70% rename from wsgiapp.py rename to wsgi.py index 87a0f37..0e3bac0 100644 --- a/wsgiapp.py +++ b/wsgi.py @@ -2,9 +2,10 @@ """ This is the entry point for the WSGI server """ +from pathlib import Path from stickynotes import make_app -application = make_app() +application = make_app(Path(__file__).parent) if __name__ == '__main__': application.run(debug=True)