commit a528892ac8b7e511ca584fd93286d47de071684b Author: Rupus Reinefjord Date: Thu Jan 11 23:40:31 2018 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..344aab2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +__pycache__/ +venv/ + +*.db +*.sqlite + +instance/* + +static/uploads/* +static/gen/* +static/.webassets-cache* diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..14e2f77 --- /dev/null +++ b/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..5b8881b --- /dev/null +++ b/Pipfile @@ -0,0 +1,21 @@ +[[source]] + +url = "https://pypi.python.org/simple" +verify_ssl = true +name = "pypi" + + +[packages] + +flask = "*" +peewee = "*" +flask-uploads = "*" +flask-login = "*" +flask-wtf = "*" +markdown = "*" +nodeenv = "*" +webassets = {git = "https://github.com/miracle2k/webassets.git", ref = "master"} +flask-assets = "*" + + +[dev-packages] diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..074868f --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,128 @@ +{ + "_meta": { + "hash": { + "sha256": "2de5ec90d8ee6f01b6383c466d82f6361f2326579862ae1b3c4014efd9e52fc8" + }, + "host-environment-markers": { + "implementation_name": "cpython", + "implementation_version": "3.6.4", + "os_name": "posix", + "platform_machine": "x86_64", + "platform_python_implementation": "CPython", + "platform_release": "4.14.12-1-ARCH", + "platform_system": "Linux", + "platform_version": "#1 SMP PREEMPT Fri Jan 5 18:19:34 UTC 2018", + "python_full_version": "3.6.4", + "python_version": "3.6", + "sys_platform": "linux" + }, + "pipfile-spec": 6, + "requires": {}, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.python.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "click": { + "hashes": [ + "sha256:29f99fc6125fbc931b758dc053b3114e55c77a6e4c6c3a2674a2dc986016381d", + "sha256:f15516df478d5a56180fbf80e68f206010e6d160fc39fa508b65e035fd75130b" + ], + "version": "==6.7" + }, + "flask": { + "hashes": [ + "sha256:0749df235e3ff61ac108f69ac178c9770caeaccad2509cb762ce1f65570a8856", + "sha256:49f44461237b69ecd901cc7ce66feea0319b9158743dd27a2899962ab214dac1" + ], + "version": "==0.12.2" + }, + "flask-assets": { + "hashes": [ + "sha256:6031527b89fb3509d1581d932affa5a79dd348cfffb58d0aef99a43461d47847" + ], + "version": "==0.12" + }, + "flask-login": { + "hashes": [ + "sha256:c815c1ac7b3e35e2081685e389a665f2c74d7e077cb93cecabaea352da4752ec" + ], + "version": "==0.4.1" + }, + "flask-uploads": { + "hashes": [ + "sha256:53ecbd6033667d50ae02b63adebbaa33c7fc56c09e5293025810cf9d841ecb02" + ], + "version": "==0.2.1" + }, + "flask-wtf": { + "hashes": [ + "sha256:d9a9e366b32dcbb98ef17228e76be15702cd2600675668bca23f63a7947fd5ac", + "sha256:5d14d55cfd35f613d99ee7cba0fc3fbbe63ba02f544d349158c14ca15561cc36" + ], + "version": "==0.14.2" + }, + "itsdangerous": { + "hashes": [ + "sha256:cbb3fcf8d3e33df861709ecaf89d9e6629cff0a217bc2848f1b41cd30d360519" + ], + "version": "==0.24" + }, + "jinja2": { + "hashes": [ + "sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd", + "sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4" + ], + "version": "==2.10" + }, + "markdown": { + "hashes": [ + "sha256:9ba587db9daee7ec761cfc656272be6aabe2ed300fece21208e4aab2e457bc8f", + "sha256:a856869c7ff079ad84a3e19cd87a64998350c2b94e9e08e44270faef33400f81" + ], + "version": "==2.6.11" + }, + "markupsafe": { + "hashes": [ + "sha256:a6be69091dac236ea9c6bc7d012beab42010fa914c459791d627dad4910eb665" + ], + "version": "==1.0" + }, + "nodeenv": { + "hashes": [ + "sha256:98835dab727f94a713eacc7234e3db6777a55cafb60f391485011899e5c818df" + ], + "version": "==1.2.0" + }, + "peewee": { + "hashes": [ + "sha256:2342067f48a779e35956a44cd547df883dda35153daa9fe994d970585aaec281" + ], + "version": "==2.10.2" + }, + "webassets": { + "hashes": [ + "sha256:e7d9c8887343123fd5b32309b33167428cb1318cdda97ece12d0907fd69d38db" + ], + "version": "==0.12.1" + }, + "werkzeug": { + "hashes": [ + "sha256:d5da73735293558eb1651ee2fddc4d0dedcfa06538b8813a2e20011583c9e49b", + "sha256:c3fd7a7d41976d9f44db327260e263132466836cef6f91512889ed60ad26557c" + ], + "version": "==0.14.1" + }, + "wtforms": { + "hashes": [ + "sha256:ffdf10bd1fa565b8233380cb77a304cd36fd55c73023e91d4b803c96bc11d46f" + ], + "version": "==2.1" + } + }, + "develop": {} +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..2151e4e --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# Website + +This is my personal website. Made with Flask and other stuff I like (and some +I don't like, unfortunatly). diff --git a/app.py b/app.py new file mode 100644 index 0000000..d961c3a --- /dev/null +++ b/app.py @@ -0,0 +1,295 @@ +import datetime +from urllib.parse import urlparse, urljoin + +import flask +import flask_assets +import flask_login +import flask_uploads +import flask_wtf +import flask_wtf.file +import markdown +import peewee as pw +import werkzeug +import wtforms + +from playhouse import flask_utils as pw_util + +app = flask.Flask(__name__, instance_relative_config=True) +app.config.from_object('config') +app.config.from_pyfile('config.py') + +db_wrapper = pw_util.FlaskDB(app) + +bundles = { + 'common_css': flask_assets.Bundle( + 'css/lib/normalize.css', + 'css/fonts.css', + 'css/style.css', + output='gen/common.css', + filters=['autoprefixer6', 'cleancss'], + ), +} + +assets = flask_assets.Environment(app) +assets.register(bundles) + +login_manager = flask_login.LoginManager() +login_manager.init_app(app) +login_manager.login_view = '.login' +login_manager.login_message_category = 'info' + +photo_set = flask_uploads.UploadSet('photos', flask_uploads.IMAGES) +flask_uploads.configure_uploads(app, photo_set) + + +def photo_resize_url(photo, size): + if app.debug: + return photo_set.url(photo.filename) + + return f"{photo_set.config.base_url}img{size}/{photo.filename}" + + +def image_resize_url(image, size): + if app.debug: + return f'static/images/{image}' + + return f"/static/images/img{size}/{image}" + + +app.jinja_env.globals['photo_resize_url'] = photo_resize_url +app.jinja_env.globals['image_resize_url'] = image_resize_url + + +class Photo(db_wrapper.Model): + filename = pw.CharField() + title = pw.CharField(null=True) + description = pw.TextField(null=True) + timestamp = pw.DateTimeField(default=datetime.datetime.now) + + @property + def html_description(self): + return markdown.markdown(self.description, output_format='html5') + + +class User(flask_login.UserMixin): + pass + + +class LoginForm(flask_wtf.FlaskForm): + password = wtforms.fields.PasswordField('Password') + + +class UploadPhotoForm(flask_wtf.FlaskForm): + photo = wtforms.fields.FileField('Photo', validators=[ + flask_wtf.file.FileRequired(), + flask_wtf.file.FileAllowed(photo_set, 'Images only!') + ]) + title = wtforms.fields.StringField('Title') + description = wtforms.fields.TextAreaField('Description') + + +class EditPhotoForm(flask_wtf.FlaskForm): + photo = wtforms.fields.FileField('Photo', validators=[ + flask_wtf.file.FileAllowed(photo_set, 'Images only!') + ]) + title = wtforms.fields.StringField('Title') + description = wtforms.fields.TextAreaField('Description') + + +class ConfirmForm(flask_wtf.FlaskForm): + pass + + +def flash_errors(form): + """Flash all errors in a form.""" + for field in form: + for error in field.errors: + flask.flash(("Error in {} field: {}" + .format(field.label.text, error)), + 'error' + ) + + +@login_manager.user_loader +def load_user(user_id): + if user_id != app.config['LOGIN']: + return + + user = User() + user.id = user_id + return user + + +def is_safe_url(target): + ref_url = urlparse(flask.request.host_url) + test_url = urlparse(urljoin(flask.request.host_url, target)) + return test_url.scheme in ('http', 'https') and \ + ref_url.netloc == test_url.netloc + + +@app.errorhandler(404) +@app.errorhandler(500) +def not_found(e): + return flask.render_template('error/{}.html'.format(e.code)), e.code + + +@app.route('/') +def index(): + return flask.render_template('index.html') + + +@app.route('/photo/', defaults={'page': 1}) +@app.route('/photo/page/') +def photography(page): + photos = Photo.select().order_by(Photo.timestamp.desc()).paginate(page, 9) + return flask.render_template('photography.html', photos=photos) + + +@app.route('/photo/') +def view_photo(photo_id): + query = Photo.select().order_by(Photo.timestamp.desc()) + + photo = pw_util.get_object_or_404(query, Photo.id == photo_id) + + try: + prev = query.where(Photo.timestamp < photo.timestamp).get() + + except Photo.DoesNotExist: + prev = None + + try: + next = (query.order_by(Photo.timestamp.asc()) + .where(Photo.timestamp > photo.timestamp).get()) + + except Photo.DoesNotExist: + next = None + + return flask.render_template('view_photo.html', + photo=photo, + prev=prev, + next=next) + + +@app.route('/me') +def me(): + return flask.render_template('me.html') + + +@app.route('/login', methods=['GET', 'POST']) +def login(): + form = LoginForm() + + if flask_login.current_user.is_authenticated: + return flask.redirect(flask.url_for('.admin')) + + if form.validate_on_submit(): + if form.password.data == app.config['LOGIN']: + user = User() + user.id = app.config['LOGIN'] + flask_login.login_user(user) + + next = flask.request.args.get('next') + + if not is_safe_url(next): + return flask.abort(400) + + flask.flash('Login successful!', 'success') + return flask.redirect(next or flask.url_for('.admin')) + + else: + flask.flash('Wrong password.', 'error') + + return flask.render_template('login.html', form=form) + + +@app.route('/logout') +def logout(): + if flask_login.current_user.is_authenticated: + flask_login.logout_user() + else: + flask.flash('You have to be logged in to log out! ಠ_ಠ', 'error') + + return flask.redirect(flask.url_for('.index')) + + +@app.route('/admin/') +@flask_login.login_required +def admin(): + return flask.render_template('admin/admin.html') + + +@app.route('/admin/photos/') +@flask_login.login_required +def admin_photos(): + photos = Photo.select().order_by(Photo.timestamp.desc()) + return flask.render_template('admin/photos.html', photos=photos) + + +@app.route('/admin/photos/', methods=['GET', 'POST']) +@flask_login.login_required +def admin_edit_photo(photo_id): + photo = pw_util.get_object_or_404(Photo, Photo.id == photo_id) + + form = EditPhotoForm( + werkzeug.datastructures.CombinedMultiDict((flask.request.files, + flask.request.form)), + obj=photo + ) + + if form.validate_on_submit(): + if form.photo.data: + photo.filename = photo_set.save(form.photo.data) + + photo.title = form.title.data + photo.description = form.description.data + + photo.save() + + flask.flash('Photo updated!', 'success') + + else: + flash_errors(form) + + return flask.render_template('admin/photo.html', form=form, photo=photo) + + +@app.route('/admin/photos/new', methods=['GET', 'POST']) +@flask_login.login_required +def admin_new_photo(): + form = UploadPhotoForm() + + if form.validate_on_submit(): + filename = photo_set.save(form.photo.data) + photo = Photo.create(filename=filename, + title=form.title.data or None, + description=form.description.data or None + ) + + flask.flash('Photo uploaded successfully!', 'success') + return flask.redirect(flask.url_for('admin_edit_photo', + photo_id=photo.id)) + + else: + flash_errors(form) + + return flask.render_template('admin/new_photo.html', form=form) + + +@app.route('/admin/photos/remove/', methods=['GET', 'POST']) +@flask_login.login_required +def admin_remove_photo(photo_id): + photo = pw_util.get_object_or_404(Photo, Photo.id == photo_id) + + form = ConfirmForm() + + if form.validate_on_submit(): + photo.delete_instance() + flask.flash('Photo removed.', 'success') + return flask.redirect(flask.url_for('admin_photos')) + + else: + flash_errors(form) + + return flask.render_template('admin/remove_photo.html', + form=form, + photo=photo) diff --git a/config.py b/config.py new file mode 100644 index 0000000..b7629d2 --- /dev/null +++ b/config.py @@ -0,0 +1,11 @@ +SECRET_KEY = 'secret!' +TEMPLATES_AUTO_RELOAD = True + +LOGIN = 'supersecretpassword' + +DATABASE = 'sqlite:///instance/db.sqlite' + +UPLOADS_DEFAULT_DEST = 'static/uploads' +UPLOADS_DEFAULT_URL = 'http://localhost:5000/static/uploads/' + +AUTOPREFIXER_BIN = 'postcss' diff --git a/npm-requirements.txt b/npm-requirements.txt new file mode 100644 index 0000000..b29e2dd --- /dev/null +++ b/npm-requirements.txt @@ -0,0 +1,3 @@ +postcss-cli@4.1.1 +autoprefixer@7.2.4 +clean-css-cli@4.1.10 diff --git a/static/css/fonts.css b/static/css/fonts.css new file mode 100644 index 0000000..8667960 --- /dev/null +++ b/static/css/fonts.css @@ -0,0 +1,66 @@ +@font-face { + font-family: "Crimson Text"; + src: url('/static/fonts/crimsontext/Crimson-Roman.woff2') format('woff2'), + url('/static/fonts/crimsontext/Crimson-Roman.woff') format('woff'); + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: "Crimson Text"; + src: url('/static/fonts/crimsontext/Crimson-Italic.woff2') format('woff2'), + url('/static/fonts/crimsontext/Crimson-Italic.woff') format('woff'); + font-weight: 400; + font-style: italic; + font-display: swap; +} + +@font-face { + font-family: "Crimson Text"; + src: url('/static/fonts/crimsontext/Crimson-SemiBold.woff2') format('woff2'), + url('/static/fonts/crimsontext/Crimson-SemiBold.woff') format('woff'); + font-weight: 600; + font-display: swap; +} + +@font-face { + font-family: "Crimson Text"; + src: url('/static/fonts/crimsontext/Crimson-SemiBoldItalic.woff2') format('woff2'), + url('/static/fonts/crimsontext/Crimson-SemiBoldItalic.woff') format('woff'); + font-weight: 600; + font-style: italic; + font-display: swap; +} + +@font-face { + font-family: "Crimson Text"; + src: url('/static/fonts/crimsontext/Crimson-Bold.woff2') format('woff2'), + url('/static/fonts/crimsontext/Crimson-Bold.woff') format('woff'); + font-weight: 700; + font-display: swap; +} + +@font-face { + font-family: "Crimson Text"; + src: url('/static/fonts/crimsontext/Crimson-BoldItalic.woff2') format('woff2'), + url('/static/fonts/crimsontext/Crimson-BoldItalic.woff') format('woff'); + font-weight: 700; + font-style: italic; + font-display: swap; +} + +@font-face { + font-family: "Fira Mono"; + src: url('/static/fonts/firamono/FiraMono-Regular.woff2') format('woff2'), + url('/static/fonts/firamono/FiraMono-Regular.woff') format('woff'); + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: "Fira Mono"; + src: url('/static/fonts/firamono/FiraMono-Bold.woff2') format('woff2'), + url('/static/fonts/firamono/FiraMono-Bold.woff') format('woff'); + font-weight: 600; + font-display: swap; +} diff --git a/static/css/lib/normalize.css b/static/css/lib/normalize.css new file mode 100644 index 0000000..fa4e73d --- /dev/null +++ b/static/css/lib/normalize.css @@ -0,0 +1,447 @@ +/*! normalize.css v7.0.0 | MIT License | github.com/necolas/normalize.css */ + +/* Document + ========================================================================== */ + +/** + * 1. Correct the line height in all browsers. + * 2. Prevent adjustments of font size after orientation changes in + * IE on Windows Phone and in iOS. + */ + +html { + line-height: 1.15; /* 1 */ + -ms-text-size-adjust: 100%; /* 2 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers (opinionated). + */ + +body { + margin: 0; +} + +/** + * Add the correct display in IE 9-. + */ + +article, +aside, +footer, +header, +nav, +section { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + * 1. Add the correct display in IE. + */ + +figcaption, +figure, +main { /* 1 */ + display: block; +} + +/** + * Add the correct margin in IE 8. + */ + +figure { + margin: 1em 40px; +} + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * 1. Remove the gray background on active links in IE 10. + * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. + */ + +a { + background-color: transparent; /* 1 */ + -webkit-text-decoration-skip: objects; /* 2 */ +} + +/** + * 1. Remove the bottom border in Chrome 57- and Firefox 39-. + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Prevent the duplicate application of `bolder` by the next rule in Safari 6. + */ + +b, +strong { + font-weight: inherit; +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font style in Android 4.3-. + */ + +dfn { + font-style: italic; +} + +/** + * Add the correct background and color in IE 9-. + */ + +mark { + background-color: #ff0; + color: #000; +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + */ + +audio, +video { + display: inline-block; +} + +/** + * Add the correct display in iOS 4-7. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Remove the border on images inside links in IE 10-. + */ + +img { + border-style: none; +} + +/** + * Hide the overflow in IE. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers (opinionated). + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: sans-serif; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` + * controls in Android 4. + * 2. Correct the inability to style clickable types in iOS and Safari. + */ + +button, +html [type="button"], /* 1 */ +[type="reset"], +[type="submit"] { + -webkit-appearance: button; /* 2 */ +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Correct the padding in Firefox. + */ + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * 1. Add the correct display in IE 9-. + * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + display: inline-block; /* 1 */ + vertical-align: baseline; /* 2 */ +} + +/** + * Remove the default vertical scrollbar in IE. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10-. + * 2. Remove the padding in IE 10-. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding and cancel buttons in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-cancel-button, +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in IE 9-. + * 1. Add the correct display in Edge, IE, and Firefox. + */ + +details, /* 1 */ +menu { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Scripting + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + */ + +canvas { + display: inline-block; +} + +/** + * Add the correct display in IE. + */ + +template { + display: none; +} + +/* Hidden + ========================================================================== */ + +/** + * Add the correct display in IE 10-. + */ + +[hidden] { + display: none; +} diff --git a/static/css/style.css b/static/css/style.css new file mode 100644 index 0000000..d82e348 --- /dev/null +++ b/static/css/style.css @@ -0,0 +1,178 @@ +* { + box-sizing: border-box; + overflow-wrap: break-word; + word-wrap: break-word; /* legacy */ + word-break: break-word; /* for WebKit/Chrome */ +} + +.wrapper > * { + /* Fixes overflowing content (anything other than "overflow: visible") + * in grid. Won't overflow without this. */ + min-width: 0; + max-width: 100%; +} + +img { + max-width: 100%; + vertical-align: middle; +} + +body { + font-size: 18px; + line-height: 1.6; + font-family: "Crimson Text", serif; + text-decoration-skip: ink; + display: flex; + flex-direction: column; + align-items: center; +} + +.wrapper { + display: grid; + grid-gap: 1rem; + grid-template-columns: minmax(0, 40rem); + padding: 1rem 1rem 5rem 1rem; +} + +code, .email { + font-family: "Fira Mono", monospace; + font-size: 15px +} + +header h1 { + margin: 0; +} + +header a { + text-decoration: none; +} + +nav ul { + list-style: none; + padding: 0; + margin: 0; +} + +.sitenav { + padding-left: 1rem; +} + +.nav-item { + color: #000; +} + +.nav-item:hover { + color: #666; +} + +.active { + color: #666; + text-decoration: none; +} + +.photo-grid { + display: grid; + grid-gap: 1rem; + grid-template-columns: repeat(auto-fill, 200px); + justify-content: center; + align-items: center; + justify-items: center; +} + +.photo-grid img { + max-height: 220px; +} + +.view-image { + display: flex; + flex-direction: column; +} + +.view-image img { + max-height: 700px; + align-self: center; +} + +.view-image nav { + align-self: center; + margin-top: 2rem; + max-width: 24rem; + width: 100%; + display: flex; +} + +.view-image form { + margin-top: 2rem; + border-top: 1px solid #ddd; + padding-top: 1rem; +} + +.prev { + margin-left: auto; +} + +.field { + margin: 1rem 0; + display: block; +} + +.field > label:first-child { + display: block; +} + +textarea { + width: 30rem; + height: 15rem; + max-width: 100%; +} + +.flashes { + justify-self: center; + text-align: center; + list-style: none; + margin: .5rem 1rem; + padding: 0; +} +.flash { + min-width: 15em; + padding: .5rem 1rem; + margin: .5rem auto; + border-radius: 7px; +} +.success { + background-color: #c9f7dd; + border: 1px solid #62d895; + color: #004d21; +} +.info { + background-color: #cbdef5; + border: 1px solid #6797d0; + color: #042246; +} +.error { + background-color: #ffd9d0; + border: 1px solid #ff9073; + color: #6c1600; +} + +@media screen and (min-width: 60rem) { + .wrapper { + grid-template-columns: 14rem 40rem; + } + + header { + grid-column: 1 / -1; + } + + nav { + grid-column: 1; + } + + main { + grid-column: 2; + } + + .photo-grid { + justify-content: left; + } +} diff --git a/static/fonts/crimsontext/Crimson-Bold.woff b/static/fonts/crimsontext/Crimson-Bold.woff new file mode 100644 index 0000000..63bfc7d Binary files /dev/null and b/static/fonts/crimsontext/Crimson-Bold.woff differ diff --git a/static/fonts/crimsontext/Crimson-Bold.woff2 b/static/fonts/crimsontext/Crimson-Bold.woff2 new file mode 100644 index 0000000..12e67e9 Binary files /dev/null and b/static/fonts/crimsontext/Crimson-Bold.woff2 differ diff --git a/static/fonts/crimsontext/Crimson-BoldItalic.woff b/static/fonts/crimsontext/Crimson-BoldItalic.woff new file mode 100644 index 0000000..cdf5547 Binary files /dev/null and b/static/fonts/crimsontext/Crimson-BoldItalic.woff differ diff --git a/static/fonts/crimsontext/Crimson-BoldItalic.woff2 b/static/fonts/crimsontext/Crimson-BoldItalic.woff2 new file mode 100644 index 0000000..cb7d404 Binary files /dev/null and b/static/fonts/crimsontext/Crimson-BoldItalic.woff2 differ diff --git a/static/fonts/crimsontext/Crimson-Italic.woff b/static/fonts/crimsontext/Crimson-Italic.woff new file mode 100644 index 0000000..92cd01d Binary files /dev/null and b/static/fonts/crimsontext/Crimson-Italic.woff differ diff --git a/static/fonts/crimsontext/Crimson-Italic.woff2 b/static/fonts/crimsontext/Crimson-Italic.woff2 new file mode 100644 index 0000000..b174e00 Binary files /dev/null and b/static/fonts/crimsontext/Crimson-Italic.woff2 differ diff --git a/static/fonts/crimsontext/Crimson-Roman-webfont.woff2 b/static/fonts/crimsontext/Crimson-Roman-webfont.woff2 new file mode 100644 index 0000000..6b3b07e Binary files /dev/null and b/static/fonts/crimsontext/Crimson-Roman-webfont.woff2 differ diff --git a/static/fonts/crimsontext/Crimson-Roman.woff b/static/fonts/crimsontext/Crimson-Roman.woff new file mode 100644 index 0000000..67a7781 Binary files /dev/null and b/static/fonts/crimsontext/Crimson-Roman.woff differ diff --git a/static/fonts/crimsontext/Crimson-Roman.woff2 b/static/fonts/crimsontext/Crimson-Roman.woff2 new file mode 100644 index 0000000..34a5dc8 Binary files /dev/null and b/static/fonts/crimsontext/Crimson-Roman.woff2 differ diff --git a/static/fonts/crimsontext/Crimson-SemiBold.woff b/static/fonts/crimsontext/Crimson-SemiBold.woff new file mode 100644 index 0000000..71adfed Binary files /dev/null and b/static/fonts/crimsontext/Crimson-SemiBold.woff differ diff --git a/static/fonts/crimsontext/Crimson-SemiBold.woff2 b/static/fonts/crimsontext/Crimson-SemiBold.woff2 new file mode 100644 index 0000000..b13d592 Binary files /dev/null and b/static/fonts/crimsontext/Crimson-SemiBold.woff2 differ diff --git a/static/fonts/crimsontext/Crimson-SemiBoldItalic.woff b/static/fonts/crimsontext/Crimson-SemiBoldItalic.woff new file mode 100644 index 0000000..b06b3b7 Binary files /dev/null and b/static/fonts/crimsontext/Crimson-SemiBoldItalic.woff differ diff --git a/static/fonts/crimsontext/Crimson-SemiBoldItalic.woff2 b/static/fonts/crimsontext/Crimson-SemiBoldItalic.woff2 new file mode 100644 index 0000000..acdf6f6 Binary files /dev/null and b/static/fonts/crimsontext/Crimson-SemiBoldItalic.woff2 differ diff --git a/static/fonts/crimsontext/LICENSE.txt b/static/fonts/crimsontext/LICENSE.txt new file mode 100644 index 0000000..23812c5 --- /dev/null +++ b/static/fonts/crimsontext/LICENSE.txt @@ -0,0 +1,93 @@ +Copyright (c) 2012-2014, The Crimson Project Developers. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/static/fonts/firamono/FiraMono-Bold.woff b/static/fonts/firamono/FiraMono-Bold.woff new file mode 100644 index 0000000..735352f Binary files /dev/null and b/static/fonts/firamono/FiraMono-Bold.woff differ diff --git a/static/fonts/firamono/FiraMono-Bold.woff2 b/static/fonts/firamono/FiraMono-Bold.woff2 new file mode 100644 index 0000000..832aaab Binary files /dev/null and b/static/fonts/firamono/FiraMono-Bold.woff2 differ diff --git a/static/fonts/firamono/FiraMono-Regular.woff b/static/fonts/firamono/FiraMono-Regular.woff new file mode 100644 index 0000000..b38ee14 Binary files /dev/null and b/static/fonts/firamono/FiraMono-Regular.woff differ diff --git a/static/fonts/firamono/FiraMono-Regular.woff2 b/static/fonts/firamono/FiraMono-Regular.woff2 new file mode 100644 index 0000000..9fa44b7 Binary files /dev/null and b/static/fonts/firamono/FiraMono-Regular.woff2 differ diff --git a/static/fonts/firamono/LICENSE b/static/fonts/firamono/LICENSE new file mode 100644 index 0000000..37c4a30 --- /dev/null +++ b/static/fonts/firamono/LICENSE @@ -0,0 +1,94 @@ +Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. +with Reserved Font Name < Fira >, + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/static/js/emaildecode.js b/static/js/emaildecode.js new file mode 100644 index 0000000..ace8366 --- /dev/null +++ b/static/js/emaildecode.js @@ -0,0 +1,8 @@ +var emails = document.getElementsByClassName("email"); + +for (var i = 0; i < emails.length; i++) { + var b64 = emails[i].innerHTML; + var decoded = window.atob(b64); + emails[i].innerHTML = decoded; + emails[i].href = "mailto:" + decoded; +} diff --git a/static/rupusreinefjord_pubkey.asc b/static/rupusreinefjord_pubkey.asc new file mode 100644 index 0000000..d19f1fa --- /dev/null +++ b/static/rupusreinefjord_pubkey.asc @@ -0,0 +1,112 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQINBFfJf7MBEACprYkxM1P4iYpWWDRkLsfS5QJCzh7pBGzfc82izOdWBmWwmKYI +o0MPOEtWaRbS6TNd7ezJcM3AxrX+cYErRE9S9C4Vk9vOUCflkDv2u/0vAEdRh9It +zVlzio/1fOaW4POrN/gp2JJl9YSzh3XvDiftmd+K2JzqVQTj2MXedLjeFtEccXNQ +q0rYsAfKE0nU8/B3JLTp2LmB2VvDne50IznrtnEdC75mD3Thlc1YZQPF+G1bDU7h +679khPeKDEE1vK+GYBSA67ADkniJIyOEzYcJfobjpf0YFIKzaNwAmez3RCntY2hM +XBQtkxjRPFS51322T/eAY/+MBxfHATq/iqTETWIOvT5M7WMQwIahI+YauLa5i3++ +1dI7OuJ6wwmBzBtRt5XFeHRXo1FkylWB5mXh76zPGjqiI/soMcnWubORhHBXXXb5 +XMPjiAUBfU5iG2m/Qn99//zmHJqXflVzobH15TPyLHWWhm3K6s9fQTFPTmrI9Y3f +HF792ingAAxevmBpQDwZLEST2YGSXsD8uOZyUlTDMikHyoUTQE4xLpyMvGsrGuvx +owGL2RuGxzUjDGb5Grp6PvApVKa9NqSga4PHVxINZ0+wff5PbVb70/qCq4fACThk +Sz7n6GcGwR7OaT31VdwHZ/qbUzG3+DUMVWdgEBwmrnygTAbOtN+rFzaq5wARAQAB +tCJSdXB1cyBSZWluZWZqb3JkIDxydXB1c0BrbHRyc3Quc2U+iQI3BBMBCAAhBQJX +yX+zAhsDBQsJCAcCBhUICQoLAgQWAgMBAh4BAheAAAoJEOkYmZvmaIvS5n4P/3YM +1NTfKucaY8vozXMPB1GWIWAhfWpWqjFR6W5+fWPRxj26kfbJ201l0l+R9xQWwfgm +d9+1J7K0ok/sK2vXkB/xyBWCLYDlDa2bOQc1XEBdk4C5gVawJkVh98J/k/LYgMZr +ahqS7A4ApopFRS12YD61Rm0ls+vJDPVDMOvhiGAexYsJ18aqpobGekL92m1AlRFS +6gGp79KKLTdf1qArzuU1m7w8ukv+j7DrZ5L56WaGXGU5X+pyKXLqT+oe/7IpBCsv +tRhwxTtabNTr4Unxtn619Zq7CzUMkGVhE65NEJWQSKb8F80CDniXYg77StzIcHnZ +g+cZ0KB+0TfyZcC7vIGJJiIC/ld46+8K7DT/Cz0A3TXOZNObNW98Drud8yC1TjYG +jHLY83MkM8No11aczfJ9ZPZGWDO9BOY24mr8zidZWeS/lqM9FNIJCah+VUlSsKQn +mOoau4T+OVhGnAlzyB7MyYK54VpO6Y07OhLujD39JKcfyRFMy5cpSlF0SQjy8xyD +R1rjf9jgzvnR605K/NM3J/HV/2H25ZXob2h2+/iiUCthOuHbYnF0j3PQN8BKRFlr +dHAzV5oMKCLz666j6Cyp1Fy7Dn/VLxGxM91v9JtAid6SWfkS/N3mkHl8ooVJVg1U +995EqywlK61zsYpEbJfQl6DSkaoglUxv7CVTA15liQJRBBMBCAA7AhsDBQsJCAcC +BhUICQoLAgQWAgMBAh4BAheAFiEE4nXD8Idl68PJBmb06RiZm+Zoi9IFAlnE5xcC +GQEACgkQ6RiZm+Zoi9I1jA/+IvZRLjk/4/c8W2MZnwhljE3acHWI9hHWpf7lX+9D +logKOTnPZ+NhjNF7aHd+JkwUFLKB+OorIyoSZQIdx11VnidlQjp6niCbeUr/T5eu +s99WdDxRNnSkNvTtnzc33JvFafN4D0q44RvyAk3UUWtWnDB9HwL58LQKr6NuIZxy +MXHHZJ3ugh5i6j6KPXFGKK57V8kX0rN3GZ522bDE7o23ziB9GB5dqeAOYW1d0xed +yIMi8P/cqVphIzXM14zCv1dJd5NjDZpTnJ06+E6oQT3HWmkUF3O+6aboW0XPlpRW +aK4P3li1Y1E00M2m7JAAbNC4sh7XZ0VzNll64siJeOSw1NoKvLYEoHL5Oeq6WBP7 +ZYViWTkkL3JYiWZGFGQHdASG6i5X0QKPCNo51beEQOXC71iK6FQKXUgqtcMKRW4T +ogptY4//3PAq4WjXnEhUdkK40wLnrC/hEMQvNOzbZzQAgQV2hRmRap639WMdjzYE +1KuctBEX9doBThxgN/A3bQO0Uzcs0Bv493yubDBqA1lNHGnJppDRlV2Dj2yc+x7w +YMsLIQOZJpdT8FquY+vlSwxZpbHWOGN6t/e7vzGqQ5n4lfViOonvZdRShVHInjdA +6ScugVq0ftogTxzg50CHNfA9WR7j6qEqA0IPla0ylpQDfWCI2QuY/N6W2NLdH9u9 +eZi0LVJ1cHVzIFJlaW5lZmpvcmQgPHdlYm1hc3RlckB0ZWtub2xvZ2tvcmVuLnNl +PokCTQQTAQgAOBYhBOJ1w/CHZevDyQZm9OkYmZvmaIvSBQJZxOMoAhsDBQsJCAcC +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 +=g3+W +-----END PGP PUBLIC KEY BLOCK----- diff --git a/templates/admin/admin.html b/templates/admin/admin.html new file mode 100644 index 0000000..45b55b8 --- /dev/null +++ b/templates/admin/admin.html @@ -0,0 +1,12 @@ +{% extends 'base.html' %} + +{% set navbar = [ +(url_for('admin_new_photo'), 'new_photo', 'Upload new photo'), +(url_for('admin_photos'), 'photos', 'View photos'), +(url_for('logout'), 'logout', 'Logout') +] %} + + +{% block header %} +

administration

+{% endblock %} diff --git a/templates/admin/new_photo.html b/templates/admin/new_photo.html new file mode 100644 index 0000000..aaaa694 --- /dev/null +++ b/templates/admin/new_photo.html @@ -0,0 +1,26 @@ +{% extends 'admin/admin.html' %} + +{% set title = 'New photo' %} + +{% set active_page = 'new_photo' %} + +{% block body %} +
+
+ {{ form.csrf_token }} +
+ {{ form.photo.label }} + {{ form.photo }} +
+
+ {{ form.title.label }} + {{ form.title }} +
+
+ {{ form.description.label }} + {{ form.description }} +
+ +
+
+{% endblock %} diff --git a/templates/admin/photo.html b/templates/admin/photo.html new file mode 100644 index 0000000..a5d76d3 --- /dev/null +++ b/templates/admin/photo.html @@ -0,0 +1,34 @@ +{% extends 'admin/admin.html' %} + +{% set title = photo.title or 'Unnamed photo' %} + +{% set active_page = 'photos' %} + +{% block body %} +
+ + +

{{ photo.title or 'Unnamed photo' }}

+ {{ photo.description or 'No description'}} + +
+ {{ form.csrf_token }} +
+ {{ form.photo.label }} + {{ form.photo }} +
+
+ {{ form.title.label }} + {{ form.title }} +
+
+ {{ form.description.label }} + {{ form.description }} +
+ +
+
+ Remove +
+
+{% endblock %} diff --git a/templates/admin/photos.html b/templates/admin/photos.html new file mode 100644 index 0000000..6586664 --- /dev/null +++ b/templates/admin/photos.html @@ -0,0 +1,18 @@ +{% extends 'admin/admin.html' %} + +{% set title = 'Photos' %} + +{% set active_page = 'photos' %} + +{% block body %} +
+

Photos

+ {% if photos %} + + {% endif %} +
+{% endblock %} diff --git a/templates/admin/remove_photo.html b/templates/admin/remove_photo.html new file mode 100644 index 0000000..f1cb7b8 --- /dev/null +++ b/templates/admin/remove_photo.html @@ -0,0 +1,26 @@ +{% extends 'admin/admin.html' %} + +{% set title = 'Remove ' + (photo.title or 'unnamed photo') %} + +{% set active_page = 'photos' %} + +{% block body %} +
+ + +

{{ photo.title or 'Unnamed photo' }}

+ {{ photo.description or 'No description'}} + +
+ {{ form.csrf_token }} +

Remove photo?

+

Are you sure?

+
+ +
+ +
+
+{% endblock %} diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..648c3a3 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,44 @@ + + + + {% block head %} + {{ title }} | reinefjord + + + + + {% assets 'common_css' %} + + {% endassets %} + {% endblock %} + + +
+
+ {% block header %}{% endblock %} +
+ + + + {% with messages = get_flashed_messages(with_categories=true) %} + {% if messages %} +
    + {% for category, message in messages %} +
  • {{ message|safe }}
  • + {% endfor %} +
+ {% endif %} + {% endwith %} + + {% block body %}{% endblock %} +
+ + diff --git a/templates/error/404.html b/templates/error/404.html new file mode 100644 index 0000000..7544b50 --- /dev/null +++ b/templates/error/404.html @@ -0,0 +1,10 @@ +{% extends "layout.html" %} + +{% set title = "404 - Not found" %} + +{% block body %} +
+

404 — Not found

+

The page you were looking for was not found :(

+
+{% endblock %} diff --git a/templates/error/500.html b/templates/error/500.html new file mode 100644 index 0000000..7ec5a9e --- /dev/null +++ b/templates/error/500.html @@ -0,0 +1,11 @@ +{% extends "layout.html" %} + +{% set title = "500 - Internal Server Error" %} + +{% block body %} +
+

500 — Internal Server Error

+

It seems like something broke on the server end. Sorry! :S

+

Try again or try something else.

+
+{% endblock %} diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..f45cb67 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,13 @@ +{% extends 'layout.html' %} + +{% set active_page = 'index' %} +{% set title = '^_^' %} + +{% block body %} + +
+

Welcome

+

... to my fancy webpage!

+
+ +{% endblock %} diff --git a/templates/layout.html b/templates/layout.html new file mode 100644 index 0000000..e38ebd0 --- /dev/null +++ b/templates/layout.html @@ -0,0 +1,11 @@ +{% extends 'base.html' %} + +{% set navbar = [ +(url_for('index'), 'index', 'Home'), +(url_for('photography'), 'photo', 'Photography'), +(url_for('me'), 'me', 'Find me') +] %} + +{% block header %} +

rupus reinefjord

+{% endblock %} diff --git a/templates/login.html b/templates/login.html new file mode 100644 index 0000000..1e18a37 --- /dev/null +++ b/templates/login.html @@ -0,0 +1,20 @@ +{% extends 'layout.html' %} + +{% set active_page = None %} +{% set title = 'Login' %} + +{% block body %} + +
+

Login

+
+ {{ form.csrf_token }} +
+ {{ form.password.label }} + {{ form.password }} +
+ +
+
+ +{% endblock %} diff --git a/templates/me.html b/templates/me.html new file mode 100644 index 0000000..8420ad1 --- /dev/null +++ b/templates/me.html @@ -0,0 +1,31 @@ +{% extends 'layout.html' %} + +{% set active_page = 'me' %} +{% set title = 'Find me' %} + +{% block body %} +
+

Find me

+
+
GitHub:
+
reinefjord
+
Instagram:
+
@rupusreinefjord
+
Flickr:
+
koltrast_
+
Keybase:
+
rupus
+
+

Contact me

+
+
Email:
+
+ + +
+
PGP:
+
E275 C3F0 8765 EBC3 C906 66F4 E918 999B E668 8BD2
+
+ +
+{% endblock %} diff --git a/templates/photography.html b/templates/photography.html new file mode 100644 index 0000000..be3897d --- /dev/null +++ b/templates/photography.html @@ -0,0 +1,20 @@ +{% extends 'layout.html' %} + +{% set active_page = 'photo' %} +{% set title = 'Photography' %} + +{% block body %} +
+

Photography

+
+ {% for photo in photos %} + + + + {% endfor %} +
+
+{% endblock %} diff --git a/templates/view_photo.html b/templates/view_photo.html new file mode 100644 index 0000000..be473d6 --- /dev/null +++ b/templates/view_photo.html @@ -0,0 +1,32 @@ +{% extends 'layout.html' %} + +{% set active_page = 'photo' %} +{% set title = 'Photography' %} + +{% block body %} +
+ + +
+ {% if photo.title %} +

{{ photo.title }}

+ {% endif %} + {% if photo.description %} + {{ photo.html_description|safe }} + {% endif %} + {% if next or prev %} +
+ +
+{% endblock %}