1 {% set query=package.name %}
3 {% extends "base.html" %}
10 <meta name="og:title" content="{{ package.title }}"/>
11 <meta name="og:description" content="{{ package.short_desc }}"/>
12 <meta name="description" content="{{ package.short_desc }}"/>
13 <meta name="og:url" content="{{ package.getDetailsURL(absolute=True) }}"/>
14 {% if package.getMainScreenshotURL() %}
15 <meta name="og:image" content="{{ package.getMainScreenshotURL(absolute=True) }}"/>
20 {% if not package.license.is_foss and not package.media_license.is_foss and package.type != package.type.TXP %}
21 {% set package_warning="Non-free code and media" %}
22 {% elif not package.license.is_foss and package.type != package.type.TXP %}
23 {% set package_warning="Non-free code" %}
24 {% elif not package.media_license.is_foss %}
25 {% set package_warning="Non-free media" %}
27 {% set release = package.getDownloadRelease() %}
29 <header class="jumbotron pb-3"
30 style="background: linear-gradient(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.7)), url('{{ package.getMainScreenshotURL() }}');
31 background-size: cover;
32 background-repeat: no-repeat;
33 background-position: center;">
34 <div class="container">
35 <h1 class="display-3">
40 {{ package.short_desc }}
44 {% if package_warning %}
45 <a class="badge badge-danger" href="/help/non_free/">
46 <i class="fas fa-exclamation-circle" style="margin-right: 0.3em;"></i>
50 {% for warning in package.content_warnings %}
51 <a class="badge badge-warning" rel="nofollow" href="/help/content_flags/"
52 title="{{ warning.description }}">
53 <i class="fas fa-exclamation-circle" style="margin-right: 0.3em;"></i>
57 {% for t in package.tags %}
58 <a class="badge badge-primary" rel="nofollow"
59 title="{{ t.description or '' }}"
60 href="{{ url_for('packages.list_all', tag=t.name) }}">
66 <div class="info-row row" style="margin-top: 2rem;">
67 <div class="btn-group-horizontal col">
68 <a class="btn" href="{{ url_for('users.profile', username=package.author.username) }}">
69 <i class="fas fa-user"></i>
71 {{ package.author.display_name }}
74 <a class="btn" rel="nofollow" href="{{ package.getDownloadURL() }}">
75 <i class="fas fa-download"></i>
76 <span class="count">{{ package.downloads }}</span>
78 <a class="btn" href="{{ url_for('threads.list_all', pid=package.id) }}">
79 <i class="fas fa-comment-alt"></i>
80 <span class="count">{{ threads | length }}</span>
82 {% if package.website %}
83 <a class="btn" href="{{ package.website }}">
84 <i class="fas fa-globe-europe"></i>
85 <span class="count">{{ _("Website") }}</span>
89 <a class="btn" href="{{ package.repo }}">
90 <i class="fas fa-code"></i>
91 <span class="count">{{ _("Source") }}</span>
94 {% if package.forums %}
95 <a class="btn" href="https://forum.minetest.net/viewtopic.php?t={{ package.forums }}">
96 <i class="fas fa-comments"></i>
97 <span class="count">{{ _("Forums") }}</span>
100 {% if package.issueTracker %}
101 <a class="btn" href="{{ package.issueTracker }}">
102 <i class="fas fa-bug"></i>
103 <span class="count">{{ _("Issue Tracker") }}</span>
107 {% if release and (release.min_rel or release.max_rel) %}
108 <div class="btn col-md-auto">
109 <img src="https://www.minetest.net/media/icon.svg" style="max-height: 1.2em;">
111 {% if release.min_rel and release.max_rel %}
112 {{ _("%(min)s - %(max)s", min=release.min_rel.name, max=release.max_rel.name) }}
113 {% elif release.min_rel %}
114 {{ _("%(min)s and above", min=release.min_rel.name) }}
115 {% elif release.max_rel %}
116 {{ _("%(max)s and below", max=release.max_rel.name) }}
121 <div class="btn-group-horizontal col-md-auto">
123 <a class="btn btn-download btn_green" rel="nofollow"
124 href="{{ package.getDownloadURL() }}">
129 {{ _("No downloads available") }}
137 {% if not package.approved %}
138 <aside class="container mt-4">
139 {% from "macros/package_approval.html" import render_banners %}
140 {{ render_banners(package, current_user, topic_error, topic_error_lvl, similar_topics) }}
142 {% if review_thread and review_thread.checkPerm(current_user, "SEE_THREAD") %}
143 <h2>{% if review_thread.private %}🔒{% endif %} {{ review_thread.title }}</h2>
144 {% if review_thread.private %}
146 This thread is only visible to the package owner and users of
147 Editor rank or above.
151 {% from "macros/threads.html" import render_thread %}
152 {{ render_thread(review_thread, current_user) }}
157 <main class="container mt-4">
158 <aside class="float-right ml-4" style="width: 18rem;">
159 <div class="card mb-4">
160 <div class="card-header">
162 <div class="btn-group float-right">
163 {% if package.checkPerm(current_user, "EDIT_PACKAGE") %}
164 <a class="btn btn-primary btn-sm ml-1" href="{{ package.getEditURL() }}"><i class="fas fa-edit"></i></a>
166 {# {% if current_user.is_authenticated %}
167 <a class="btn btn-primary btn-sm ml-1" href="{{ package.getCreateEditRequestURL() }}">Suggest Changes</a>
169 {% if package.checkPerm(current_user, "DELETE_PACKAGE") or package.checkPerm(current_user, "UNAPPROVE_PACKAGE") %}
170 <a class="btn btn-danger btn-sm ml-1" href="{{ package.getRemoveURL() }}"><i class="fas fa-trash"></i></a>
175 {% if package_warning %}
176 <div class="card-body">
177 <div class="alert alert-danger">
178 <a href="/help/non_free/" class="float-right">Info</a>
179 <b>Warning:</b> {{ package_warning }}
183 <table class="table">
186 <td>{{ package.type.value }}</td>
190 <td>{{ package.name }}</td>
192 {% if package.provides %}
195 <td>{% for meta in package.provides %}
196 <a class="badge badge-primary"
197 href="{{ url_for('metapackages.view', name=meta.name) }}">{{ meta.name }}</a>
204 {% if package.license == package.media_license %}
205 {{ package.license.name }}
206 {% elif package.type == package.type.TXP %}
207 {{ package.media_license.name }}
209 {{ package.license.name }} for code,<br />
210 {{ package.media_license.name }} for media.
216 <td>{{ package.created_at | datetime }}</td>
221 {% if package.checkPerm(current_user, "EDIT_MAINTAINERS") %}
222 <a class="btn btn-primary btn-sm ml-1 float-right" href="{{ package.getEditMaintainersURL() }}"><i class="fas fa-edit"></i></a>
225 {% for user in package.maintainers %}
226 <a class="badge badge-primary"
227 href="{{ url_for('users.profile', username=user.username) }}">
228 {{ user.display_name }}
232 {% if current_user in package.maintainers and current_user != package.author %}
233 <form class="mt-2" method="post" action="{{ package.getRemoveSelfMaintainerURL() }}">
234 <input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
235 <input class="btn btn-sm btn-link p-0" type="submit" value="{{ _("Remove myself") }}" />
243 {% if package.author.donate_url %}
244 <div class="alert alert-secondary">
245 Like {{ package.author.display_name }}'s work?
246 <a href="{{ package.author.donate_url }}" rel="nofollow">Donate now!</a>
250 {% if package.type != package.type.TXP %}
251 <div class="card my-4">
252 <div class="card-header">Dependencies</div>
253 <div class="card-body">
254 <div class="card-subtitle mb-2 text-muted">{{ _("Required") }}</div>
255 {% for dep in package.getSortedHardDependencies() %}
256 {%- if dep.package %}
257 <div </div class="badge badge-primary"
258 href="{{ dep.package.getDetailsURL() }}">
259 {{ dep.package.title }} by {{ dep.package.author.display_name }}
260 {% elif dep.meta_package %}
261 <a class="badge badge-primary"
262 href="{{ url_for('metapackages.view', name=dep.meta_package.name) }}">
263 {{ dep.meta_package.name }}
265 {{ "Excepted package or meta_package in dep!" | throw }}
268 <i>No required dependencies</i>
271 {% set optional_deps=package.getSortedOptionalDependencies() %}
272 {% if optional_deps %}
273 <div class="card-subtitle my-2 text-muted">{{ _("Optional") }}</div>
274 {% for dep in optional_deps %}
275 {%- if dep.package %}
276 <a class="badge badge-secondary"
277 href="{{ dep.package.getDetailsURL() }}">
278 {{ dep.package.title }} by {{ dep.package.author.display_name }}
279 {% elif dep.meta_package %}
280 <a class="badge badge-secondary"
281 href="{{ url_for('metapackages.view', name=dep.meta_package.name) }}">
282 {{ dep.meta_package.name }}
284 {{ "Excepted package or meta_package in dep!" | throw }}
292 <div class="card my-4">
293 <div class="card-header">
295 {% if package.checkPerm(current_user, "MAKE_RELEASE") %}
296 <div class="btn-group float-right">
297 <a class="btn btn-primary btn-sm ml-1" href="{{ package.getBulkReleaseURL() }}">
298 <i class="fas fa-wrench"></i>
301 <a class="btn btn-primary btn-sm ml-1" href="{{ package.getCreateReleaseURL() }}"><i class="fas fa-plus"></i></a>
305 <ul class="list-group list-group-flush">
306 {% for rel in releases %}
307 {% if rel.approved or package.checkPerm(current_user, "MAKE_RELEASE") or package.checkPerm(current_user, "APPROVE_RELEASE") %}
308 <li class="list-group-item">
310 {% if package.checkPerm(current_user, "MAKE_RELEASE") or package.checkPerm(current_user, "APPROVE_RELEASE") %}
311 <a class="btn btn-sm btn-primary float-right" href="{{ rel.getEditURL() }}">Edit
312 {% if not rel.task_id and not rel.approved and package.checkPerm(current_user, "APPROVE_RELEASE") %}
318 {% if not rel.approved %}<i>{% endif %}
320 <a href="{{ rel.getDownloadURL() }}" rel="nofollow">{{ rel.title }}</a>
322 <span style="color:#ddd;">
323 {% if rel.min_rel and rel.max_rel %}
324 [MT {{ rel.min_rel.name }}-{{ rel.max_rel.name }}]
325 {% elif rel.min_rel %}
326 [MT {{ rel.min_rel.name }}+]
327 {% elif rel.max_rel %}
328 [MT ≤{{ rel.max_rel.name }}]
334 <small style="color:#999;">
335 {% if rel.commit_hash %}
336 [{{ rel.commit_hash | truncate(5, end='') }}]
339 created {{ rel.releaseDate | date }}.
341 {% if (package.checkPerm(current_user, "MAKE_RELEASE") or package.checkPerm(current_user, "APPROVE_RELEASE")) and rel.task_id %}
342 <a href="{{ url_for('tasks.check', id=rel.task_id, r=package.getDetailsURL()) }}">Importing...</a>
343 {% elif not rel.approved %}
344 Waiting for approval.
347 {% if not rel.approved %}</i>{% endif %}
352 <li class="list-group-item">No releases available.</li>
357 <div class="card my-4">
358 <div class="card-header">
359 {% if package.approved and package.checkPerm(current_user, "CREATE_THREAD") %}
360 <div class="btn-group float-right">
361 <a class="btn btn-primary btn-sm mx-1" href="{{ url_for('threads.new', pid=package.id) }}"><i class="fas fa-plus"></i></a>
366 <ul class="list-group list-group-flush">
367 {% from "macros/threads.html" import render_threadlist %}
368 {{ render_threadlist(threads, compact=True) }}
372 {% if package.approved and package.checkPerm(current_user, "CREATE_THREAD") and current_user != package.author and not current_user.rank.atLeast(current_user.rank.EDITOR) %}
373 <a class="float-right"
374 href="{{ url_for('threads.new', pid=package.id) }}">
375 Report a problem with this listing
380 <ul class="screenshot_list mb-4">
381 {% for ss in package.screenshots %}
382 {% if ss.approved or package.checkPerm(current_user, "ADD_SCREENSHOTS") %}
384 <a href="{% if package.checkPerm(current_user, 'ADD_SCREENSHOTS') %}{{ ss.getEditURL() }}{% else %}{{ ss.url }}{% endif %}">
385 <img src="{{ ss.getThumbnailURL() }}" alt="{{ ss.title }}" />
390 {% if package.checkPerm(current_user, "EDIT_PACKAGE") %}
392 <a href="{{ package.getNewScreenshotURL() }}">
393 <div class="fas fa-plus screenshot-add"></div>
399 {{ package.desc | markdown }}
401 <div style="clear: both;"></div>
403 <h3>Ratings and Reviews</h3>
405 {% from "macros/reviews.html" import render_reviews, render_review_form, render_review_preview %}
406 {% if current_user.is_authenticated %}
409 <a class="btn btn-primary" href="{{ package.getReviewURL() }}">
410 {{ _("Edit Review") }}
413 {% elif current_user in package.maintainers %}
415 {{ _("You can't review your own package.") }}
418 {{ render_review_preview(package) }}
421 {{ render_review_preview(package) }}
423 {{ render_reviews(package.reviews, current_user) }}
426 {% if current_user.is_authenticated or requests %}
427 <h3>Edit Requests</h3>
430 {% for r in requests %}
432 <a href="{{ r.getURL() }}">{{ r.title }}</a>
434 <a href="{{ url_for('users.profile', username=r.author.username) }}">{{ r.author.display_name }}</a>
437 <li>No edit requests have been made.</li>
443 {% if alternatives %}
446 {% from "macros/packagegridtile.html" import render_pkggrid %}
447 {{ render_pkggrid(alternatives) }}
450 {% if similar_topics %}
451 <h3>Similar Forum Topics</h3>
453 {% for t in similar_topics %}
456 <a href="https://forum.minetest.net/viewtopic.php?t={{ t.topic_id }}">
457 {{ t.title }} by {{ t.author.display_name }}
459 {% if t.wip %}[WIP]{% endif %}
465 {% if package.checkPerm(current_user, "EDIT_PACKAGE") %}
469 {{ package.makeShield("title") | markdown }}
473 <pre><code>{{ package.makeShield("title") }}</code></pre>
477 {{ package.makeShield("downloads") | markdown }}
481 <pre><code>{{ package.makeShield("downloads") }}</code></pre>