Add basic image support.
This commit is contained in:
parent
cfd33a3f58
commit
f02373601b
12
app/agora.py
12
app/agora.py
|
@ -188,14 +188,10 @@ def asset(user, asset):
|
||||||
path = '/'.join(["garden", user, 'assets', asset])
|
path = '/'.join(["garden", user, 'assets', asset])
|
||||||
return current_app.send_static_file(path)
|
return current_app.send_static_file(path)
|
||||||
|
|
||||||
@bp.route('/raw/<node>')
|
@bp.route('/raw/<path:subnode>')
|
||||||
def raw(node):
|
def raw(subnode):
|
||||||
# Currently unused.
|
s = db.subnode_by_uri(subnode)
|
||||||
# hack hack
|
return Response(s.content, mimetype=s.mediatype)
|
||||||
# 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/<node>')
|
@bp.route('/backlinks/<node>')
|
||||||
def backlinks(node):
|
def backlinks(node):
|
||||||
|
|
40
app/db.py
40
app/db.py
|
@ -88,8 +88,14 @@ class Graph:
|
||||||
# does this belong here?
|
# does this belong here?
|
||||||
@cachetools.func.ttl_cache(maxsize=1, ttl=20)
|
@cachetools.func.ttl_cache(maxsize=1, ttl=20)
|
||||||
def subnodes(self, sort=lambda x: x.uri.lower()):
|
def subnodes(self, sort=lambda x: x.uri.lower()):
|
||||||
|
# Markdown.
|
||||||
subnodes = [Subnode(f) for f in glob.glob(os.path.join(config.AGORA_PATH, '**/*.md'), recursive=True)]
|
subnodes = [Subnode(f) for f in glob.glob(os.path.join(config.AGORA_PATH, '**/*.md'), recursive=True)]
|
||||||
|
# Org mode.
|
||||||
subnodes.extend([Subnode(f) for f in glob.glob(os.path.join(config.AGORA_PATH, '**/*.org'), recursive=True)])
|
subnodes.extend([Subnode(f) for f in glob.glob(os.path.join(config.AGORA_PATH, '**/*.org'), recursive=True)])
|
||||||
|
# Image formats.
|
||||||
|
subnodes.extend([Subnode(f, mediatype='image/jpg') for f in glob.glob(os.path.join(config.AGORA_PATH, '**/*.jpg'), recursive=True)])
|
||||||
|
subnodes.extend([Subnode(f, mediatype='image/png') for f in glob.glob(os.path.join(config.AGORA_PATH, '**/*.png'), recursive=True)])
|
||||||
|
subnodes.extend([Subnode(f, mediatype='image/gif') for f in glob.glob(os.path.join(config.AGORA_PATH, '**/*.gif'), recursive=True)])
|
||||||
if sort:
|
if sort:
|
||||||
return sorted(subnodes, key=sort)
|
return sorted(subnodes, key=sort)
|
||||||
else:
|
else:
|
||||||
|
@ -188,7 +194,7 @@ class Subnode:
|
||||||
"""A subnode is a note or media resource volunteered by a user of the Agora.
|
"""A subnode is a note or media resource volunteered by a user of the Agora.
|
||||||
It maps to a particular file in the Agora repository, stored (relative to
|
It maps to a particular file in the Agora repository, stored (relative to
|
||||||
the Agora root) in the attribute 'uri'."""
|
the Agora root) in the attribute 'uri'."""
|
||||||
def __init__(self, path):
|
def __init__(self, path, mediatype='text/plain'):
|
||||||
# Use a subnode's URI as its identifier.
|
# Use a subnode's URI as its identifier.
|
||||||
self.uri = path_to_uri(path)
|
self.uri = path_to_uri(path)
|
||||||
self.url = '/subnode/' + path_to_uri(path)
|
self.url = '/subnode/' + path_to_uri(path)
|
||||||
|
@ -196,10 +202,20 @@ class Subnode:
|
||||||
# i.e. if two users contribute subnodes titled [[foo]], they both show up when querying node [[foo]].
|
# i.e. if two users contribute subnodes titled [[foo]], they both show up when querying node [[foo]].
|
||||||
self.wikilink = util.canonical_wikilink(path_to_wikilink(path))
|
self.wikilink = util.canonical_wikilink(path_to_wikilink(path))
|
||||||
self.user = path_to_user(path)
|
self.user = path_to_user(path)
|
||||||
with open(path) as f:
|
self.mediatype = mediatype
|
||||||
self.content = f.read()
|
|
||||||
|
if self.mediatype == 'text/plain':
|
||||||
|
with open(path) as f:
|
||||||
|
self.content = f.read()
|
||||||
|
self.forward_links = content_to_forward_links(self.content)
|
||||||
|
elif self.mediatype.startswith('image'):
|
||||||
|
with open(path, 'rb') as f:
|
||||||
|
self.content = f.read()
|
||||||
|
self.forward_links = []
|
||||||
|
else:
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
self.mtime = os.path.getmtime(path)
|
self.mtime = os.path.getmtime(path)
|
||||||
self.forward_links = content_to_forward_links(self.content)
|
|
||||||
self.node = self.wikilink
|
self.node = self.wikilink
|
||||||
# Initiate node for wikilink if this is the first subnode, append otherwise.
|
# Initiate node for wikilink if this is the first subnode, append otherwise.
|
||||||
# G.addsubnode(self)
|
# G.addsubnode(self)
|
||||||
|
@ -220,13 +236,19 @@ class Subnode:
|
||||||
return 100-fuzz.ratio(self.wikilink, other.wikilink)
|
return 100-fuzz.ratio(self.wikilink, other.wikilink)
|
||||||
|
|
||||||
def render(self):
|
def render(self):
|
||||||
# hack hack
|
if self.mediatype != 'text/plain':
|
||||||
|
# hack hack
|
||||||
|
#return 'This is a subnode of type {}. You can <a href="/raw/{}">view</a> it.'.format(self.mediatype, self.uri)
|
||||||
|
return '<br /><img src="/raw/{}" style="display: block; margin-left: auto; margin-right: auto; width: 50%" /> <br />'.format(self.uri)
|
||||||
if self.uri.endswith('md') or self.uri.endswith('MD'):
|
if self.uri.endswith('md') or self.uri.endswith('MD'):
|
||||||
content = render.markdown(self.content)
|
content = render.markdown(self.content)
|
||||||
if self.uri.endswith('org') or self.uri.endswith('ORG'):
|
if self.uri.endswith('org') or self.uri.endswith('ORG'):
|
||||||
content = render.orgmode(self.content)
|
content = render.orgmode(self.content)
|
||||||
return render.postprocess(content)
|
return render.postprocess(content)
|
||||||
|
|
||||||
|
def raw(self):
|
||||||
|
return content
|
||||||
|
|
||||||
def go(self):
|
def go(self):
|
||||||
"""
|
"""
|
||||||
returns a set of go links contained in this subnode
|
returns a set of go links contained in this subnode
|
||||||
|
@ -274,6 +296,8 @@ class Subnode:
|
||||||
|
|
||||||
def subnode_to_actions(subnode, action):
|
def subnode_to_actions(subnode, action):
|
||||||
# hack hack.
|
# hack hack.
|
||||||
|
if subnode.mediatype != 'text/plain':
|
||||||
|
return []
|
||||||
action_regex ='\[\[' + action + '\]\] (.*?)$'
|
action_regex ='\[\[' + action + '\]\] (.*?)$'
|
||||||
content = subnode.content
|
content = subnode.content
|
||||||
actions = []
|
actions = []
|
||||||
|
@ -352,11 +376,11 @@ def subnodes_by_wikilink(wikilink, fuzzy_matching=True):
|
||||||
return subnodes
|
return subnodes
|
||||||
|
|
||||||
def search_subnodes(query):
|
def search_subnodes(query):
|
||||||
subnodes = [subnode for subnode in G.subnodes() if re.search(query, subnode.content, re.IGNORECASE)]
|
subnodes = [subnode for subnode in G.subnodes() if subnode.mediatype == 'text/plain' and re.search(query, subnode.content, re.IGNORECASE)]
|
||||||
return subnodes
|
return subnodes
|
||||||
|
|
||||||
def search_subnodes_by_user(query, user):
|
def search_subnodes_by_user(query, user):
|
||||||
subnodes = [subnode for subnode in G.subnodes() if re.search(query, subnode.content, re.IGNORECASE) and subnode.user == user]
|
subnodes = [subnode for subnode in G.subnodes() if subnode.mediatype == 'text/plain' and subnode.user == user and re.search(query, subnode.content, re.IGNORECASE)]
|
||||||
return subnodes
|
return subnodes
|
||||||
|
|
||||||
def subnodes_by_user(user):
|
def subnodes_by_user(user):
|
||||||
|
@ -366,7 +390,7 @@ def subnodes_by_user(user):
|
||||||
def user_readmes(user):
|
def user_readmes(user):
|
||||||
# hack hack
|
# hack hack
|
||||||
# fix duplication.
|
# fix duplication.
|
||||||
subnodes = [subnode for subnode in G.subnodes() if subnode.user == user and re.search('readme', subnode.wikilink, re.IGNORECASE)]
|
subnodes = [subnode for subnode in G.subnodes() if subnode.mediatype == 'text/plain' and subnode.user == user and re.search('readme', subnode.wikilink, re.IGNORECASE)]
|
||||||
return subnodes
|
return subnodes
|
||||||
|
|
||||||
def subnode_by_uri(uri):
|
def subnode_by_uri(uri):
|
||||||
|
|
|
@ -80,19 +80,24 @@ nav { font-family: sans-serif}
|
||||||
.subnode-header {
|
.subnode-header {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.subnode-footer {
|
.subnode-footer {
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
background: #2f2f2f;
|
||||||
|
color: #cfcfcf;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.subnode-id {
|
.subnode-id {
|
||||||
float: left;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.subnode-links {
|
.subnode-links {
|
||||||
float: right;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.links {
|
.links {
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
<meta name="HandheldFriendly" content="True" />
|
<meta name="HandheldFriendly" content="True" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Merriweather:300,700,700italic,300italic|Open+Sans:700,400" />
|
<link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Merriweather:300,700,700italic,300italic|Open+Sans:700,400" />
|
||||||
<link rel="stylesheet" type="text/css" href="{{ url_for('static',filename='css/screen-dark.css')}}" id="theme-link" />
|
<link rel="stylesheet" type="text/css" href="{{ url_for('static',filename='css/screen-dark.css')}}?uncached2" id="theme-link" />
|
||||||
<script type=text/javascript src="{{url_for('static', filename='js/main.js')}}"></script>
|
<script type=text/javascript src="{{url_for('static', filename='js/main.js')}}"></script>
|
||||||
<!-- considering this way of integrating hypothesis, although I like "standard" (sidebar) mode.
|
<!-- considering this way of integrating hypothesis, although I like "standard" (sidebar) mode.
|
||||||
<script type="application/json" class="js-hypothesis-config">
|
<script type="application/json" class="js-hypothesis-config">
|
||||||
|
|
|
@ -34,10 +34,9 @@ Try listing all <a href="/nodes">nodes</a> or perhaps <a href="/search">search</
|
||||||
{% for subnode in node.subnodes %}
|
{% for subnode in node.subnodes %}
|
||||||
<div class="subnode">
|
<div class="subnode">
|
||||||
<div class="subnode-header">
|
<div class="subnode-header">
|
||||||
<span class="subnode-id"><strong>Subnode</strong> <a href="/@{{subnode.user}}/{{node.uri}}">[[@{{subnode.user}}/{{node.uri}}]]</a></span>
|
<span class="subnode-id"><strong>Subnode</strong> <a href="/@{{subnode.user}}/{{node.uri}}">[[@{{subnode.user}}/{{node.uri}}]]</a></span><br />
|
||||||
<span class="subnode-links">by <a href="/@{{subnode.user}}">@{{subnode.user}}</a></span>
|
<span class="subnode-links"><strong>from</strong> <a href="/raw/{{subnode.uri}}">{{subnode.uri}}</a> by <a href="/@{{subnode.user}}">@{{subnode.user}}</a></span>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
|
||||||
{{ subnode.render()|linkify|safe }}
|
{{ subnode.render()|linkify|safe }}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
@ -51,10 +50,9 @@ Try listing all <a href="/nodes">nodes</a> or perhaps <a href="/search">search</
|
||||||
{% for subnode in node.subnodes %}
|
{% for subnode in node.subnodes %}
|
||||||
<div class="subnode">
|
<div class="subnode">
|
||||||
<div class="subnode-header">
|
<div class="subnode-header">
|
||||||
<span class="subnode-id"><strong>Subnode</strong> <a href="/@{{subnode.user}}/{{node.uri}}">[[@{{subnode.user}}/{{node.uri}}]]</a></span>
|
<span class="subnode-id"><strong>Subnode</strong> <a href="/@{{subnode.user}}/{{node.uri}}">[[@{{subnode.user}}/{{node.uri}}]]</a></span><br />
|
||||||
<span class="subnode-links">by <a href="/@{{subnode.user}}">@{{subnode.user}}</a></span>
|
<span class="subnode-links"><strong>from</strong> <a href="/raw/{{subnode.uri}}">{{subnode.uri}}</a> by <a href="/@{{subnode.user}}">@{{subnode.user}}</a></span>
|
||||||
</div>
|
</div>
|
||||||
<br />
|
|
||||||
{{ subnode.render()|linkify|safe }}
|
{{ subnode.render()|linkify|safe }}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -30,9 +30,10 @@ Try going up to node <a href="/node/{{node.uri}}">[[{{node.uri}}]]</a> or perhap
|
||||||
{% for subnode in node.subnodes %}
|
{% for subnode in node.subnodes %}
|
||||||
<div class="subnode">
|
<div class="subnode">
|
||||||
<div class="subnode-header">
|
<div class="subnode-header">
|
||||||
<span><strong>Subnode</strong> <a href="/@{{subnode.user}}/{{node.uri}}">[[@{{subnode.user}}/{{node.uri}}]]</a></span> in <strong>node</strong> <a href="/node/{{subnode.wikilink}}">[[{{subnode.wikilink}}]]</a></span>
|
|
||||||
<span class="subnode-links">by <a href="/@{{subnode.user}}">@{{subnode.user}}</a></span>
|
|
||||||
<br />
|
<span><strong>Subnode</strong> <a href="/@{{subnode.user}}/{{node.uri}}">[[@{{subnode.user}}/{{node.uri}}]]</a></span> in <strong>node</strong> <a href="/node/{{subnode.wikilink}}">[[{{subnode.wikilink}}]]</a></span><br />
|
||||||
|
<span class="subnode-links"><strong>from</strong> <a href="/raw/{{subnode.uri}}">{{subnode.uri}}</a> by <a href="/@{{subnode.user}}">@{{subnode.user}}</a></span>
|
||||||
</div>
|
</div>
|
||||||
{{ subnode.render()|linkify|safe }}
|
{{ subnode.render()|linkify|safe }}
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue