feat: Introduce pyproject.toml for build configuration
Migrated project dependencies and metadata into a newly added `pyproject.toml`, aligning with modern Python packaging practices. Removed `requirements.txt` as dependencies are now specified in the unified project configuration. Restructured the project by moving Python code, static assets, and templates into a `src/structables` directory to encapsulate project components better and facilitate packaging. Moreover, refactor `main.py` to read environment variables earlier, streamline argument parsing, and ensure configurations are correctly applied before the Flask app initialization. This reorganization supports better project scalability, simplifies the build process, and enhances maintainability by consolidating project configurations and dependencies. Note: The usage of `pyproject.toml` requires tools that support PEP 518 and might necessitate updates to CI/CD pipelines or developer workflows.
This commit is contained in:
parent
25a07d797b
commit
85d5c88294
34 changed files with 108 additions and 60 deletions
0
src/structables/__init__.py
Normal file
0
src/structables/__init__.py
Normal file
1282
src/structables/main.py
Normal file
1282
src/structables/main.py
Normal file
File diff suppressed because it is too large
Load diff
323
src/structables/static/css/style.css
Normal file
323
src/structables/static/css/style.css
Normal file
|
@ -0,0 +1,323 @@
|
|||
:root {
|
||||
--main-font: "DejaVu Sans Mono", monospace;
|
||||
--main-color: #bbc2cf;
|
||||
--link-color: #ff6c6b;
|
||||
--heading-color: #51afef;
|
||||
--code-bg: #20232a;
|
||||
--code-color: #969ba6;
|
||||
--border-color: lightgrey;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: var(--main-font);
|
||||
margin: 20px auto;
|
||||
line-height: 1.5;
|
||||
font-size: 1.1em;
|
||||
max-width: 100vw;
|
||||
color: var(--main-color);
|
||||
padding: 0 10px;
|
||||
hyphens: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--link-color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2,
|
||||
h3 {
|
||||
line-height: 1.2;
|
||||
color: var(--heading-color);
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
pre,
|
||||
code {
|
||||
background: var(--code-bg);
|
||||
color: var(--code-color);
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 5px;
|
||||
tab-size: 4;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
border-left: 10px solid var(--code-color);
|
||||
padding-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.navbar-logo {
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
flex: 1;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.card-footer {
|
||||
padding: 1rem;
|
||||
background-color: #f8f9fa;
|
||||
border-top: 1px solid #ddd;
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.card-img-top {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.navbar-collapse {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.navbar-nav {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.navbar-nav {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
margin-left: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.navbar-collapse {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
.ibles {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.ible-small {
|
||||
font-size: 0.7em;
|
||||
font-weight: thin;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
.step-section {
|
||||
background-color: var(--primary-bg);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px var(--shadow-color);
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.step-header h2 {
|
||||
font-size: 2em;
|
||||
color: var(--heading-color);
|
||||
}
|
||||
|
||||
.step-images img,
|
||||
.step-videos video,
|
||||
.step-iframes iframe {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px var(--shadow-color);
|
||||
}
|
||||
|
||||
.step-text {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.reply-button,
|
||||
.replies {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.reply-button+label {
|
||||
position: relative;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input.reply-button:checked+label+.replies {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
.member-list {
|
||||
display: inline-block;
|
||||
max-width: 200px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.ible-list-item {
|
||||
display: inline-block;
|
||||
max-width: 350px;
|
||||
vertical-align: top;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.contest-list-item {
|
||||
display: inline-block;
|
||||
max-width: 500px;
|
||||
vertical-align: top;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.archive-month-wrapper {
|
||||
display: inline-block;
|
||||
width: 30vw;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.archive-month {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: -10px;
|
||||
margin-bottom: 1rem;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.archive {
|
||||
margin-bottom: -20px;
|
||||
}
|
||||
|
||||
ul.pagination {
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
padding: 0 33vw;
|
||||
list-style-type: none;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
ul.pagination li.active a,
|
||||
ul.pagination li.disabled a,
|
||||
ul.pagination li.active a:hover,
|
||||
ul.pagination li.disabled a:hover {
|
||||
color: #bbc2cf;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.closed-contest-contest {
|
||||
object-fit: cover;
|
||||
width: 33vw;
|
||||
height: 15vw;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.closed-contest-winner,
|
||||
.closed-contest-winner-img {
|
||||
width: 15vw;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
padding: 0 10px;
|
||||
font-size: 0.8em;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sitemap-group {
|
||||
margin-top: 2em;
|
||||
display: inline-block;
|
||||
width: 30vw;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.sitemap-group h2 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.container {
|
||||
margin-top: 20px !important;
|
||||
}
|
||||
|
||||
header {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
.go_here_link {
|
||||
background-color: #4caf50;
|
||||
/* Green */
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 15px 32px;
|
||||
text-align: center;
|
||||
text-decoration: none;
|
||||
display: inline-block;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.go_here_link:hover {
|
||||
color: black;
|
||||
}
|
||||
|
||||
.wrap {
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
.navbar-logo {
|
||||
height: 64px;
|
||||
}
|
||||
|
||||
.img-fluid {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
video {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
iframe {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: #6c757d !important;
|
||||
}
|
6
src/structables/static/dist/css/bootstrap.min.css
vendored
Normal file
6
src/structables/static/dist/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
src/structables/static/dist/css/bootstrap.min.css.map
vendored
Normal file
1
src/structables/static/dist/css/bootstrap.min.css.map
vendored
Normal file
File diff suppressed because one or more lines are too long
7
src/structables/static/dist/js/bootstrap.bundle.min.js
vendored
Normal file
7
src/structables/static/dist/js/bootstrap.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
src/structables/static/dist/js/bootstrap.bundle.min.js.map
vendored
Normal file
1
src/structables/static/dist/js/bootstrap.bundle.min.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
src/structables/static/img/logo.png
Normal file
BIN
src/structables/static/img/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
BIN
src/structables/static/img/logo_lg.png
Normal file
BIN
src/structables/static/img/logo_lg.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 891 KiB |
1
src/structables/static/img/magnifying-glass-solid.svg
Normal file
1
src/structables/static/img/magnifying-glass-solid.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512"><!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"/></svg>
|
After Width: | Height: | Size: 494 B |
8
src/structables/templates/400.html
Normal file
8
src/structables/templates/400.html
Normal file
|
@ -0,0 +1,8 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<center>
|
||||
<h1>400</h1>
|
||||
<p>The request was malformed</p>
|
||||
</center>
|
||||
{% endblock %}
|
13
src/structables/templates/404.html
Normal file
13
src/structables/templates/404.html
Normal file
|
@ -0,0 +1,13 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<center>
|
||||
<h1>404</h1>
|
||||
<p>This resource cannot be found</p>
|
||||
<br>
|
||||
<p>Why don't you try one of these instead?</p>
|
||||
<a href="/" class="go_here_link">Featured</a>
|
||||
<a href="/contest" class="go_here_link">Contests</a>
|
||||
<br><br>
|
||||
</center>
|
||||
{% endblock %}
|
9
src/structables/templates/429.html
Normal file
9
src/structables/templates/429.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
{% include "header.html" %}
|
||||
<center>
|
||||
<h1>429</h1>
|
||||
<p>This instance is being rate-limited</p>
|
||||
</center>
|
||||
{% endblock %}
|
8
src/structables/templates/500.html
Normal file
8
src/structables/templates/500.html
Normal file
|
@ -0,0 +1,8 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<center>
|
||||
<h1>500</h1>
|
||||
<p>An internal server error has occurred. Please try again later, or notify the administrator if this keeps happening.</p>
|
||||
</center>
|
||||
{% endblock %}
|
31
src/structables/templates/archives.html
Normal file
31
src/structables/templates/archives.html
Normal file
|
@ -0,0 +1,31 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<center>
|
||||
<h1>Past Contests</h1>
|
||||
<p>{{ contest_count }}</p>
|
||||
<p><a href="/contest/">See running contests</a></p>
|
||||
</center>
|
||||
{% for year in contest_list %}
|
||||
<div class="archive-year">
|
||||
<hr>
|
||||
<h2>{{ year[0] }}</h2>
|
||||
<hr>
|
||||
{% for month in year[1] %}
|
||||
<div class="archive-month-wrapper">
|
||||
<div class="archive-month">
|
||||
<h3>{{ month[0] }}</h3>
|
||||
{% for contest in month[1] %}
|
||||
<div class="archive">
|
||||
<p>{{ contest[0] }}<a href="{{ contest[1] }}">{{ contest[2] }}</a></p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<br>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<hr>
|
||||
{{ pagination|safe }}
|
||||
{% endblock %}
|
7
src/structables/templates/article-review.html
Normal file
7
src/structables/templates/article-review.html
Normal file
|
@ -0,0 +1,7 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
<center>
|
||||
<h1>This content is being reviewed</h1>
|
||||
<p>This Instructable was just published and is still pending review.</p>
|
||||
</center>
|
||||
{% endblock %}
|
87
src/structables/templates/article.html
Normal file
87
src/structables/templates/article.html
Normal file
|
@ -0,0 +1,87 @@
|
|||
{% extends "base.html" %} {% block content %}
|
||||
<div class="container my-5">
|
||||
<div class="header-section text-center mb-5">
|
||||
<h1 class="display-4">{{ title }}</h1>
|
||||
<p class="text-muted">
|
||||
by
|
||||
<a href="{{ author_link }}" class="text-decoration-none">{{ author }}</a>
|
||||
in
|
||||
<a href="{{ category_link }}" class="text-decoration-none"
|
||||
>{{ category }}</a
|
||||
>
|
||||
>
|
||||
<a href="{{ channel_link }}" class="text-decoration-none"
|
||||
>{{ channel }}</a
|
||||
>
|
||||
</p>
|
||||
<p class="text-muted">
|
||||
{{ views }} Views, {{ favorites }} Favorites, {{ comment_count }} Comments
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% for step in steps %}
|
||||
<section class="step-section mb-5 p-4 rounded shadow-sm">
|
||||
<div class="step-header mb-4">
|
||||
<h2>{{ step.title }}</h2>
|
||||
</div>
|
||||
|
||||
{% if step.imgs %}
|
||||
<div class="step-images row mb-4">
|
||||
{% for step_img in step.imgs %}
|
||||
<div class="col-md-4 mb-3">
|
||||
<img
|
||||
src="{{ step_img.src }}"
|
||||
alt="{{ step_img.alt }}"
|
||||
class="img-fluid rounded shadow-sm"
|
||||
/>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %} {% if step.videos %}
|
||||
<div class="step-videos row mb-4">
|
||||
{% for step_video in step.videos %}
|
||||
<div class="col-md-6 mb-3">
|
||||
<video
|
||||
src="{{ step_video }}"
|
||||
controls
|
||||
class="w-100 rounded shadow-sm"
|
||||
></video>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %} {% if step.iframes %}
|
||||
<div class="step-iframes row mb-4">
|
||||
{% for step_iframe in step.iframes %}
|
||||
<div class="col-md-8 mb-3">
|
||||
<iframe
|
||||
src="{{ step_iframe.src }}"
|
||||
width="100%"
|
||||
height="{{ step_iframe.height }}"
|
||||
frameborder="0"
|
||||
class="rounded shadow-sm"
|
||||
></iframe>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="step-text mb-3">{{ step.text|safe }}</div>
|
||||
|
||||
{% if step.downloads %}
|
||||
<div class="step-downloads row mb-4">
|
||||
<div class="col-12">
|
||||
<h3>Downloads</h3>
|
||||
</div>
|
||||
{% for step_download in step.downloads %}
|
||||
<div class="col-md-2 mb-3">
|
||||
<a href="{{ step_download.src }}" class="btn btn-primary w-100"
|
||||
>{{ step_download.name }}</a
|
||||
>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
27
src/structables/templates/base.html
Normal file
27
src/structables/templates/base.html
Normal file
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{% if title %}{{ title }} - {% endif %}Structables</title>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="{{ url_for('static', filename='css/style.css') }}"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="{{ url_for('static', filename='dist/css/bootstrap.min.css') }}"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
href="{{ url_for('static', filename='img/logo.png') }}"
|
||||
/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{% include "header.html" %}
|
||||
<main>{% block content %} {% endblock %}</main>
|
||||
{% include "footer.html" %}
|
||||
</body>
|
||||
</html>
|
68
src/structables/templates/category.html
Normal file
68
src/structables/templates/category.html
Normal file
|
@ -0,0 +1,68 @@
|
|||
{% extends "base.html" %} {% block content %}
|
||||
<div class="container">
|
||||
<h1 class="text-center">{{ title }}</h1>
|
||||
<div class="row">
|
||||
{% for channel in channels %}
|
||||
<div class="col-6 col-md-4 col-lg-3 text-center mb-3">
|
||||
<a
|
||||
href="/{{ channel }}"
|
||||
class="btn btn-primary d-block position-relative"
|
||||
>
|
||||
{{ channel }}
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<h2 class="text-center">
|
||||
<a href="{{ path }}projects/">Featured Projects</a>
|
||||
</h2>
|
||||
<div class="row">
|
||||
{% for ible in ibles %}
|
||||
<div class="col-xs-12 col-sm-6 col-lg-4 mb-4">
|
||||
<div class="card h-100 d-flex flex-column">
|
||||
<a href="{{ ible.link }}">
|
||||
<img
|
||||
class="card-img-top"
|
||||
src="{{ ible.img }}"
|
||||
alt="{{ ible.alt }}"
|
||||
style="max-width: 100%"
|
||||
/>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ ible.title }}</h5>
|
||||
</div>
|
||||
</a>
|
||||
<div class="card-footer mt-auto">
|
||||
<p class="card-text">
|
||||
by <a href="{{ ible.author_link }}">{{ ible.author }}</a>
|
||||
</p>
|
||||
<p class="card-text">
|
||||
in <a href="{{ ible.channel_link }}">{{ ible.channel }}</a>
|
||||
</p>
|
||||
<p class="card-text">
|
||||
{{ ible.favorites }} Favorites, {{ ible.views }} Views
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- Doesn't seem to be current
|
||||
<h2 class="text-center"><a href="/contest/">Contests</a></h2>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
{% for contest in contests %}
|
||||
<a href="{{ contest.link }}">
|
||||
<img
|
||||
src="{{ contest.img }}"
|
||||
alt="{{ contest.title }}"
|
||||
class="img-fluid"
|
||||
/>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
{% endblock %}
|
34
src/structables/templates/collection.html
Normal file
34
src/structables/templates/collection.html
Normal file
|
@ -0,0 +1,34 @@
|
|||
{% extends "base.html" %} {% block content %}
|
||||
<div class="container text-center">
|
||||
<h1>{{ title }}</h1>
|
||||
<p>
|
||||
by <a href="{{ author_link }}">{{ author }}</a> in
|
||||
<a href="{{ category_link }}">{{ category }}</a> >
|
||||
<a href="{{ channel_link }}">{{ channel }}</a>
|
||||
</p>
|
||||
<p>{{ views }} Views, {{ favorites }} Favorites</p>
|
||||
|
||||
<div class="row justify-content-center">
|
||||
{% for thumbnail in thumbnails %}
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="ible-list-item">
|
||||
<a href="{{ thumbnail.link }}" style="color: #bbc2cf">
|
||||
<img
|
||||
class="img-fluid"
|
||||
src="{{ thumbnail.img }}"
|
||||
alt="{{ thumbnail.title }}"
|
||||
style="max-width: 350px"
|
||||
/>
|
||||
<p>{{ thumbnail.author }}</p>
|
||||
</a>
|
||||
<p>
|
||||
by <a href="{{ thumbnail.author_link }}">{{ thumbnail.author }}</a> in
|
||||
<a href="{{ thumbnail.channel_link }}">{{ thumbnail.channel }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
23
src/structables/templates/contest.html
Normal file
23
src/structables/templates/contest.html
Normal file
|
@ -0,0 +1,23 @@
|
|||
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<center>
|
||||
<img src="{{ img }}" alt="{{ title }}" style="max-width:98vw;">
|
||||
<p>{{ entry_count }} Entries, {{ prizes }} Prizes</p>
|
||||
<br>
|
||||
{{ info|safe }}
|
||||
<div class="ible-list">
|
||||
{% for ible in entries %}
|
||||
<div class="ible-list-item">
|
||||
<a href="{{ ible.link }}" style="color:#bbc2cf;">
|
||||
<img style="max-width:350px;" src="{{ ible.entry_img }}" alt="{{ ible.entry_title }}">
|
||||
<p>{{ ible.entry_title }}</p>
|
||||
</a>
|
||||
<p>by <a href="{{ ible.author_link }}">{{ ible.author }}</a> in <a href="{{ ible.channel_link }}">{{ ible.channel }}</a></p>
|
||||
<p>{{ ible.views }} Views</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</center>
|
||||
{% endblock %}
|
40
src/structables/templates/contests.html
Normal file
40
src/structables/templates/contests.html
Normal file
|
@ -0,0 +1,40 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<center>
|
||||
<h1>{{ title }}</h1>
|
||||
{{ contest_count|safe }}
|
||||
<br>
|
||||
<div class="contest-list">
|
||||
{% for contest in contests %}
|
||||
<div class="contest-list-item">
|
||||
<a href="{{ contest.link }}">
|
||||
<img src="{{ contest.img }}" alt="{{ contest.alt }}" style="max-width:500px;">
|
||||
</a>
|
||||
<p>Closes {{ contest.deadline }}</p>
|
||||
<p class="ible-small">{{ contest.prizes }} Prizes, {{ contest.entries }} Entries</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="closed-contests" id="contest-winners">
|
||||
<h2>Winner's Circle</h2>
|
||||
{% for closed in closed %}
|
||||
<div class="closed-contest">
|
||||
<a href="{{ closed.link }}"><img class="closed-contest-contest" src="{{ closed.img }}"
|
||||
alt="{{ closed.alt }}"></a>
|
||||
{% for featured_items in closed.featured_items %}
|
||||
<div class="closed-contest-winner">
|
||||
<a href="{{ featured_items.link }}">
|
||||
<img class="closed-contest-winner-img" src="{{ featured_items.img }}"
|
||||
alt="{{ featured_items.title}}">
|
||||
<br>
|
||||
<b>{{ featured_items.title }}</b>
|
||||
</a>
|
||||
<p>by <a href="{{ featured_items.author_link }}">{{ featured_items.author }}</a></p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</center>
|
||||
{% endblock %}
|
11
src/structables/templates/footer.html
Normal file
11
src/structables/templates/footer.html
Normal file
|
@ -0,0 +1,11 @@
|
|||
<footer class="text-center">
|
||||
<hr />
|
||||
<p>
|
||||
<a href="https://git.private.coffee/PrivateCoffee/structables"
|
||||
>Structables Code (AGPLv3)</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<a href="/privacypolicy">View privacy policy.</a>
|
||||
</p>
|
||||
</footer>
|
39
src/structables/templates/header.html
Normal file
39
src/structables/templates/header.html
Normal file
|
@ -0,0 +1,39 @@
|
|||
<header>
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="/">
|
||||
<img
|
||||
class="navbar-logo"
|
||||
src="{{ url_for('static', filename='img/logo.png') }}"
|
||||
alt="Structables Logo"
|
||||
/>
|
||||
</a>
|
||||
<div class="navbar-collapse">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/projects/">Projects</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/contest/">Contests</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/teachers/">Teachers</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/sitemap/">Sitemap</a>
|
||||
</li>
|
||||
</ul>
|
||||
<form class="d-flex" action="/search" method="post">
|
||||
<input
|
||||
class="form-control me-2"
|
||||
type="search"
|
||||
placeholder="Search"
|
||||
name="q"
|
||||
aria-label="Search"
|
||||
/>
|
||||
<button class="btn btn-outline-success" type="submit">Search</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
12
src/structables/templates/iframe.html
Normal file
12
src/structables/templates/iframe.html
Normal file
|
@ -0,0 +1,12 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>iframe content</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Blocked iframe</h1>
|
||||
<p>This page contains content from outside Instructables.com. This was blocked for your safety.</p>
|
||||
<p>It tries to load the following URL:</p>
|
||||
<p><a href="{{ url | safe }}" target="_self">{{ url | safe }}</a></p>
|
||||
<p>Click <a href="{{ url | safe }}" target="_self">here</a> to load the content.</p>
|
||||
</body>
|
||||
</html>
|
43
src/structables/templates/index.html
Normal file
43
src/structables/templates/index.html
Normal file
|
@ -0,0 +1,43 @@
|
|||
{% extends "base.html" %} {% block content %}
|
||||
<div class="container text-center">
|
||||
<h1>{{ title }}</h1>
|
||||
{% for section in sections %}
|
||||
<section>
|
||||
<div class="row">
|
||||
<h3><a href="{{ section[1] }}">{{ section[0] }}</a></h3>
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
{% for ible in section[2] %}
|
||||
<div class="col-xs-12 col-sm-6 col-lg-4 mb-4">
|
||||
<div class="card h-100">
|
||||
<a href="{{ ible.link }}">
|
||||
<img
|
||||
class="card-img-top"
|
||||
src="{{ ible.img }}"
|
||||
alt="{{ ible.alt }}"
|
||||
style="max-width: 100%"
|
||||
/>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ ible.title }}</h5>
|
||||
</div>
|
||||
</a>
|
||||
<div class="card-footer">
|
||||
<p class="card-text">
|
||||
by <a href="{{ ible.author_link }}">{{ ible.author }}</a>
|
||||
</p>
|
||||
<p class="card-text">
|
||||
in <a href="{{ ible.channel_link }}">{{ ible.channel }}</a>
|
||||
</p>
|
||||
<p class="card-text">
|
||||
{{ ible.favorites }} Favorites, {{ ible.views }} Views
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<hr />
|
||||
</section>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
32
src/structables/templates/member-instructables.html
Normal file
32
src/structables/templates/member-instructables.html
Normal file
|
@ -0,0 +1,32 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<center>
|
||||
<img width=150px height=150px style="display:inline-block;" src="{{ header_content.avatar }}" alt="{{ header_content.title }}">
|
||||
<h1>{{ header_content.title }}</h1>
|
||||
<span>{{ header_content.location }} </span>
|
||||
<span>{{ header_content.signup }}</span>
|
||||
<br>
|
||||
<span>{{ header_content.instructables }} Instructables </span>
|
||||
<span>{{ header_content.views }} Views </span>
|
||||
<span>{{ header_content.comments }} Comments </span>
|
||||
<span>{{ header_content.followers }} Followers</span>
|
||||
<br>
|
||||
<p>{{ header_content.bio }}</p>
|
||||
<hr>
|
||||
<h2>Instructables</h2>
|
||||
|
||||
<div style="max-width:90%;">
|
||||
{% for ible in ibles %}
|
||||
<div class="ible-list-item">
|
||||
<a href="{{ ible.link }}" style="color:#bbc2cf;">
|
||||
<img style="max-width:350px;" src="{{ ible.img }}" alt="{{ ible.title }}">
|
||||
<p>{{ ible.title }}</p>
|
||||
<p>{{ ible.views }} Views, {{ ible.favorites }} Favorites</p>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
</center>
|
||||
{% endblock %}
|
39
src/structables/templates/member.html
Normal file
39
src/structables/templates/member.html
Normal file
|
@ -0,0 +1,39 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<center>
|
||||
<img width=150px height=150px style="display:inline-block;" src="{{ header_content.avatar }}" alt="{{ header_content.title }}">
|
||||
<h1>{{ header_content.title }}</h1>
|
||||
<span>{{ header_content.location }} </span>
|
||||
<span>{{ header_content.signup }}</span>
|
||||
<br>
|
||||
<span>{{ header_content.instructables }} Instructables </span>
|
||||
<span>{{ header_content.views }} Views </span>
|
||||
<span>{{ header_content.comments }} Comments </span>
|
||||
<span>{{ header_content.followers }} Followers</span>
|
||||
<br>
|
||||
<p>{{ header_content.bio }}</p>
|
||||
{% if ible_list_title != "" %}
|
||||
<hr>
|
||||
{% endif %}
|
||||
<h2>{{ ible_list_title }}</h2>
|
||||
{% for ible in ibles %}
|
||||
<div class="member-list">
|
||||
<a href="{{ ible.link }}" style="color:#bbc2cf;">
|
||||
<img style="max-width:200px;" src="{{ ible.img }}" alt="{{ ible.title }}">
|
||||
<p>{{ ible.title }}</p>
|
||||
</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<p><a href="/member/{{ header_content.title }}/instructables/">View all Instructables</a></p>
|
||||
<br>
|
||||
<h2>{{ ach_list_title }}</h2>
|
||||
{% for ach in achs %}
|
||||
<div class="member-list">
|
||||
<p><b>{{ ach[0] }}</b></p>
|
||||
<p>{{ ach[1] }}</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
</center>
|
||||
{% endblock %}
|
10
src/structables/templates/privacypolicy.html
Normal file
10
src/structables/templates/privacypolicy.html
Normal file
|
@ -0,0 +1,10 @@
|
|||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<center>
|
||||
<br>
|
||||
<h1 style="font-size:2em;line-height:0em;">Privacy Policy</h1>
|
||||
<br>
|
||||
<p>{{ content }}</p>
|
||||
</center>
|
||||
{% endblock %}
|
56
src/structables/templates/projects.html
Normal file
56
src/structables/templates/projects.html
Normal file
|
@ -0,0 +1,56 @@
|
|||
{% extends "base.html" %} {% block content %}
|
||||
<center>
|
||||
<h1>{{ title }}</h1>
|
||||
<span><a href="{{ path }}/">Featured</a></span>
|
||||
<span><a href="{{ path }}/recent/">Recent</a></span>
|
||||
<span><a href="{{ path }}/popular/">Popular</a></span>
|
||||
<span><a href="{{ path }}/views/">Views</a></span>
|
||||
<span><a href="{{ path }}/winners/">Winners</a></span>
|
||||
<br />
|
||||
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
{% for ible in ibles %}
|
||||
<div class="col-xs-12 col-sm-6 col-lg-4 mb-4">
|
||||
<div class="card h-100 d-flex flex-column">
|
||||
<a href="{{ ible.link }}">
|
||||
<img
|
||||
class="card-img-top"
|
||||
src="{{ ible.img }}"
|
||||
alt="{{ ible.alt }}"
|
||||
style="max-width: 100%"
|
||||
/>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ ible.title }}</h5>
|
||||
</div>
|
||||
</a>
|
||||
<div class="card-footer mt-auto">
|
||||
<p class="card-text">
|
||||
by <a href="{{ ible.author_link }}">{{ ible.author }}</a>
|
||||
</p>
|
||||
<p class="card-text">
|
||||
in <a href="{{ ible.channel_link }}">{{ ible.channel }}</a>
|
||||
</p>
|
||||
<p class="card-text">
|
||||
{{ ible.favorites }} Favorites, {{ ible.views }} Views
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<ul class="pagination">
|
||||
{% for page in pagination %}
|
||||
<li class="page-item">
|
||||
<a
|
||||
class="page-link {% if page.active %}active{% endif %} {% if page.disabled %}disabled{% endif %}"
|
||||
href="{{ page.link }}"
|
||||
>{{ page.text }}</a
|
||||
>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</center>
|
||||
{% endblock %}
|
23
src/structables/templates/sitemap.html
Normal file
23
src/structables/templates/sitemap.html
Normal file
|
@ -0,0 +1,23 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<center>
|
||||
<h1>Sitemap</h1>
|
||||
<div class="container">
|
||||
<div class="row sitemap-groups"></div>
|
||||
{% for group in groups %}
|
||||
<div class="sitemap-group col-xs-12 col-sm-6 col-lg-4">
|
||||
<a href="{{ group[1] }}">
|
||||
<h2>{{ group[0] }}</h2>
|
||||
</a>
|
||||
<ul>
|
||||
{% for channel in group[2] %}
|
||||
<li><a href="{{ channel[1] }}">{{ channel[0] }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</center>
|
||||
{% endblock %}
|
2
src/structables/templates/style.html
Normal file
2
src/structables/templates/style.html
Normal file
|
@ -0,0 +1,2 @@
|
|||
<!-- TODO: Get rid of this -->
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
Loading…
Add table
Add a link
Reference in a new issue