manetta
3 years ago
commit
266d5fc1cd
18 changed files with 62777 additions and 0 deletions
@ -0,0 +1,9 @@ |
|||||
|
default: run |
||||
|
|
||||
|
setup: |
||||
|
@python3 -m venv .venv |
||||
|
@.venv/bin/pip install -r requirements.txt |
||||
|
|
||||
|
run: |
||||
|
@.venv/bin/python feedmode.py |
||||
|
|
@ -0,0 +1,12 @@ |
|||||
|
# feedmode |
||||
|
|
||||
|
Multifeeder/RSS feeds → selection + CSS + template → PDF (very beta) |
||||
|
|
||||
|
## Use feedmode locally |
||||
|
|
||||
|
`make setup` (sets up a virtual environment and install the requirements) |
||||
|
|
||||
|
`make run` (runs the Flask application) |
||||
|
|
||||
|
Open the application at <http://localhost:5001>. |
||||
|
|
@ -0,0 +1,4 @@ |
|||||
|
class Config(object): |
||||
|
PORTNUMBER = 5001 |
||||
|
PAD_API_URL = 'https://pad.vvvvvvaria.org/api/1.2.15/' |
||||
|
PAD_API_KEY = '<insert API key here>' |
@ -0,0 +1,138 @@ |
|||||
|
import flask |
||||
|
from flask import request, redirect |
||||
|
from urllib.request import urlopen |
||||
|
from urllib.parse import urlencode |
||||
|
import json |
||||
|
import os |
||||
|
import pypandoc |
||||
|
from jinja2 import Template |
||||
|
|
||||
|
APP = flask.Flask(__name__) |
||||
|
APP.config.from_object("config.Config") |
||||
|
|
||||
|
# --- |
||||
|
|
||||
|
def get_pad_content(pad): |
||||
|
arguments = { |
||||
|
'padID' : pad, |
||||
|
'apikey' : APP.config['PAD_API_KEY'] |
||||
|
} |
||||
|
api_call = 'getText' |
||||
|
response = json.load(urlopen(f"{ APP.config['PAD_API_URL'] }{ api_call }", data=urlencode(arguments).encode())) |
||||
|
content = response['data']['text'] |
||||
|
|
||||
|
return content |
||||
|
|
||||
|
def update_pad_contents(): |
||||
|
# download the stylesheet + template pad |
||||
|
css = get_pad_content('feedmode.css') |
||||
|
template = get_pad_content('feedmode.template') |
||||
|
# !!! this breaks the whole idea that this application can be shared by multiple projects at the same time |
||||
|
# !!! but py_pandoc needs to run with files........ hmmm |
||||
|
with open('templates/pandoc-template.html', 'w') as f: |
||||
|
f.write(template) |
||||
|
|
||||
|
return css, template |
||||
|
|
||||
|
def get_multifeeder_feed(query): |
||||
|
feed_json = json.load(urlopen(query)) |
||||
|
multifeeder_template = open('templates/multifeeder-template.html').read() |
||||
|
jinja_template = Template(multifeeder_template) |
||||
|
feed_html = jinja_template.render(response=feed_json) |
||||
|
|
||||
|
return feed_html, feed_json |
||||
|
|
||||
|
def load_query(): |
||||
|
query = open('static/query.txt').read().strip() # !!! needs fixing |
||||
|
|
||||
|
return query |
||||
|
|
||||
|
# --- |
||||
|
|
||||
|
@APP.route('/', methods=['GET']) |
||||
|
def index(): |
||||
|
return redirect("/preview/", code=302) |
||||
|
|
||||
|
@APP.route('/update/', methods=['POST']) |
||||
|
def update(): |
||||
|
query = request.form['query'] |
||||
|
with open('static/query.txt', 'w') as out: |
||||
|
out.write(query) |
||||
|
|
||||
|
return redirect("/select/", code=302) |
||||
|
|
||||
|
@APP.route('/select/', methods=['GET']) |
||||
|
def select(): |
||||
|
# get feed contents |
||||
|
# render selection template with checkboxes |
||||
|
query = load_query() |
||||
|
x, feed_json = get_multifeeder_feed(query) |
||||
|
|
||||
|
return flask.render_template('selection-template.html', feed=feed_json, query=query) |
||||
|
|
||||
|
@APP.route('/preview/', methods=['GET']) |
||||
|
def preview(): |
||||
|
# update pad contents |
||||
|
x, template_content = update_pad_contents() |
||||
|
# get feed contents |
||||
|
query = load_query() |
||||
|
feed_html, x = get_multifeeder_feed(query) |
||||
|
# render multifeeder feed in template |
||||
|
template = Template(template_content) |
||||
|
html = template.render(feed=feed_html, mode="screen") |
||||
|
|
||||
|
return flask.render_template('html.html', html=html, query=query) |
||||
|
|
||||
|
@APP.route('/stylesheet/', methods=['GET']) |
||||
|
def stylesheet(): |
||||
|
ext = '.css' |
||||
|
query = load_query() |
||||
|
|
||||
|
return flask.render_template('pad.html', name="feedmode", ext=ext, query=query) |
||||
|
|
||||
|
@APP.route('/template/', methods=['GET']) |
||||
|
def template(): |
||||
|
ext = '.template' |
||||
|
query = load_query() |
||||
|
|
||||
|
return flask.render_template('pad.html', name="feedmode", ext=ext, query=query) |
||||
|
|
||||
|
@APP.route('/pdf/') |
||||
|
def pdf(): |
||||
|
query = load_query() |
||||
|
|
||||
|
return flask.render_template('pdf.html', query=query) |
||||
|
|
||||
|
# ////////////// |
||||
|
# rendered resources (not saved as a file on the server) |
||||
|
|
||||
|
@APP.route('/print.css') |
||||
|
def css(): |
||||
|
css, x = update_pad_contents() |
||||
|
|
||||
|
return css, 200, {'Content-Type': 'text/css; charset=utf-8'} |
||||
|
|
||||
|
@APP.route('/pandoc-template.html') |
||||
|
def pandoc_template(): |
||||
|
x, template = update_pad_contents() |
||||
|
|
||||
|
return template, 200, {'Content-Type': 'text/html; charset=utf-8'} |
||||
|
|
||||
|
@APP.route('/pagedjs.html') |
||||
|
def pagedjs(): |
||||
|
# update pad contents |
||||
|
x, template = update_pad_contents() |
||||
|
# get feed contents |
||||
|
query = "https://multi.vvvvvvaria.org/API/latest/50" # !!! needs fixing |
||||
|
feed_html, feed_json = get_multifeeder_feed(query) |
||||
|
# render multifeeder feed in template |
||||
|
jinja_template = Template(template) |
||||
|
html = jinja_template.render(feed=feed_html, mode="print") |
||||
|
|
||||
|
return html, 200, {'Content-Type': 'text/html; charset=utf-8'} |
||||
|
|
||||
|
# ////////////// |
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
APP.debug=True |
||||
|
APP.run(host="0.0.0.0", port=f'{ APP.config["PORTNUMBER"] }', threaded=True) |
@ -0,0 +1,17 @@ |
|||||
|
click==8.0.3 |
||||
|
Flask==2.0.2 |
||||
|
importlib-metadata==4.10.1 |
||||
|
itsdangerous==2.0.1 |
||||
|
Jinja2==3.0.3 |
||||
|
Markdown==3.3.6 |
||||
|
MarkupSafe==2.0.1 |
||||
|
pandoc==2.0.1 |
||||
|
pkg-resources==0.0.0 |
||||
|
plumbum==1.7.2 |
||||
|
ply==3.11 |
||||
|
pypandoc==1.7.2 |
||||
|
typing-extensions==4.0.1 |
||||
|
urllib3==1.26.8 |
||||
|
Werkzeug==2.0.2 |
||||
|
zipp==3.7.0 |
||||
|
|
@ -0,0 +1,76 @@ |
|||||
|
body{ |
||||
|
min-width: 900px; |
||||
|
} |
||||
|
|
||||
|
/* GENERAL RULES */ |
||||
|
|
||||
|
/* main title element that says "in feedmode" */ |
||||
|
h1 em.feedmode{ |
||||
|
color: darkorchid; |
||||
|
} |
||||
|
|
||||
|
/* navigation */ |
||||
|
div#nav{ |
||||
|
position: fixed; |
||||
|
width: calc(100% - 1em); |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
margin: 0; |
||||
|
padding: 0 0.5em; |
||||
|
} |
||||
|
div#nav h1{ |
||||
|
position: absolute; |
||||
|
width: auto; |
||||
|
line-height: 0; |
||||
|
margin: 0.75em 15px; |
||||
|
float: left; |
||||
|
font-size: 24px; |
||||
|
} |
||||
|
div#nav div#buttons{ |
||||
|
margin: 0.5em 15px; |
||||
|
float: right; |
||||
|
} |
||||
|
div#nav input{ |
||||
|
min-width: 300px; |
||||
|
} |
||||
|
div#nav input#html{ |
||||
|
min-width: unset; |
||||
|
} |
||||
|
div#nav form#update{ |
||||
|
float: left; |
||||
|
margin: 0.5em 15px 0 300px; |
||||
|
} |
||||
|
|
||||
|
/* iframe rules */ |
||||
|
iframe{ |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
border: none; |
||||
|
} |
||||
|
|
||||
|
/* main content area */ |
||||
|
div#wrapper{ |
||||
|
/* pages with an iframe are on fixed mode */ |
||||
|
position: fixed; |
||||
|
top: 50px; |
||||
|
left: 25px; |
||||
|
width: calc(100vw - 25px - 25px); |
||||
|
height: calc(100vh - 50px - 25px); |
||||
|
} |
||||
|
div#wrapper.scroll{ |
||||
|
/* the HTML page is on scroll mode */ |
||||
|
position: relative; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
margin: 5em 0 0 0; |
||||
|
} |
||||
|
|
||||
|
/* Z-INDEX */ |
||||
|
|
||||
|
div#wrapper, |
||||
|
div.pagedjs_pages{ |
||||
|
z-index: 1; |
||||
|
} |
||||
|
div#nav{ |
||||
|
z-index: 11; |
||||
|
} |
File diff suppressed because it is too large
File diff suppressed because it is too large
@ -0,0 +1,180 @@ |
|||||
|
/* CSS for Paged.js interface – v0.2 */ |
||||
|
|
||||
|
/* Change the look */ |
||||
|
:root { |
||||
|
--color-background: whitesmoke; |
||||
|
--color-pageSheet: #cfcfcf; |
||||
|
--color-pageBox: violet; |
||||
|
--color-paper: white; |
||||
|
--color-marginBox: transparent; |
||||
|
--pagedjs-crop-color: black; |
||||
|
--pagedjs-crop-shadow: white; |
||||
|
--pagedjs-crop-stroke: 1px; |
||||
|
} |
||||
|
|
||||
|
/* To define how the book look on the screen: */ |
||||
|
@media screen { |
||||
|
body { |
||||
|
background-color: var(--color-background); |
||||
|
} |
||||
|
|
||||
|
.pagedjs_pages { |
||||
|
display: flex; |
||||
|
width: calc(var(--pagedjs-width) * 2); |
||||
|
flex: 0; |
||||
|
flex-wrap: wrap; |
||||
|
margin: 0 auto; |
||||
|
} |
||||
|
|
||||
|
.pagedjs_page { |
||||
|
background-color: var(--color-paper); |
||||
|
box-shadow: 0 0 0 1px var(--color-pageSheet); |
||||
|
margin: 0; |
||||
|
flex-shrink: 0; |
||||
|
flex-grow: 0; |
||||
|
margin-top: 10mm; |
||||
|
} |
||||
|
|
||||
|
.pagedjs_first_page { |
||||
|
margin-left: var(--pagedjs-width); |
||||
|
} |
||||
|
|
||||
|
.pagedjs_page:last-of-type { |
||||
|
margin-bottom: 10mm; |
||||
|
} |
||||
|
|
||||
|
.pagedjs_pagebox{ |
||||
|
box-shadow: 0 0 0 1px var(--color-pageBox); |
||||
|
} |
||||
|
|
||||
|
.pagedjs_left_page{ |
||||
|
z-index: 20; |
||||
|
width: calc(var(--pagedjs-bleed-left) + var(--pagedjs-pagebox-width))!important; |
||||
|
} |
||||
|
|
||||
|
.pagedjs_left_page .pagedjs_bleed-right .pagedjs_marks-crop { |
||||
|
border-color: transparent; |
||||
|
} |
||||
|
|
||||
|
.pagedjs_left_page .pagedjs_bleed-right .pagedjs_marks-middle{ |
||||
|
width: 0; |
||||
|
} |
||||
|
|
||||
|
.pagedjs_right_page{ |
||||
|
z-index: 10; |
||||
|
position: relative; |
||||
|
left: calc(var(--pagedjs-bleed-left)*-1); |
||||
|
} |
||||
|
|
||||
|
/* show the margin-box */ |
||||
|
|
||||
|
.pagedjs_margin-top-left-corner-holder, |
||||
|
.pagedjs_margin-top, |
||||
|
.pagedjs_margin-top-left, |
||||
|
.pagedjs_margin-top-center, |
||||
|
.pagedjs_margin-top-right, |
||||
|
.pagedjs_margin-top-right-corner-holder, |
||||
|
.pagedjs_margin-bottom-left-corner-holder, |
||||
|
.pagedjs_margin-bottom, |
||||
|
.pagedjs_margin-bottom-left, |
||||
|
.pagedjs_margin-bottom-center, |
||||
|
.pagedjs_margin-bottom-right, |
||||
|
.pagedjs_margin-bottom-right-corner-holder, |
||||
|
.pagedjs_margin-right, |
||||
|
.pagedjs_margin-right-top, |
||||
|
.pagedjs_margin-right-middle, |
||||
|
.pagedjs_margin-right-bottom, |
||||
|
.pagedjs_margin-left, |
||||
|
.pagedjs_margin-left-top, |
||||
|
.pagedjs_margin-left-middle, |
||||
|
.pagedjs_margin-left-bottom { |
||||
|
box-shadow: 0 0 0 1px inset var(--color-marginBox); |
||||
|
} |
||||
|
|
||||
|
/* uncomment this part for recto/verso book : ------------------------------------ */ |
||||
|
/* |
||||
|
|
||||
|
.pagedjs_pages { |
||||
|
flex-direction: column; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.pagedjs_first_page { |
||||
|
margin-left: 0; |
||||
|
} |
||||
|
|
||||
|
.pagedjs_page { |
||||
|
margin: 0 auto; |
||||
|
margin-top: 10mm; |
||||
|
} |
||||
|
|
||||
|
.pagedjs_left_page{ |
||||
|
width: calc(var(--pagedjs-bleed-left) + var(--pagedjs-pagebox-width) + var(--pagedjs-bleed-left))!important; |
||||
|
} |
||||
|
|
||||
|
.pagedjs_left_page .pagedjs_bleed-right .pagedjs_marks-crop{ |
||||
|
border-color: var(--pagedjs-crop-color); |
||||
|
} |
||||
|
|
||||
|
.pagedjs_left_page .pagedjs_bleed-right .pagedjs_marks-middle{ |
||||
|
width: var(--pagedjs-cross-size)!important; |
||||
|
} |
||||
|
|
||||
|
.pagedjs_right_page{ |
||||
|
left: 0; |
||||
|
} |
||||
|
*/ |
||||
|
|
||||
|
|
||||
|
|
||||
|
/*--------------------------------------------------------------------------------------*/ |
||||
|
|
||||
|
|
||||
|
|
||||
|
/* uncomment this par to see the baseline : -------------------------------------------*/ |
||||
|
|
||||
|
/* |
||||
|
.pagedjs_pagebox { |
||||
|
--pagedjs-baseline: 22px; |
||||
|
--pagedjs-baseline-position: 5px; |
||||
|
--pagedjs-baseline-color: cyan; |
||||
|
background: linear-gradient(transparent 0%, transparent calc(var(--pagedjs-baseline) - 1px), var(--pagedjs-baseline-color) calc(var(--pagedjs-baseline) - 1px), var(--pagedjs-baseline-color) var(--pagedjs-baseline)), transparent; |
||||
|
background-size: 100% var(--pagedjs-baseline); |
||||
|
background-repeat: repeat-y; |
||||
|
background-position-y: var(--pagedjs-baseline-position); |
||||
|
} */ |
||||
|
|
||||
|
|
||||
|
/*--------------------------------------------------------------------------------------*/ |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
/* Marks (to delete when merge in paged.js) */ |
||||
|
|
||||
|
.pagedjs_marks-crop{ |
||||
|
z-index: 999999999999; |
||||
|
|
||||
|
} |
||||
|
|
||||
|
.pagedjs_bleed-top .pagedjs_marks-crop, |
||||
|
.pagedjs_bleed-bottom .pagedjs_marks-crop{ |
||||
|
box-shadow: 1px 0px 0px 0px var(--pagedjs-crop-shadow); |
||||
|
} |
||||
|
|
||||
|
.pagedjs_bleed-top .pagedjs_marks-crop:last-child, |
||||
|
.pagedjs_bleed-bottom .pagedjs_marks-crop:last-child{ |
||||
|
box-shadow: -1px 0px 0px 0px var(--pagedjs-crop-shadow); |
||||
|
} |
||||
|
|
||||
|
.pagedjs_bleed-left .pagedjs_marks-crop, |
||||
|
.pagedjs_bleed-right .pagedjs_marks-crop{ |
||||
|
box-shadow: 0px 1px 0px 0px var(--pagedjs-crop-shadow); |
||||
|
} |
||||
|
|
||||
|
.pagedjs_bleed-left .pagedjs_marks-crop:last-child, |
||||
|
.pagedjs_bleed-right .pagedjs_marks-crop:last-child{ |
||||
|
box-shadow: 0px -1px 0px 0px var(--pagedjs-crop-shadow); |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
div.post{ |
||||
|
width: 250px; |
||||
|
border: 1px solid magenta; |
||||
|
padding: 1em; |
||||
|
margin: 0.5em; |
||||
|
max-height: 400px; |
||||
|
float: left; |
||||
|
overflow-y: scroll; |
||||
|
font-size: 12px; |
||||
|
} |
||||
|
div.post img{ |
||||
|
max-width: 100%; |
||||
|
} |
@ -0,0 +1,44 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang='en'> |
||||
|
<head> |
||||
|
<meta charset="utf-8" /> |
||||
|
<title>feedmode</title> |
||||
|
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='main.css') }}"> |
||||
|
{% block head %} |
||||
|
{% endblock %} |
||||
|
</head> |
||||
|
<body> |
||||
|
<div id="wrapper" class="{% block pagetype %}{% endblock %}"> |
||||
|
{% block content %} |
||||
|
{% endblock %} |
||||
|
</div> |
||||
|
</body> |
||||
|
<script> |
||||
|
window.addEventListener('load', function () { |
||||
|
|
||||
|
// Insert the nav buttons, after the page is loaded |
||||
|
const nav = document.createElement('div'); |
||||
|
nav.id = 'nav'; |
||||
|
|
||||
|
nav.innerHTML = ` |
||||
|
<h1><em class="feedmode">feedmode</em><sup>(very beta)</sup></h1> |
||||
|
<form id="update" action="/update/" method="post"> |
||||
|
<input type="text" name="query" value="{% if query %}{{ query }}{% else %}https://multi.vvvvvvaria.org/API/latest/50{% endif %}"> |
||||
|
<input id="html" type="submit" value="update"> |
||||
|
</form> |
||||
|
<div id="buttons"> |
||||
|
<a href="/select/"><button>select</button></a> |
||||
|
<a href="/preview/"><button>preview</button></a> |
||||
|
<a href="/pdf/"><button>pdf</button></a> |
||||
|
<a href="/stylesheet/"><button>stylesheet</button></a>: <input type="text" name="pad" value="https://pad.vvvvvvaria.org/feedmode.css"> |
||||
|
<a href="/template/"><button>template</button></a>: <input type="text" name="pad" value="https://pad.vvvvvvaria.org/feedmode.template"> |
||||
|
</div> |
||||
|
`; |
||||
|
|
||||
|
document.body.insertBefore(nav, document.body.firstChild); |
||||
|
|
||||
|
}) |
||||
|
</script> |
||||
|
{% block footer %} |
||||
|
{% endblock %} |
||||
|
</html> |
@ -0,0 +1,9 @@ |
|||||
|
{% extends "base.html" %} |
||||
|
|
||||
|
{% block pagetype %} |
||||
|
scroll |
||||
|
{% endblock %} |
||||
|
|
||||
|
{% block content %} |
||||
|
{{ html | safe }} |
||||
|
{% endblock %} |
@ -0,0 +1,5 @@ |
|||||
|
{% extends "base.html" %} |
||||
|
|
||||
|
{% block content %} |
||||
|
<iframe src="{{ url }}"></iframe> |
||||
|
{% endblock %} |
@ -0,0 +1,10 @@ |
|||||
|
{% for post in response %} |
||||
|
<div class="post"> |
||||
|
<!-- <input type="checkbox" name="check" value="{{ loop.index }}"><br><br> --> |
||||
|
<h1>{{ post.title }}</h1> |
||||
|
<!-- <div><small>{{ post.author }}</small></div> --> |
||||
|
<div><small>{{ post.published }}</small></div> |
||||
|
<div><small><a href="{{ post.link }}">{{ post.link }}</a></small></div> |
||||
|
<div>{{ post.summary }}</div> |
||||
|
</div> |
||||
|
{% endfor %} |
@ -0,0 +1,5 @@ |
|||||
|
{% extends "base.html" %} |
||||
|
|
||||
|
{% block content %} |
||||
|
<iframe src="https://pad.vvvvvvaria.org/{{ name }}{{ ext }}"></iframe> |
||||
|
{% endblock %} |
@ -0,0 +1,36 @@ |
|||||
|
<!DOCTYPE html> |
||||
|
<html lang="en"> |
||||
|
|
||||
|
<head> |
||||
|
<meta charset="utf-8"> |
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
|
{% if mode == "print" %} |
||||
|
<script src="/static/paged.js" type="text/javascript"></script> |
||||
|
<script src="/static/paged.polyfill.js" type="text/javascript"></script> |
||||
|
<link href="/static/pagedjs.css" rel="stylesheet" type="text/css" media="screen"> |
||||
|
{% endif %} |
||||
|
<link href="/print.css" rel="stylesheet" type="text/css" media="{{ mode }}"> |
||||
|
<title>The Everydaily</title> |
||||
|
</head> |
||||
|
|
||||
|
<body> |
||||
|
|
||||
|
<section id="cover"> |
||||
|
<h1 id="title">The Everydaily</h1> |
||||
|
</section> |
||||
|
|
||||
|
<section id="header"> |
||||
|
<div>This is the header...</div> |
||||
|
</section> |
||||
|
|
||||
|
<section id="feed"> |
||||
|
{{ feed }} |
||||
|
</section> |
||||
|
|
||||
|
<section id="footer"> |
||||
|
<div>Anything in the footer here...</div> |
||||
|
</section> |
||||
|
</body> |
||||
|
</html> |
||||
|
|
||||
|
|
@ -0,0 +1,30 @@ |
|||||
|
{% extends "base.html" %} |
||||
|
|
||||
|
{% block content %} |
||||
|
<iframe id="pdf" name="pdf" src="/pagedjs.html"></iframe> |
||||
|
{% endblock %} |
||||
|
|
||||
|
{% block footer %} |
||||
|
<script> |
||||
|
function printPage(){ |
||||
|
window.frames["pdf"].focus(); |
||||
|
window.frames["pdf"].print(); |
||||
|
} |
||||
|
|
||||
|
window.addEventListener('load', function () { |
||||
|
|
||||
|
// Load the main.css again, to load the stylesheet for the nav |
||||
|
var cssLink = document.createElement('link'); |
||||
|
cssLink.rel = 'stylesheet'; |
||||
|
cssLink.href = '/static/main.css'; |
||||
|
var head = document.getElementsByTagName('head')[0]; |
||||
|
head.insertBefore(cssLink, head.firstChild); |
||||
|
|
||||
|
// Insert the SAVE button |
||||
|
const nav = document.getElementById('buttons'); |
||||
|
const save = '<a href="#"><button id="save" onClick="printPage()">save</button></a>'; |
||||
|
nav.innerHTML = nav.innerHTML + save; |
||||
|
|
||||
|
}) |
||||
|
</script> |
||||
|
{% endblock %} |
@ -0,0 +1,21 @@ |
|||||
|
{% extends "base.html" %} |
||||
|
|
||||
|
{% block head %} |
||||
|
<link rel="stylesheet" type="text/css" href="/static/select.css"> |
||||
|
{% endblock %} |
||||
|
|
||||
|
{% block pagetype %} |
||||
|
scroll |
||||
|
{% endblock %} |
||||
|
|
||||
|
{% block content %} |
||||
|
{% for post in feed %} |
||||
|
<div class="post"> |
||||
|
<input type="checkbox" name="check" value="{{ loop.index }}"><br><br> |
||||
|
<h1>{{ post.title }}</h1> |
||||
|
<div><small>{{ post.published }}</small></div> |
||||
|
<div><small><a href="{{ post.link }}">{{ post.link }}</a></small></div> |
||||
|
<div>{{ post.summary | safe }}</div> |
||||
|
</div> |
||||
|
{% endfor %} |
||||
|
{% endblock %} |
Loading…
Reference in new issue