openwrite 0.10.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. openwrite/__init__.py +134 -0
  2. openwrite/cli.py +340 -0
  3. openwrite/db/__init__.py +0 -0
  4. openwrite/db/base.py +3 -0
  5. openwrite/launch_gemini.py +8 -0
  6. openwrite/routes/admin.py +170 -0
  7. openwrite/routes/auth.py +77 -0
  8. openwrite/routes/blog.py +205 -0
  9. openwrite/routes/dashboard.py +408 -0
  10. openwrite/routes/federation.py +284 -0
  11. openwrite/routes/main.py +96 -0
  12. openwrite/routes/upload.py +81 -0
  13. openwrite/static/IBMPlexSans-Regular.ttf +0 -0
  14. openwrite/static/Merriweather.ttf +0 -0
  15. openwrite/static/avatar.png +0 -0
  16. openwrite/static/favicon.ico +0 -0
  17. openwrite/static/logo.png +0 -0
  18. openwrite/static/script/chart.js +14 -0
  19. openwrite/static/script/highlight.min.js +1244 -0
  20. openwrite/static/script/openwrite.js +88 -0
  21. openwrite/static/script/papaparse.min.js +7 -0
  22. openwrite/static/script/showdown.min.js +3 -0
  23. openwrite/static/script/showdown.min.js.map +1 -0
  24. openwrite/static/script/site.compat.min.js +7 -0
  25. openwrite/static/script/site.min.js +7 -0
  26. openwrite/static/style/hl.css +1 -0
  27. openwrite/static/style/style.css +611 -0
  28. openwrite/static/style/themes/light.css +28 -0
  29. openwrite/static/style/themes/obscura.css +27 -0
  30. openwrite/static/style/themes/stickynote.css +50 -0
  31. openwrite/static/style/themes/terminal.css +37 -0
  32. openwrite/static/style/themes/warmnight.css +73 -0
  33. openwrite/static/style/themes/windows98.css +132 -0
  34. openwrite/templates/admin.html +47 -0
  35. openwrite/templates/blog.html +16 -0
  36. openwrite/templates/blog_footer.html +6 -0
  37. openwrite/templates/blog_header.html +45 -0
  38. openwrite/templates/blog_preview.html +44 -0
  39. openwrite/templates/changepw.html +19 -0
  40. openwrite/templates/create.html +46 -0
  41. openwrite/templates/dashboard.html +45 -0
  42. openwrite/templates/discover.html +11 -0
  43. openwrite/templates/edit.html +190 -0
  44. openwrite/templates/footer.html +16 -0
  45. openwrite/templates/header.html +61 -0
  46. openwrite/templates/import.html +142 -0
  47. openwrite/templates/index.html +9 -0
  48. openwrite/templates/instances.html +14 -0
  49. openwrite/templates/login.html +18 -0
  50. openwrite/templates/new_post.html +207 -0
  51. openwrite/templates/post.html +53 -0
  52. openwrite/templates/preview.html +37 -0
  53. openwrite/templates/register.html +42 -0
  54. openwrite/templates/settings.html +63 -0
  55. openwrite/templates/stats.html +242 -0
  56. openwrite/utils/create_db.py +15 -0
  57. openwrite/utils/db.py +30 -0
  58. openwrite/utils/helpers.py +216 -0
  59. openwrite/utils/i18n.json +229 -0
  60. openwrite/utils/models.py +87 -0
  61. openwrite/utils/translations.py +6 -0
  62. openwrite-0.10.1.dist-info/METADATA +684 -0
  63. openwrite-0.10.1.dist-info/RECORD +67 -0
  64. openwrite-0.10.1.dist-info/WHEEL +5 -0
  65. openwrite-0.10.1.dist-info/entry_points.txt +2 -0
  66. openwrite-0.10.1.dist-info/licenses/LICENSE +661 -0
  67. openwrite-0.10.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,37 @@
1
+ body {
2
+ background: #000;
3
+ color: #FFF;
4
+ }
5
+
6
+ article.blog-content {
7
+ font-family: "Courier New";
8
+ }
9
+
10
+ h1.blog-name a {
11
+ font-family: "Courier New";
12
+ color: #00FF00;
13
+ }
14
+
15
+ h1.post-title {
16
+ font-family: "Courier New";
17
+ }
18
+
19
+ .header #menu a svg {
20
+ fill: #00FF00;
21
+ }
22
+
23
+ h1.blog-name a::before {
24
+ content: '> ';
25
+ }
26
+
27
+ .blog-content .post-title {
28
+ color: #545454;
29
+ }
30
+
31
+ i.post-date {
32
+ color: #545454;
33
+ }
34
+
35
+ article.post-content a {
36
+ color: #00FF00;
37
+ }
@@ -0,0 +1,73 @@
1
+ body {
2
+ background: #201917;
3
+ }
4
+
5
+ header.header {
6
+ border-bottom: unset;
7
+ }
8
+
9
+ .container {
10
+ max-width: 700px;
11
+ padding: 1rem 0.5rem;
12
+ font-family: 'Georgia', serif;
13
+ line-height: 1.6;
14
+ }
15
+
16
+ h1.blog-name a {
17
+ color: #202020;
18
+ }
19
+
20
+ #menu a svg {
21
+ fill: #F59D84;
22
+ }
23
+
24
+ article.post-content {
25
+ color: #F59D84;
26
+ }
27
+
28
+ article.blog-content {
29
+ border: 1px solid #F59D84;
30
+ padding: 2rem;
31
+ }
32
+
33
+ article.blog-content h3 {
34
+ color: #F59D84;
35
+ }
36
+
37
+ article.post-content a {
38
+ color: #ffa489;
39
+ text-decoration: underline;
40
+ }
41
+
42
+ article.post-content h1, article.post-content h2, article.post-content h3, article.post-content h4, article.post-content h5 {
43
+ color: #ffd2c5;
44
+ }
45
+
46
+ article.post-content blockquote {
47
+ color: #8d7068;
48
+ border-left: 4px solid #8d7068;
49
+ }
50
+
51
+ h1.post-title {
52
+ color: #fff;
53
+ }
54
+
55
+ h1.blog-name {
56
+ font-family: "Courier New", "Courier";
57
+ font-size: 18px;
58
+ background: #F59D84;
59
+ padding-left: 1rem;
60
+ padding-right: 1rem;
61
+ }
62
+
63
+ i.post-date {
64
+ color: #7c7c7c;
65
+ }
66
+
67
+ pre code.hljs {
68
+ background: #1A0E0B;
69
+ }
70
+
71
+ p code {
72
+ background: #ffffea;
73
+ }
@@ -0,0 +1,132 @@
1
+ @media only screen and (max-width: 850px) {
2
+ .header {
3
+ flex-direction: row;
4
+ }
5
+
6
+ .header #menu {
7
+ margin-top: unset;
8
+ }
9
+ }
10
+
11
+ body {
12
+ background: #ff00f7;
13
+ background: linear-gradient(180deg, rgba(255, 0, 247, 1) 0%, rgba(148, 230, 255, 1) 100%);
14
+ font-family: "Georgia", "Times New Roman";
15
+ background-attachment: fixed;
16
+ }
17
+
18
+ .container {
19
+ margin-top: 1rem;
20
+ background: #b7b7b7;
21
+ border: 2px solid #FFFFFF;
22
+ box-shadow:
23
+ 2px 2px 0 #000000,
24
+ 1px 1px 0 #FFFFFF,
25
+ inset 1px 1px 0 #FFFFFF,
26
+ inset -1px -1px 0 #A0A0A0;
27
+ }
28
+
29
+ h1.blog-name {
30
+ font-size: 12px;
31
+ margin-left: 10px;
32
+ margin-top: 4px;
33
+ }
34
+
35
+ h1.blog-name a {
36
+ color: #fff;
37
+ font-family: "Microsoft Sans Serif", "MS Sans Serif", sans-serif;
38
+ display: inline-flex;
39
+ align-items: center;
40
+ gap: 0.5rem;
41
+ text-decoration: none;
42
+ }
43
+
44
+ h1.blog-name a::before {
45
+ content: '';
46
+ display: inline-block;
47
+ width: 20px;
48
+ height: 20px;
49
+ background-image: url(https://openwrite.b-cdn.net/world-2.png);
50
+ background-size: contain;
51
+ background-repeat: no-repeat;
52
+ }
53
+
54
+ header.header {
55
+ height: 1rem;
56
+ background: #0000a3;
57
+ background: linear-gradient(90deg,rgba(0, 0, 163, 1) 0%, rgba(0, 195, 255, 1) 100%);
58
+ }
59
+
60
+ article.blog-content {
61
+ font-family: "Georgia", "Times New Roman";
62
+ color: #222;
63
+ padding: 1rem;
64
+ }
65
+
66
+ article.post-content {
67
+ color: #222;
68
+ }
69
+
70
+ article.post-content a {
71
+ color: #0000FF;
72
+ }
73
+
74
+ .header #menu a {
75
+ margin-right: 4px;
76
+ }
77
+
78
+ h1.post-title {
79
+ font-family: "Georgia", "Times New Roman";
80
+ }
81
+
82
+ h1.post-title::before {
83
+ content: '';
84
+ display: inline-block;
85
+ width: 32px;
86
+ height: 32px;
87
+ background-image: url(https://openwrite.b-cdn.net/write_file-1.png);
88
+ background-size: contain;
89
+ background-repeat: no-repeat;
90
+ }
91
+
92
+ p code {
93
+ background: #fff;
94
+ color: #000;
95
+ font-family: "Courier New", "Courier";
96
+ box-shadow: -2px -2px 0px 0px rgba(59,59,59,0.75) inset;
97
+ -webkit-box-shadow: -2px -2px 0px 0px rgba(59,59,59,0.75) inset;
98
+ -moz-box-shadow: -2px -2px 0px 0px rgba(59,59,59,0.75) inset;
99
+ display: inline-block;
100
+ padding: 2px;
101
+ }
102
+
103
+ i.post-date::before {
104
+ content: '';
105
+ display: inline-block;
106
+ width: 13px;
107
+ height: 13px;
108
+ background-image: url(https://openwrite.b-cdn.net/calendar-0.png);
109
+ background-size: contain;
110
+ background-repeat: no-repeat;
111
+ margin-right: 5px;
112
+ }
113
+
114
+ footer.footer {
115
+ border: none;
116
+ }
117
+
118
+ pre code.hljs {
119
+ background: #fff;
120
+ color: #000;
121
+ font-family: "Courier New", "Courier";
122
+ box-shadow: -2px -2px 0px 0px rgba(59,59,59,0.75) inset;
123
+ -webkit-box-shadow: -2px -2px 0px 0px rgba(59,59,59,0.75) inset;
124
+ -moz-box-shadow: -2px -2px 0px 0px rgba(59,59,59,0.75) inset;
125
+ display: block;
126
+ padding: 2px;
127
+ }
128
+
129
+ article.post-content blockquote {
130
+ color: #46608e;
131
+ border-left: 4px solid #46608e;
132
+ }
@@ -0,0 +1,47 @@
1
+ {% include 'header.html' %}
2
+ {% if error %}
3
+ <div class="error">{{ error }}</div>
4
+ {% endif %}
5
+ <h1 class="centered">Actions</h1>
6
+ <div style="display: flex; justify-content: center; gap: 1rem;">
7
+ <a style="width: 5rem;" class="btn purple" href="/admin/add_user">{{ g.trans['add_user'] }}</a>
8
+ <a style="width: 5rem;" class="btn purple" href="/admin/settings">Settings</a>
9
+ </div>
10
+ <h1 class="centered">Blogs</h1>
11
+ <table class="table">
12
+ <thead>
13
+ <th>Title</th><th>Name</th><th>Author</th><th>Actions</th>
14
+ </thead>
15
+ <tbody>
16
+ {% for blog in blogs %}
17
+ <tr><td>{{ blog.title }}</td><td>{{ blog.name }}</td><td>{{ blog.ownername }}</td><td><a class="action" href="/admin/delete_blog/{{ blog.name }}" onclick="return confirm('{{ g.trans['are_you_sure_blog'] }} {{ blog.name }}?\n{{ g.trans['this_action_cannot_be_reverted'] }}.')">Delete</a></td></tr>
18
+ {% endfor %}
19
+ </tbody>
20
+ </table>
21
+
22
+ <h1 class="centered">Users</h1>
23
+ <table class="table">
24
+ <thead>
25
+ <th>Username</th><th>Admin?</th><th>Actions</th>
26
+ </thead>
27
+ <tbody>
28
+ {% for user in users %}
29
+ <tr><td>{{ user.username }}</td>
30
+ <td>{% if user.admin > 0 %}{{ g.trans['yes'] }}{% else %}{{ g.trans['no'] }}{% endif %}</td>
31
+ <td>
32
+ <a class="action" href="/admin/delete_user/{{ user.username }}" onclick="return confirm('{{ g.trans['are_you_sure_user'] }} {{ user.username }}?\n{{ g.trans['this_action_cannot_be_reverted'] }}.')">Delete</a>
33
+ {% if user.admin > 0 %}
34
+ {% if user.username != "admin" %}
35
+ <a class="action" onclick="return confirm('{{ g.trans['are_you_sure_remove_admin'] }} {{ user.username }}?')" href="/admin/remove_admin/{{ user.username }}">{{ g.trans['remove_admin'] }}</a>
36
+ {% endif %}
37
+ {% else %}
38
+ <a class="action" onclick="return confirm('{{ g.trans['are_you_sure_make_admin'] }} {{ user.username }}?')" href="/admin/make_admin/{{ user.username }}">{{ g.trans['add_admin'] }}</a>
39
+ {% endif %}
40
+ </td>
41
+ </tr>
42
+ {% endfor %}
43
+ </tbody>
44
+ </table>
45
+
46
+
47
+ {% include 'footer.html' %}
@@ -0,0 +1,16 @@
1
+ {% include 'blog_header.html' %}
2
+ <article class="post-content">
3
+ {{ blog.description_html | safe }}
4
+ </article>
5
+ {% if posts | count > 0 %}
6
+ <hr>
7
+ <h3><b>{{ g.trans['posts'] }}</b></h3>
8
+ <article class="post-content" id="posts">
9
+ {% for post in posts %}
10
+ <div class="postlist_item">
11
+ <i class="datetime">{{ post.date }}</i> - {% if g.mode == "multi" %}<a href="{{ blog.url }}/{{ post.link }}">{{ post.title }}</a>{% else %}<a href="http://{{ g.main_domain }}/p/{{ post.link }}">{{ post.title }}</a>{% endif %}
12
+ </div>
13
+ {% endfor%}
14
+ </article>
15
+ {% endif %}
16
+ {% include 'blog_footer.html' %}
@@ -0,0 +1,6 @@
1
+ </article>
2
+ </div>
3
+ <footer class="blog footer">
4
+ Written with ♡ on<a target="_blank" href="https://openwrite.io/">openwrite.io</a>
5
+ </footer>
6
+ </body>
@@ -0,0 +1,45 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ {% if blog.index == "off" %}<meta name="robots" content="noindex, nofollow"> {% endif %}
7
+ <title>{% if post %}{{ post.title }} | {% endif %}{{ blog.title }}</title>
8
+ <meta name="description" content="Minimalist, opensource blog. Write, freely.">
9
+ <link rel="stylesheet" href="{{ url_for('static', filename='style/style.css') }}">
10
+ <link rel="stylesheet" href="{{ url_for('static', filename='style/hl.css') }}">
11
+ <link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
12
+ <script src="{{ url_for('static', filename='script/highlight.min.js') }}"></script>
13
+ <script nonce="{{ g.nonce }}">hljs.highlightAll();</script>
14
+ <script src="{{ url_for('static', filename='script/openwrite.js') }}"></script>
15
+ {% if blog.theme != "default" %}
16
+ <link rel="stylesheet" href="{{ url_for('static', filename='style/themes/'+blog.theme) }}.css">
17
+ {% endif %}
18
+ {% if blog.name == "themes-test" %}
19
+ <link rel="stylesheet" href="{{ url_for('static', filename='style/test.css') }}">
20
+ {% endif %}
21
+ {% if blog.css %}
22
+ <style>
23
+ {{ blog.css }}
24
+ </style>
25
+ {% endif%}
26
+ </head>
27
+ <body>
28
+ <div class="container">
29
+ <header class="header">
30
+ {% if g.mode == "multi" %}
31
+ <h1 class="blog-name"><a href="{{ blog.url }}"><span>{{ blog.title }}</span></a></h1>
32
+ {% else %}
33
+ <h1 class="blog-name"><a href="http://{{ g.main_domain }}"><span>{{ blog.title }}</span></a></h1>
34
+ {% endif %}
35
+ <div id="menu">
36
+ <a href="{{ blog.url }}/rss">
37
+ <svg fill="#FFFFFF" width="20px" height="20px" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg">
38
+ <path d="M20.885 30.996h-5.83c-0.037-7.745-6.306-14.014-14.047-14.052h-0.004v-5.83c10.958 0.055 19.826 8.924 19.881 19.877l0 0.005zM5.117 22.767c0.001 0 0.001 0 0.002 0 2.272 0 4.114 1.842 4.114 4.114s-1.842 4.114-4.114 4.114c-2.271 0-4.113-1.84-4.114-4.111v-0c0.003-2.271 1.842-4.112 4.112-4.118h0.001zM24.996 30.996c-0.064-13.225-10.767-23.928-23.986-23.993l-0.006-0v-5.998c16.531 0.082 29.91 13.461 29.991 29.984l0 0.008z"></path>
39
+
40
+ </svg>
41
+
42
+ </a>
43
+ </div>
44
+ </header>
45
+ <article class="blog-content">
@@ -0,0 +1,44 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>[preview] {{ data.title }}</title>
7
+ <meta name="description" content="Minimalist, opensource blog. Write, freely.">
8
+ <link rel="stylesheet" href="{{ url_for('static', filename='style/style.css') }}">
9
+ <link rel="stylesheet" href="{{ url_for('static', filename='style/hl.css') }}">
10
+ <link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}">
11
+ <script src="{{ url_for('static', filename='highlight.min.js') }}"></script>
12
+ <script nonce="{{ g.nonce }}">hljs.highlightAll();</script>
13
+ <script src="{{ url_for('static', filename='openwrite.js') }}"></script>
14
+ {% if data.theme != "default" %}
15
+ <link rel="stylesheet" href="{{ url_for('static', filename='style/themes/'+data.theme) }}.css">
16
+ {% endif %}
17
+ </head>
18
+ <body>
19
+ <div class="container">
20
+ <header class="header">
21
+ <h1 class="blog-name"><a href="#"><span>{{ data.title }}</span></a></h1>
22
+ <div id="menu">
23
+ </div>
24
+ </header>
25
+ <article class="blog-content">
26
+ <article class="post-content">
27
+ {{ data.content | safe }}
28
+ </article>
29
+ <hr>
30
+ <h3><b>{{ g.trans['posts'] }}</b></h3>
31
+ <article class="post-content">
32
+ <div class="postlist_item">
33
+ <i class="datetime">01.01.1970, 00:01</i>
34
+ -
35
+ <a href="#">{{ g.trans['post'] }} 1</a>
36
+ </div>
37
+ </article>
38
+ </article>
39
+ </div>
40
+ <footer class="footer">
41
+ Written with ♡ on <a target="_blank" href="https://{{ g.main_domain }}/">{{ g.main_domain }}</a>
42
+ </footer>
43
+ </body>
44
+ </html>
@@ -0,0 +1,19 @@
1
+ {% include 'header.html' %}
2
+
3
+ <h1 class="centered">{{ g.trans["change_password"] }}</h1>
4
+
5
+ <article class="main-content">
6
+ {% if error %}
7
+ <div class="error">{{ error }}</div>
8
+ {% endif %}
9
+ <form method="post" action="/dashboard/changepw">
10
+ <span class="form-field">{{ g.trans["current_password"] }}:</span>
11
+ <input type="password" name="current_pass">
12
+ <span class="form-field">{{ g.trans["new_password"] }}:</span>
13
+ <input type="password" name="new_pass">
14
+ <span class="form-field">{{ g.trans["password2"] }}:</span>
15
+ <input type="password" name="new_pass2">
16
+ <input type="submit" name="sub" value="{{ g.trans["change_password"] }}" class="btn purple" style="margin-top: 1rem;">
17
+ </form>
18
+ </article>
19
+ {% include 'footer.html' %}
@@ -0,0 +1,46 @@
1
+ {% include 'header.html' %}
2
+ <h1 class="centered">create a new blog</h1>
3
+ <article class="main-content">
4
+ {% if error %}
5
+ <div class="error">{{ error }}</div>
6
+ {% endif %}
7
+
8
+ <form method="post" action="/dashboard/create">
9
+ <span class="form-field">{{ g.trans['choose_blog_title'] }}:</span>
10
+ <input type="text" name="name" class="medium" placeholder="my awesome blog" required>
11
+ <span class="form-field">{{ g.trans['choose_blog_url'] }}:</span>
12
+ <input id="url" oninput="change_url();" type="text" name="url" class="medium" placeholder="my-awesome-blog" required>
13
+
14
+ <span class="form-field" style="margin-top: 2rem;">{{ g.trans['access_blog_how'] }}?</span>
15
+ <div class="horizontal-fields">
16
+ <input type="radio" id="domain" name="access" value="domain">
17
+ <label for="domain">{{ g.trans['domain_name'] }}: <i><span id="bname">my-awesome-blog</span>.{{ g.main_domain }}</i></label>
18
+ </div>
19
+ <div class="horizontal-fields">
20
+ <input type="radio" id="path" name="access" value="path" checked>
21
+ <label for="path">{{ g.trans['path'] }}: <i>{{ g.main_domain }}/b/<span id="bname2">my-awesome-blog</span></i></label>
22
+ </div>
23
+ <div class="horizontal-fields" style="margin-top: 2rem;">
24
+ <input type="checkbox" name="index" id="index">
25
+ <label for="index">{{ g.trans['index_blog'] }}?</label>
26
+ </div>
27
+ <input type="submit" value="{{ g.trans['create'] }}" class="btn purple">
28
+ </form>
29
+ </article>
30
+
31
+ <script nonce="{{ g.nonce }}">
32
+ function change_url() {
33
+ var url = document.querySelector("#url").value.trim().replaceAll(/[^a-zA-Z0-9]+/g, "-").replaceAll(".", "-").toLowerCase();
34
+ var bname = document.querySelector("#bname");
35
+ var bname2 = document.querySelector("#bname2");
36
+
37
+ if(url.length > 0) {
38
+ bname.innerText = url;
39
+ bname2.innerText = url;
40
+ } else {
41
+ bname.innerText = "my-awesome-blog";
42
+ bname2.innerText = "my-awesome-blog";
43
+ }
44
+ }
45
+ </script>
46
+ {% include 'footer.html' %}
@@ -0,0 +1,45 @@
1
+ {% include 'header.html' %}
2
+ {% if error %}
3
+ <div class="error">{{ error }}</div>
4
+ {% endif %}
5
+ {% if message %}
6
+ <div class="message">{{ message }}</div>
7
+ {% endif %}
8
+ <h1 class="centered">{% if g.mode == "single" %}{{ g.trans['your_blog'] }}{% else %}{{ g.trans['your_blogs'] }}{% endif %}</h1>
9
+ {% if g.mode == "multi" %}
10
+ <h2>{{ blogs | count }}/{{ g.blog_limit }}</h2>
11
+ {% endif %}
12
+ {% for blog in blogs %}
13
+ <div class="bloginfo">
14
+ {% if g.mode == "multi" %}
15
+ <h2><b>{{ blog.name }}</b></h2>
16
+ {% endif %}
17
+ <span class="bloginfo">{{ g.trans['title'] }}: {{ blog.title }}</span>
18
+ <span class="bloginfo">URL:
19
+ {% if g.mode == "multi" %}
20
+ <a href="{% if blog.access == "path" %}https://{{ g.main_domain }}/b/{{ blog.name }} {% else %}https://{{ blog.name }}.{{ g.main_domain }}/{% endif %}" target="_blank" style="text-decoration: none; color: #fff;">{% if blog.access == "path" %}https://{{ g.main_domain }}/b/{{ blog.name }} {% else %}https://{{ blog.name }}.{{ g.main_domain }}/{% endif %}</a>
21
+ {% else %}
22
+ <a style="text-decoration: none; color: #fff" href="http://{{ g.main_domain }}">{{ g.main_domain }}</a>
23
+ {% endif %}
24
+ </span>
25
+ <div class="buttons-list">
26
+ <a href="/dashboard/edit/{{ blog.name }}" class="btn empty">{{ g.trans['edit'] }}</a>
27
+ <a href="/dashboard/post/{{ blog.name }}" class="btn purple">{{ g.trans['new_post'] }}</a>
28
+ <a href="/dashboard/stats/{{ blog.name }}" class="btn purple">{{ g.trans['statistics'] }}</a>
29
+ {% if g.mode == "multi" %}
30
+ <a href="/dashboard/delete/{{ blog.name }}" class="btn red" onclick="return confirm('{{ g.trans['are_you_sure_blog'] }} {{ blog.name }}?\n{{ g.trans['this_action_cannot_be_reverted'] }}.')">{{g.trans['delete_blog'] }}</a>
31
+ {% endif %}
32
+ </div>
33
+ </div>
34
+ {% endfor %}
35
+ <h1 style="padding: 2rem;" class="centered">{{ g.trans['settings'] }}</h1>
36
+ <div class="buttons-list">
37
+ {% if g.mode == "multi" %}
38
+ {% if blogs | count < g.blog_limit | int %}
39
+ <a href="/dashboard/create" class="btn purple">{{ g.trans['create_new_blog'] }}</a>
40
+ {% endif %}
41
+ {% endif %}
42
+ <a class="btn purple" href="/dashboard/changepw">{{ g.trans['change_password'] }}</a>
43
+ <a class="btn purple" href="/dashboard/import">{{ g.trans['migrate_posts'] }}</a>
44
+ </div>
45
+ {% include 'footer.html' %}
@@ -0,0 +1,11 @@
1
+ {% include 'header.html' %}
2
+ <h1 class="centered">{{ g.trans['discover'] }}</h1>
3
+ <p style="margin-bottom: 2rem;">{{ g.trans['discover_text'] }}</p>
4
+ <div style="display: flex; flex-direction: column">
5
+ {% for p in posts %}
6
+ <div style="display: flex; flex-direction: row; gap: 1rem">
7
+ <i class="datetime">{{ p.date }}</i> - {{ p.blogname }} - <a class="action" href="{{ p.url }}" target="_blank">{{ p.title }}</a>
8
+ </div>
9
+ {% endfor %}
10
+ </div>
11
+ {% include 'footer.html' %}