commit bdce0d9fc380420dfd281e1fb8e598718c917baf Author: Flancian <0@flancia.org> Date: Sat Nov 7 15:34:48 2020 +0100 First commit after re-import. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a08dd18 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +*~ +*swp +*swo +__pycache__ +venv diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..22b241c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,29 @@ +# How to Contribute + +We'd love to accept your patches and contributions to this project. There are +just a few small guidelines you need to follow. + +## Contributor License Agreement + +Contributions to this project must be accompanied by a Contributor License +Agreement (CLA). You (or your employer) retain the copyright to your +contribution; this simply gives us permission to use and redistribute your +contributions as part of the project. Head over to + to see your current agreements on file or +to sign a new one. + +You generally only need to submit a CLA once, so if you've already submitted one +(even if it was for a different project), you probably don't need to do it +again. + +## Code reviews + +All submissions, including submissions by project members, require review. We +use GitHub pull requests for this purpose. Consult +[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more +information on using pull requests. + +## Community Guidelines + +This project follows +[Google's Open Source Community Guidelines](https://opensource.google/conduct/). diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100755 index 0000000..b2bea15 --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# To use +This Agora Server is meant to be used in conjunction with an Agora. An Agora is +a collection of digital gardens and other information sources that are assembled +into a distributed knowledge graph. + +For an example Agora, and for more information on the Agora design, please refer to . + +To see the Agora Server in action with the example Agora, please visit +. + +# To develop + +Install OS dependencies: +``` +$ apt-get install python3 python3-venv python3-pip +``` + +Then Flask inside a virtual environment in this directory: +``` +python3 -m venv venv +. venv/bin/activate +pip install -r requirements.txt +``` + +Then run the development server: +``` +./run.sh +``` + +## Optional: my git configuration + +(Tangent) in case this is your first git contribution/you just haven't done it and you find this useful. This is my git config: + +``` +git config --global user.email "example@example.org" +git config --global user.name "Example" +git config --global alias.st status +git config --global alias.ci commit +git config --global alias.co checkout +git config --global alias.f "reset --hard HEAD" +``` + +## About the project +As you might have inferred from the above, this project is based on [Flask](https://flask.palletsprojects.com). ```/app``` hosts the Flask app. In it: + +```/templates``` are Jinja2 templates. +```__init__.py``` has the high level Flask setup. +```agora.py``` does rendering (url maps, views). +```db.py``` has logic to read/process notes. The db is actually the filesystem :) diff --git a/app/__init__.py b/app/__init__.py new file mode 100644 index 0000000..bdcf6fd --- /dev/null +++ b/app/__init__.py @@ -0,0 +1,61 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import bleach +import os +from flask import Flask +from flaskext.markdown import Markdown +from markdown.extensions.wikilinks import WikiLinkExtension + +def wikilink_to_url(label, base, end): + label = label.lower() + label = label.replace(' ', '-') + label = label.replace('\'', '') + label = label.replace(',', '') + url = '/node/' + label + return url + +def create_app(test_config=None): + # create and configure the app + app = Flask(__name__, instance_relative_config=True) + app.config.from_mapping( + SECRET_KEY='dev', + ) + + if test_config is None: + # load the instance config, if it exists, when not testing + app.config.from_pyfile('config.py', silent=True) + else: + # load the test config if passed in + app.config.from_mapping(test_config) + + # ensure the instance folder exists + try: + os.makedirs(app.instance_path) + except OSError: + pass + + # Add blueprints here. + from . import agora + app.register_blueprint(agora.bp) + app.add_url_rule('/', endpoint='index') + + # Jinja2 extensions. + Markdown(app, tab_length=2, extensions=["sane_lists", WikiLinkExtension(build_url=wikilink_to_url)]) + + @app.template_filter('linkify') + def linkify(s): + return bleach.linkify(s) + + return app diff --git a/app/agora.py b/app/agora.py new file mode 100644 index 0000000..b413d1a --- /dev/null +++ b/app/agora.py @@ -0,0 +1,70 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import datetime +from flask import Blueprint, url_for, render_template, current_app, Response, redirect +from markupsafe import escape +from . import db +bp = Blueprint('agora', __name__) + +@bp.route('/') +def index(): + return render_template('index.html', help=url_for('agora.help'), nodes=url_for('agora.nodes'), journals=url_for('agora.journals')) + +@bp.route('/help') +def help(): + current_app.logger.warning('Not implemented.') + return 'If I had implemented help already, here you\'d see documentation on all URL endpoints. For now, please refer to the code.' + +@bp.route('/notes') # alias for now +@bp.route('/nodes') +def nodes(): + return render_template('nodes.html', nodes=db.all_nodes()) + +@bp.route('/journals') +def journals(): + return render_template('nodes.html', nodes=db.all_journals()) + +@bp.route('/today') +def today(): + today = datetime.datetime.now().date() + return redirect("https://anagora.org/node/%s" % today.strftime("%Y-%m-%d")) + +@bp.route('/u/') +@bp.route('/user/') +def user(username): + current_app.logger.warning('Not implemented.') + return 'If I had implemented reading user profiles already, here you would see user named "%s".' % escape(username) + +@bp.route('/garden/') +def garden(garden): + current_app.logger.warning('Not implemented.') + return 'If I had implemented rendering gardens already, here you would see garden named "%s".' % escape(garden) + +@bp.route('/node/') +@bp.route('/wikilink/') # alias for now +def wikilink(node): + return render_template('nodes_rendered.html', wikilink=node, nodes=db.nodes_by_wikilink(node), backlinks=db.nodes_by_outlink(node)) + +@bp.route('/raw/') +def raw(node): + # hack hack + # outlinks + return Response("\n\n".join([str(n.outlinks) for n in db.nodes_by_wikilink(node)]), mimetype="text/plain") + # content + # return Response("\n\n".join([n.content for n in db.nodes_by_wikilink(node)]), mimetype="text/plain") + +@bp.route('/backlinks/') +def backlinks(node): + return render_template('nodes.html', nodes=db.nodes_by_outlink(node)) diff --git a/app/db.py b/app/db.py new file mode 100644 index 0000000..4e02b72 --- /dev/null +++ b/app/db.py @@ -0,0 +1,61 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import glob +import re +import os +from operator import attrgetter + +BASE = '../../agora/' +RE_WIKILINKS = re.compile('\[\[(.*?)\]\]') + +class Node: + def __init__(self, path): + self.dir = path_to_url(path) + self.wikilink = path_to_wikilink(path) + self.url = '/node/' + self.wikilink + with open(path) as f: + self.content = f.read() + self.outlinks = content_to_outlinks(self.content) + +def path_to_url(path): + return os.path.split(path)[0].replace(BASE, '') + +def path_to_wikilink(path): + return os.path.splitext(os.path.basename(path))[0] + +def content_to_outlinks(content): + # hack hack. + match = RE_WIKILINKS.findall(content) + if match: + return [m.lower().replace(' ', '-').replace('\'', '').replace(',', '') for m in match] + else: + return [] + +def all_nodes(): + l = sorted([f for f in glob.glob(BASE + '**/*.md', recursive=True)]) + return [Node(f) for f in l] + +def all_journals(): + # hack hack. + l = sorted([f for f in glob.glob(BASE + '**/????-??-??.md', recursive=True)]) + return sorted([Node(f) for f in l], key=attrgetter('wikilink'), reverse=True) + +def nodes_by_wikilink(wikilink): + nodes = [node for node in all_nodes() if node.wikilink == wikilink] + return nodes + +def nodes_by_outlink(wikilink): + nodes = [node for node in all_nodes() if wikilink in node.outlinks] + return nodes diff --git a/app/static/css/screen-dark.css b/app/static/css/screen-dark.css new file mode 100644 index 0000000..f331dff --- /dev/null +++ b/app/static/css/screen-dark.css @@ -0,0 +1,21 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +a { color: rgb(140, 140, 250); }::-webkit-scrollbar-track-piece { background-color: rgba(255, 255, 255, 0.2) !important; }::-webkit-scrollbar-track { background-color: rgba(255, 255, 255, 0.3) !important; }::-webkit-scrollbar-thumb { background-color: rgba(255, 255, 255, 0.5) !important; }embed[type="application/pdf"] { filter: invert(90%); } + html { color: rgb(191, 191, 191); background: rgb(31, 31, 31) !important; }body { background-color: rgb(31, 31, 31); background-image: none !important; }input, select, textarea, button { color: rgb(191, 191, 191); background-color: rgb(31, 31, 31); }font { color: rgb(191, 191, 191); } + html { filter: contrast(100%) brightness(100%) saturate(100%); }.NIGHTEYE_Filter { width: 100%; height: 100%; position: fixed; left: 0px; top: 0px; pointer-events: none; z-index: 2147483647; }.NIGHTEYE_YellowFilter { background: rgba(255, 255, 0, 0.15); opacity: 0; }.NIGHTEYE_BlueFilter { background: rgba(0, 0, 255, 0.15); opacity: 0; }.NIGHTEYE_DimFilter { background: rgba(0, 0, 0, 0.5); opacity: 0; }.NIGHTEYE_TransformZ { transform: translateZ(0px); } + @font-face { font-family: Merriweather; font-style: italic; font-weight: 300; src: local("Merriweather Light Italic"), local("Merriweather-LightItalic"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR7lXff1jvzRPA.woff2") format("woff2"); unicode-range: U+460-52F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }@font-face { font-family: Merriweather; font-style: italic; font-weight: 300; src: local("Merriweather Light Italic"), local("Merriweather-LightItalic"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR7lXff8jvzRPA.woff2") format("woff2"); unicode-range: U+400-45F, U+490-491, U+4B0-4B1, U+2116; }@font-face { font-family: Merriweather; font-style: italic; font-weight: 300; src: local("Merriweather Light Italic"), local("Merriweather-LightItalic"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR7lXff3jvzRPA.woff2") format("woff2"); unicode-range: U+102-103, U+110-111, U+128-129, U+168-169, U+1A0-1A1, U+1AF-1B0, U+1EA0-1EF9, U+20AB; }@font-face { font-family: Merriweather; font-style: italic; font-weight: 300; src: local("Merriweather Light Italic"), local("Merriweather-LightItalic"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR7lXff2jvzRPA.woff2") format("woff2"); unicode-range: U+100-24F, U+259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; }@font-face { font-family: Merriweather; font-style: italic; font-weight: 300; src: local("Merriweather Light Italic"), local("Merriweather-LightItalic"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR7lXff4jvw.woff2") format("woff2"); unicode-range: U+0-FF, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }@font-face { font-family: Merriweather; font-style: italic; font-weight: 700; src: local("Merriweather Bold Italic"), local("Merriweather-BoldItalic"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR71Wvf1jvzRPA.woff2") format("woff2"); unicode-range: U+460-52F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }@font-face { font-family: Merriweather; font-style: italic; font-weight: 700; src: local("Merriweather Bold Italic"), local("Merriweather-BoldItalic"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR71Wvf8jvzRPA.woff2") format("woff2"); unicode-range: U+400-45F, U+490-491, U+4B0-4B1, U+2116; }@font-face { font-family: Merriweather; font-style: italic; font-weight: 700; src: local("Merriweather Bold Italic"), local("Merriweather-BoldItalic"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR71Wvf3jvzRPA.woff2") format("woff2"); unicode-range: U+102-103, U+110-111, U+128-129, U+168-169, U+1A0-1A1, U+1AF-1B0, U+1EA0-1EF9, U+20AB; }@font-face { font-family: Merriweather; font-style: italic; font-weight: 700; src: local("Merriweather Bold Italic"), local("Merriweather-BoldItalic"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR71Wvf2jvzRPA.woff2") format("woff2"); unicode-range: U+100-24F, U+259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; }@font-face { font-family: Merriweather; font-style: italic; font-weight: 700; src: local("Merriweather Bold Italic"), local("Merriweather-BoldItalic"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4l0qyriQwlOrhSvowK_l5-eR71Wvf4jvw.woff2") format("woff2"); unicode-range: U+0-FF, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }@font-face { font-family: Merriweather; font-style: normal; font-weight: 300; src: local("Merriweather Light"), local("Merriweather-Light"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l521wRZVcf6lvg.woff2") format("woff2"); unicode-range: U+460-52F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }@font-face { font-family: Merriweather; font-style: normal; font-weight: 300; src: local("Merriweather Light"), local("Merriweather-Light"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l521wRZXMf6lvg.woff2") format("woff2"); unicode-range: U+400-45F, U+490-491, U+4B0-4B1, U+2116; }@font-face { font-family: Merriweather; font-style: normal; font-weight: 300; src: local("Merriweather Light"), local("Merriweather-Light"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l521wRZV8f6lvg.woff2") format("woff2"); unicode-range: U+102-103, U+110-111, U+128-129, U+168-169, U+1A0-1A1, U+1AF-1B0, U+1EA0-1EF9, U+20AB; }@font-face { font-family: Merriweather; font-style: normal; font-weight: 300; src: local("Merriweather Light"), local("Merriweather-Light"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l521wRZVsf6lvg.woff2") format("woff2"); unicode-range: U+100-24F, U+259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; }@font-face { font-family: Merriweather; font-style: normal; font-weight: 300; src: local("Merriweather Light"), local("Merriweather-Light"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l521wRZWMf6.woff2") format("woff2"); unicode-range: U+0-FF, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }@font-face { font-family: Merriweather; font-style: normal; font-weight: 700; src: local("Merriweather Bold"), local("Merriweather-Bold"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l52xwNZVcf6lvg.woff2") format("woff2"); unicode-range: U+460-52F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }@font-face { font-family: Merriweather; font-style: normal; font-weight: 700; src: local("Merriweather Bold"), local("Merriweather-Bold"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l52xwNZXMf6lvg.woff2") format("woff2"); unicode-range: U+400-45F, U+490-491, U+4B0-4B1, U+2116; }@font-face { font-family: Merriweather; font-style: normal; font-weight: 700; src: local("Merriweather Bold"), local("Merriweather-Bold"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l52xwNZV8f6lvg.woff2") format("woff2"); unicode-range: U+102-103, U+110-111, U+128-129, U+168-169, U+1A0-1A1, U+1AF-1B0, U+1EA0-1EF9, U+20AB; }@font-face { font-family: Merriweather; font-style: normal; font-weight: 700; src: local("Merriweather Bold"), local("Merriweather-Bold"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l52xwNZVsf6lvg.woff2") format("woff2"); unicode-range: U+100-24F, U+259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; }@font-face { font-family: Merriweather; font-style: normal; font-weight: 700; src: local("Merriweather Bold"), local("Merriweather-Bold"), url("https://fonts.gstatic.com/s/merriweather/v22/u-4n0qyriQwlOrhSvowK_l52xwNZWMf6.woff2") format("woff2"); unicode-range: U+0-FF, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 400; src: local("Open Sans Regular"), local("OpenSans-Regular"), url("https://fonts.gstatic.com/s/opensans/v18/mem8YaGs126MiZpBA-UFWJ0bbck.woff2") format("woff2"); unicode-range: U+460-52F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 400; src: local("Open Sans Regular"), local("OpenSans-Regular"), url("https://fonts.gstatic.com/s/opensans/v18/mem8YaGs126MiZpBA-UFUZ0bbck.woff2") format("woff2"); unicode-range: U+400-45F, U+490-491, U+4B0-4B1, U+2116; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 400; src: local("Open Sans Regular"), local("OpenSans-Regular"), url("https://fonts.gstatic.com/s/opensans/v18/mem8YaGs126MiZpBA-UFWZ0bbck.woff2") format("woff2"); unicode-range: U+1F00-1FFF; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 400; src: local("Open Sans Regular"), local("OpenSans-Regular"), url("https://fonts.gstatic.com/s/opensans/v18/mem8YaGs126MiZpBA-UFVp0bbck.woff2") format("woff2"); unicode-range: U+370-3FF; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 400; src: local("Open Sans Regular"), local("OpenSans-Regular"), url("https://fonts.gstatic.com/s/opensans/v18/mem8YaGs126MiZpBA-UFWp0bbck.woff2") format("woff2"); unicode-range: U+102-103, U+110-111, U+128-129, U+168-169, U+1A0-1A1, U+1AF-1B0, U+1EA0-1EF9, U+20AB; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 400; src: local("Open Sans Regular"), local("OpenSans-Regular"), url("https://fonts.gstatic.com/s/opensans/v18/mem8YaGs126MiZpBA-UFW50bbck.woff2") format("woff2"); unicode-range: U+100-24F, U+259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 400; src: local("Open Sans Regular"), local("OpenSans-Regular"), url("https://fonts.gstatic.com/s/opensans/v18/mem8YaGs126MiZpBA-UFVZ0b.woff2") format("woff2"); unicode-range: U+0-FF, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 700; src: local("Open Sans Bold"), local("OpenSans-Bold"), url("https://fonts.gstatic.com/s/opensans/v18/mem5YaGs126MiZpBA-UN7rgOX-hpOqc.woff2") format("woff2"); unicode-range: U+460-52F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 700; src: local("Open Sans Bold"), local("OpenSans-Bold"), url("https://fonts.gstatic.com/s/opensans/v18/mem5YaGs126MiZpBA-UN7rgOVuhpOqc.woff2") format("woff2"); unicode-range: U+400-45F, U+490-491, U+4B0-4B1, U+2116; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 700; src: local("Open Sans Bold"), local("OpenSans-Bold"), url("https://fonts.gstatic.com/s/opensans/v18/mem5YaGs126MiZpBA-UN7rgOXuhpOqc.woff2") format("woff2"); unicode-range: U+1F00-1FFF; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 700; src: local("Open Sans Bold"), local("OpenSans-Bold"), url("https://fonts.gstatic.com/s/opensans/v18/mem5YaGs126MiZpBA-UN7rgOUehpOqc.woff2") format("woff2"); unicode-range: U+370-3FF; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 700; src: local("Open Sans Bold"), local("OpenSans-Bold"), url("https://fonts.gstatic.com/s/opensans/v18/mem5YaGs126MiZpBA-UN7rgOXehpOqc.woff2") format("woff2"); unicode-range: U+102-103, U+110-111, U+128-129, U+168-169, U+1A0-1A1, U+1AF-1B0, U+1EA0-1EF9, U+20AB; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 700; src: local("Open Sans Bold"), local("OpenSans-Bold"), url("https://fonts.gstatic.com/s/opensans/v18/mem5YaGs126MiZpBA-UN7rgOXOhpOqc.woff2") format("woff2"); unicode-range: U+100-24F, U+259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; }@font-face { font-family: "Open Sans"; font-style: normal; font-weight: 700; src: local("Open Sans Bold"), local("OpenSans-Bold"), url("https://fonts.gstatic.com/s/opensans/v18/mem5YaGs126MiZpBA-UN7rgOUuhp.woff2") format("woff2"); unicode-range: U+0-FF, U+131, U+152-153, U+2BB-2BC, U+2C6, U+2DA, U+2DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; } + body { font-family: sans-serif; background: rgb(43, 43, 43); }a, h1, h2 { color: rgb(159, 198, 224); }h1, h2 { font-family: Georgia, serif; margin: 0px; }h1 { border-bottom: 2px solid rgb(43, 43, 43); }h2 { font-size: 1.2em; }.page { margin: 2em auto; width: 35em; border: 5px solid rgb(77, 77, 77); padding: 0.8em; background: rgb(28, 28, 33); }.entries { list-style: none; margin: 0px; padding: 0px; }.entries li { margin: 0.8em 1.2em; }.entries li h2 { margin-left: -1em; }.add-entry { font-size: 0.9em; border-bottom: 1px solid rgb(77, 77, 77); }.add-entry dl { font-weight: bold; }.metanav { text-align: right; font-size: 0.8em; padding: 0.3em; margin-bottom: 1em; background: rgb(31, 31, 31); }.flash { background: rgb(22, 62, 90); padding: 0.5em; border: 1px solid rgb(42, 88, 122); }.error { background: rgb(78, 29, 29); padding: 0.5em; }.wikilink::before { content: "[["; }.wikilink::after { content: "]]"; } \ No newline at end of file diff --git a/app/static/css/screen.css b/app/static/css/screen.css new file mode 100644 index 0000000..27722b4 --- /dev/null +++ b/app/static/css/screen.css @@ -0,0 +1,42 @@ +/** + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +body { font-family: sans-serif; background: #eee; } +a, h1, h2 { color: #377ba8; } +h1, h2 { font-family: 'Georgia', serif; margin: 0; } +h1 { border-bottom: 2px solid #eee; } +h2 { font-size: 1.2em; } + +.page { margin: 2em auto; width: 35em; border: 5px solid #ccc; + padding: 0.8em; background: white; } +.entries { list-style: none; margin: 0; padding: 0; } +.entries li { margin: 0.8em 1.2em; } +.entries li h2 { margin-left: -1em; } +.add-entry { font-size: 0.9em; border-bottom: 1px solid #ccc; } +.add-entry dl { font-weight: bold; } +.metanav { text-align: right; font-size: 0.8em; padding: 0.3em; + margin-bottom: 1em; background: #fafafa; } +.flash { background: #cee5F5; padding: 0.5em; + border: 1px solid #aacbe2; } +.error { background: #f0d6d6; padding: 0.5em; } + +.wikilink:before { + content: "[[" +} + +.wikilink:after { + content: "]]" +} diff --git a/app/static/img/agora.png b/app/static/img/agora.png new file mode 100644 index 0000000..4a43460 Binary files /dev/null and b/app/static/img/agora.png differ diff --git a/app/templates/base.html b/app/templates/base.html new file mode 100644 index 0000000..4dc373f --- /dev/null +++ b/app/templates/base.html @@ -0,0 +1,40 @@ + + + + + + + Agora + + + + + + + + | + + + /nodes | /journals + + + help + +
+ {% block content %} + {% endblock %} + diff --git a/app/templates/index.html b/app/templates/index.html new file mode 100644 index 0000000..bc4a903 --- /dev/null +++ b/app/templates/index.html @@ -0,0 +1,33 @@ + + +{% extends "base.html" %} +{% block content %} +Ahoy, matey!
+
+This is the Agora v0.5a.
+
+This site is very much under construction, but feel free to look around: +
    +
  • {{nodes}} lists all nodes in this Agora; currently these are mostly notes as volunteered by users.
  • +
  • {{journals}} displays all journal entries.
  • +
+ +The [[wikilink]] is the heart of the Agora. /node/foo and /wikilink/foo will both render every node that responds to wikilink [[foo]]. For example: [[agora-help]]. + +

+For more information, please visit the Agora repository. +{% endblock %} diff --git a/app/templates/nodes.html b/app/templates/nodes.html new file mode 100644 index 0000000..a457811 --- /dev/null +++ b/app/templates/nodes.html @@ -0,0 +1,23 @@ + + +{% extends "base.html" %} +

Nodes

+{% block content %} +{% for node in nodes %} +{{ node.dir }}: {{node.wikilink}}
+{% endfor %} +{% endblock %} diff --git a/app/templates/nodes_rendered.html b/app/templates/nodes_rendered.html new file mode 100644 index 0000000..23c0049 --- /dev/null +++ b/app/templates/nodes_rendered.html @@ -0,0 +1,37 @@ + + +{% extends "base.html" %} +{% block content %} +{% if not nodes %} +No nodes found for '{{wikilink}}'. +

+If you contributed a node named '{{wikilink}}' to the Agora, here is where your node would be :) +

+Go back to /nodes? +{% endif %} + +{% for node in nodes %} +{{ node.dir }}: {{node.wikilink}}
+{{ node.content|markdown|linkify|safe }} +
+{% endfor %} +Backlinks:
+{% for node in backlinks %} +{{ node.dir }}: {{node.wikilink}}
+{% endfor %} + +{% endblock %} diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..fc57b3d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,17 @@ +bleach==3.2.1 +click==7.1.2 +Flask==1.1.2 +Flask-Markdown==0.3 +importlib-metadata==2.0.0 +itsdangerous==1.1.0 +Jinja2==2.11.2 +Markdown==3.3.3 +MarkupSafe==1.1.1 +packaging==20.4 +pkg-resources==0.0.0 +pyparsing==2.4.7 +six==1.15.0 +uWSGI==2.0.19.1 +webencodings==0.5.1 +Werkzeug==1.0.1 +zipp==3.4.0 diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..c114714 --- /dev/null +++ b/run.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +export FLASK_APP=app +export FLASK_ENV=development +flask run diff --git a/uwsgi.ini b/uwsgi.ini new file mode 100644 index 0000000..c814033 --- /dev/null +++ b/uwsgi.ini @@ -0,0 +1,8 @@ +[uwsgi] +module = app:create_app() +master = true +processes = 5 +socket = /tmp/uwsgi.sock +chmod-socket = 666 +vacuum = true +die-on-term = true diff --git a/uwsgi.sh b/uwsgi.sh new file mode 100755 index 0000000..20a7be3 --- /dev/null +++ b/uwsgi.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +uwsgi --socket 0.0.0.0:5000 --protocol=http --module 'app:create_app()'