Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aad4fd2a70 | ||
|
|
d2bda0fded | ||
|
|
b84727b187 | ||
|
|
6fd36dbfff | ||
|
|
8e134a7c85 | ||
|
|
389258a10c | ||
|
|
3657316fa2 | ||
|
|
a6f4249afb | ||
|
|
70afb94d3b | ||
|
|
8984adaa72 | ||
|
|
c523624696 | ||
|
|
072f189006 | ||
|
|
9967101d9f | ||
|
|
1ed09b646b | ||
|
|
f554bfc92b | ||
|
|
c80ea2c1b1 | ||
|
|
edd51b86d0 | ||
|
|
944b8a4eb0 | ||
|
|
a627893355 | ||
|
|
1600687449 | ||
|
|
fa2f17526f | ||
|
|
002e6828b6 | ||
|
|
a947472c67 | ||
|
|
e7acd7faa3 | ||
|
|
f755c7d429 | ||
|
|
b6652547fa | ||
|
|
be20146f25 | ||
|
|
df291db69b | ||
|
|
63a3b5e872 | ||
|
|
6353ac29e9 | ||
|
|
a4b583bac5 | ||
|
|
52fdc8c212 | ||
|
|
7e80adad56 | ||
|
|
bf5080aa18 | ||
|
|
89f95a22dc | ||
|
|
f1b21b73b2 | ||
|
|
6a13dca2d5 | ||
|
|
048b604a75 | ||
|
|
f7bb29c839 | ||
|
|
ba506cb16d | ||
|
|
179d0be933 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,6 +6,7 @@ tmp
|
||||
log.txt
|
||||
*.rdb
|
||||
uploads
|
||||
thumbnails
|
||||
|
||||
# Created by https://www.gitignore.io/api/linux,macos,python,windows
|
||||
|
||||
|
||||
@@ -60,5 +60,5 @@ rm db.sqlite && python setup.py -t && FLASK_CONFIG=../config.cfg FLASK_APP=app/_
|
||||
FLASK_CONFIG=../config.cfg FLASK_APP=app/__init__.py flask db migrate
|
||||
|
||||
# Run migration
|
||||
FLASK_CONFIG=../config.cfg FLASK_APP=app/__init__.py flask db migrate
|
||||
FLASK_CONFIG=../config.cfg FLASK_APP=app/__init__.py flask db upgrade
|
||||
```
|
||||
|
||||
@@ -2,3 +2,4 @@ title: Help
|
||||
|
||||
* [Package Tags](package_tags)
|
||||
* [Ranks and Permissions](ranks_permissions)
|
||||
* [Reporting Content](reporting)
|
||||
|
||||
@@ -193,7 +193,7 @@ title: Ranks and Permissions
|
||||
<th></th>
|
||||
<th>✓</th> <!-- member -->
|
||||
<th></th>
|
||||
<th></th> <!-- trusted member -->
|
||||
<th>✓</th> <!-- trusted member -->
|
||||
<th></th>
|
||||
<th>✓</th> <!-- editor -->
|
||||
<th></th>
|
||||
|
||||
8
app/flatpages/help/reporting.md
Normal file
8
app/flatpages/help/reporting.md
Normal file
@@ -0,0 +1,8 @@
|
||||
title: Reporting Content
|
||||
|
||||
Please let us know if anything on the ContentDB violates our rules or any applicable
|
||||
laws.
|
||||
|
||||
We take copyright violation and other offenses very seriously.
|
||||
|
||||
<a href="https://rubenwardy.com/contact/" class="button btn_green">Contact</a>
|
||||
105
app/flatpages/policy_and_guidance.md
Normal file
105
app/flatpages/policy_and_guidance.md
Normal file
@@ -0,0 +1,105 @@
|
||||
title: Package Inclusion Policy and Guidance
|
||||
|
||||
<div class="box box_grey alert alert-warning">
|
||||
<b>Note:</b> This is a draft
|
||||
</div>
|
||||
|
||||
## 1. General
|
||||
|
||||
It is not permitted to submit abusive, obscene, vulgar, slanderous, hateful,
|
||||
threatening, sexually-orientated or any material that may violate any laws be
|
||||
it of your country, the country where "Content DB” is hosted or International Law.
|
||||
|
||||
The ContentDB admin reserves the right to remove packages for any reason,
|
||||
including ones not covered by this document, and to ban users who abuse this service.
|
||||
|
||||
Also see the [help page on tags](/help/package_tags/).
|
||||
|
||||
|
||||
## 2. Accepted Content and State of Completion
|
||||
|
||||
The submission of malware is strictly prohibited. This includes software which
|
||||
does not do as it advertises, for example if it posts telemetry without stating
|
||||
clearly that it does in the package meta.
|
||||
|
||||
ContentDB should only currently contain playable content, ie: stuff that would
|
||||
be in Mod Releases and Game Releases. Please don't upload any Work In Progress (WIP)
|
||||
things. This will probably change in future if/when an "early access" feature is
|
||||
added.
|
||||
|
||||
Adding non-player facing mods, such as libraries and server tools, is perfectly fine.
|
||||
ContentDB isn't just for player-facing things, and adding libraries allows them to be
|
||||
installed when a mod depends on it.
|
||||
|
||||
|
||||
## 3. Technical Names
|
||||
|
||||
### 3.1 Right to a name
|
||||
|
||||
The first package to use a name based on the creation of its forum topic or
|
||||
contentdb submission has the right to the technical name. The use of a package
|
||||
on a server or in private doesn't reserve its name. No other packages of the same
|
||||
type may use the same name, except for the exception given by 2.2.
|
||||
|
||||
If it turns out that we made a mistake by approving a package and that the
|
||||
name should have been given to another package, then we *may* unapprove the
|
||||
package and give the name to the correct one.
|
||||
|
||||
If you submit a package where you don't have the right to the name you will be asked
|
||||
to change the name of the package, or your package won't be accepted.
|
||||
|
||||
### 3.2 Mod Forks and Reimplementations
|
||||
|
||||
An exception to the above is that mods are allowed to have the same name as a
|
||||
mod if its a fork of that mod (or a close reimplementation). In real terms, it
|
||||
should be possible to use the new mod as a drop-in replacement.
|
||||
|
||||
We reserve the right to decide whether a mod counts as a fork or
|
||||
reimplementation of the mod that owns the name.
|
||||
|
||||
|
||||
## 4. Licenses
|
||||
|
||||
### 4.1 Allowed Licenses
|
||||
|
||||
Please ensure that you correctly credit any resources (code, assets, or otherwise)
|
||||
that you have used in your package.
|
||||
|
||||
**The use of licenses which do not allow derivatives or redistribution is not
|
||||
permitted. This includes CC-ND (No-Derivatives) and lots of closed source licenses.**
|
||||
|
||||
However, closed sourced licenses are allowed if they allow the above.
|
||||
|
||||
If the license you use is not on the list then please choose the correct "Other"
|
||||
option.
|
||||
|
||||
Please note that the definitions of "free" and "non-free" is the same as that
|
||||
of the [Free Software Foundation](https://www.gnu.org/philosophy/free-sw.en.html).
|
||||
|
||||
### 4.2 Recommended Licenses
|
||||
|
||||
It is recommended that you use a proper license for code with a warranty
|
||||
disclaimer, such as the (L)GPL or MIT. You should also use a proper media license
|
||||
for media, such as a Creative Commons license.
|
||||
|
||||
The use of WTFPL is discouraged as it doesn't contain a valid warranty disclaimer,
|
||||
and also includes swearing which dissuades teachers from using your content.
|
||||
|
||||
Public domain is not a valid license in many countries, please use CC0 or MIT instead.
|
||||
|
||||
|
||||
## 5. Promotions and Advertisements (inc. asking for donations)
|
||||
|
||||
Any information other than the long description - including screenshots - must
|
||||
not contain any promotions or advertisements. This includes asking for donations,
|
||||
promoting online shops, or linking to personal websites and social media.
|
||||
|
||||
ContentDB is for the community. We may remove any promotions if we feel that
|
||||
they're inappropriate.
|
||||
|
||||
Paid promotions are not allowed at all, anywhere.
|
||||
|
||||
|
||||
## 6. Reporting Violations
|
||||
|
||||
See the [Reporting Content](/help/reporting/) page.
|
||||
@@ -96,15 +96,15 @@ class User(db.Model, UserMixin):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
# User authentication information
|
||||
username = db.Column(db.String(50), nullable=False, unique=True)
|
||||
password = db.Column(db.String(255), nullable=False, server_default="")
|
||||
username = db.Column(db.String(50, collation="NOCASE"), nullable=False, unique=True, index=True)
|
||||
password = db.Column(db.String(255), nullable=True)
|
||||
reset_password_token = db.Column(db.String(100), nullable=False, server_default="")
|
||||
|
||||
rank = db.Column(db.Enum(UserRank))
|
||||
|
||||
# Account linking
|
||||
github_username = db.Column(db.String(50), nullable=True, unique=True)
|
||||
forums_username = db.Column(db.String(50), nullable=True, unique=True)
|
||||
github_username = db.Column(db.String(50, collation="NOCASE"), nullable=True, unique=True)
|
||||
forums_username = db.Column(db.String(50, collation="NOCASE"), nullable=True, unique=True)
|
||||
|
||||
# User email information
|
||||
email = db.Column(db.String(255), nullable=True, unique=True)
|
||||
@@ -121,12 +121,15 @@ class User(db.Model, UserMixin):
|
||||
packages = db.relationship("Package", backref="author", lazy="dynamic")
|
||||
requests = db.relationship("EditRequest", backref="author", lazy="dynamic")
|
||||
|
||||
def __init__(self, username):
|
||||
def __init__(self, username, active=False, email=None, password=None):
|
||||
import datetime
|
||||
|
||||
self.username = username
|
||||
self.confirmed_at = datetime.datetime.now() - datetime.timedelta(days=6000)
|
||||
self.display_name = username
|
||||
self.active = active
|
||||
self.email = email
|
||||
self.password = password
|
||||
self.rank = UserRank.NOT_JOINED
|
||||
|
||||
def canAccessTodoList(self):
|
||||
@@ -181,12 +184,13 @@ class Notification(db.Model):
|
||||
|
||||
|
||||
class License(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(50), nullable=False, unique=True)
|
||||
packages = db.relationship("Package", backref="license", lazy="dynamic")
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
name = db.Column(db.String(50), nullable=False, unique=True)
|
||||
is_foss = db.Column(db.Boolean, nullable=False, default=True)
|
||||
|
||||
def __init__(self, v):
|
||||
def __init__(self, v, is_foss=True):
|
||||
self.name = v
|
||||
self.is_foss = is_foss
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
@@ -213,18 +217,19 @@ class PackageType(enum.Enum):
|
||||
|
||||
|
||||
class PackagePropertyKey(enum.Enum):
|
||||
name = "Name"
|
||||
title = "Title"
|
||||
shortDesc = "Short Description"
|
||||
desc = "Description"
|
||||
type = "Type"
|
||||
license = "License"
|
||||
tags = "Tags"
|
||||
provides = "Provides"
|
||||
repo = "Repository"
|
||||
website = "Website"
|
||||
issueTracker = "Issue Tracker"
|
||||
forums = "Forum Topic ID"
|
||||
name = "Name"
|
||||
title = "Title"
|
||||
shortDesc = "Short Description"
|
||||
desc = "Description"
|
||||
type = "Type"
|
||||
license = "License"
|
||||
media_license = "Media License"
|
||||
tags = "Tags"
|
||||
provides = "Provides"
|
||||
repo = "Repository"
|
||||
website = "Website"
|
||||
issueTracker = "Issue Tracker"
|
||||
forums = "Forum Topic ID"
|
||||
|
||||
def convert(self, value):
|
||||
if self == PackagePropertyKey.tags:
|
||||
@@ -324,7 +329,10 @@ class Package(db.Model):
|
||||
type = db.Column(db.Enum(PackageType))
|
||||
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
|
||||
|
||||
license_id = db.Column(db.Integer, db.ForeignKey("license.id"))
|
||||
license_id = db.Column(db.Integer, db.ForeignKey("license.id"), nullable=False, default=1)
|
||||
license = db.relationship("License", foreign_keys=[license_id])
|
||||
media_license_id = db.Column(db.Integer, db.ForeignKey("license.id"), nullable=False, default=1)
|
||||
media_license = db.relationship("License", foreign_keys=[media_license_id])
|
||||
|
||||
approved = db.Column(db.Boolean, nullable=False, default=False)
|
||||
soft_deleted = db.Column(db.Boolean, nullable=False, default=False)
|
||||
@@ -336,7 +344,7 @@ class Package(db.Model):
|
||||
forums = db.Column(db.Integer, nullable=True)
|
||||
|
||||
provides = db.relationship("MetaPackage", secondary=provides, lazy="subquery",
|
||||
backref=db.backref("packages", lazy=True))
|
||||
backref=db.backref("packages", lazy="dynamic"))
|
||||
|
||||
dependencies = db.relationship("Dependency", backref="depender", lazy="dynamic", foreign_keys=[Dependency.depender_id])
|
||||
|
||||
@@ -364,6 +372,7 @@ class Package(db.Model):
|
||||
setattr(self, e.name, getattr(package, e.name))
|
||||
|
||||
def getAsDictionary(self, base_url):
|
||||
tnurl = self.getThumbnailURL()
|
||||
return {
|
||||
"name": self.name,
|
||||
"title": self.title,
|
||||
@@ -374,9 +383,18 @@ class Package(db.Model):
|
||||
"repo": self.repo,
|
||||
"url": base_url + self.getDownloadURL(),
|
||||
"release": self.getDownloadRelease().id if self.getDownloadRelease() is not None else None,
|
||||
"screenshots": [base_url + ss.url for ss in self.screenshots]
|
||||
"screenshots": [base_url + ss.url for ss in self.screenshots],
|
||||
"thumbnail": (base_url + tnurl) if tnurl is not None else None
|
||||
}
|
||||
|
||||
def getThumbnailURL(self):
|
||||
screenshot = self.screenshots.filter_by(approved=True).first()
|
||||
return screenshot.getThumbnailURL() if screenshot is not None else None
|
||||
|
||||
def getMainScreenshotURL(self):
|
||||
screenshot = self.screenshots.filter_by(approved=True).first()
|
||||
return screenshot.url if screenshot is not None else None
|
||||
|
||||
def getDetailsURL(self):
|
||||
return url_for("package_page",
|
||||
author=self.author.username, name=self.name)
|
||||
@@ -409,10 +427,6 @@ class Package(db.Model):
|
||||
return url_for("package_download_page",
|
||||
author=self.author.username, name=self.name)
|
||||
|
||||
def getMainScreenshotURL(self):
|
||||
screenshot = self.screenshots.filter_by(approved=True).first()
|
||||
return screenshot.url if screenshot is not None else None
|
||||
|
||||
def getDownloadRelease(self):
|
||||
for rel in self.releases:
|
||||
if rel.approved:
|
||||
@@ -575,7 +589,7 @@ class PackageScreenshot(db.Model):
|
||||
id=self.id)
|
||||
|
||||
def getThumbnailURL(self):
|
||||
return self.url # TODO
|
||||
return self.url.replace("/uploads/", "/thumbnails/350x233/")
|
||||
|
||||
class EditRequest(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
@@ -664,6 +678,25 @@ class EditRequestChange(db.Model):
|
||||
else:
|
||||
setattr(package, self.key.name, self.newValue)
|
||||
|
||||
|
||||
|
||||
class KrockForumTopic(db.Model):
|
||||
topic_id = db.Column(db.Integer, primary_key=True, autoincrement=False)
|
||||
author_id = db.Column(db.Integer, db.ForeignKey("user.id"), nullable=False)
|
||||
author = db.relationship("User")
|
||||
|
||||
ttype = db.Column(db.Integer, nullable=False)
|
||||
title = db.Column(db.String(200), nullable=False)
|
||||
name = db.Column(db.String(30), nullable=True)
|
||||
link = db.Column(db.String(200), nullable=True)
|
||||
|
||||
def getType(self):
|
||||
if self.ttype == 1 or self.ttype == 2:
|
||||
return PackageType.MOD
|
||||
elif self.ttype == 6:
|
||||
return PackageType.GAME
|
||||
|
||||
|
||||
# Setup Flask-User
|
||||
db_adapter = SQLAlchemyAdapter(db, User) # Register the User model
|
||||
user_manager = UserManager(db_adapter, app) # Initialize Flask-User
|
||||
|
||||
@@ -25,7 +25,7 @@ a:hover {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.box h2, .box h3 {
|
||||
.box > h2, .box > h3 {
|
||||
margin: 0;
|
||||
padding: 0.5em 0.5em 0.5em 15px;
|
||||
border-bottom: 1px solid #444;
|
||||
@@ -235,43 +235,51 @@ select:not([multiple]) {
|
||||
|
||||
/* Alerts */
|
||||
|
||||
.alert {
|
||||
padding: 10px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.alert .alert_right, .alert > form {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.alert .alert_right form {
|
||||
height: 100%;
|
||||
}
|
||||
.alert {
|
||||
padding: 10px;
|
||||
position: relative;
|
||||
|
||||
.alert form {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.alert_right:not(.button) {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.alert input {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
background: 0;
|
||||
border: 0;
|
||||
border-left: 1px solid rgba(255,255,255,0.12);
|
||||
border-radius: 0;
|
||||
}
|
||||
.alert_right form {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.alert input:hover {
|
||||
border: 0;
|
||||
border-left: 1px solid rgba(255,255,255,0.2);
|
||||
form {
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
input, .button {
|
||||
margin: 0;
|
||||
background: 0;
|
||||
border: 0;
|
||||
border-left: 1px solid rgba(255,255,255,0.12);
|
||||
border-radius: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
input:hover, .button:hover {
|
||||
border: 0;
|
||||
border-left: 1px solid rgba(255,255,255,0.2);
|
||||
}
|
||||
}
|
||||
|
||||
#alerts {
|
||||
@@ -280,6 +288,7 @@ select:not([multiple]) {
|
||||
bottom: 15px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
#alerts .alert {
|
||||
@@ -383,6 +392,10 @@ table.fancyTable tfoot td {
|
||||
color: #b6f;
|
||||
}
|
||||
|
||||
.TRUSTED_MEMBER a, a.TRUSTED_MEMBER {
|
||||
color: #2c2;
|
||||
}
|
||||
|
||||
/*
|
||||
Aside
|
||||
*/
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
display: block;
|
||||
min-width: 300px;
|
||||
min-height: 200px;
|
||||
max-width: 332px;
|
||||
padding: 0;
|
||||
margin: 7px;
|
||||
}
|
||||
|
||||
@@ -75,3 +75,47 @@ def importUsersFromModList():
|
||||
db.session.commit()
|
||||
for author in found:
|
||||
checkForumAccount.delay(author, None)
|
||||
|
||||
|
||||
BANNED_NAMES = ["mod", "game", "old", "outdated", "wip", "api"]
|
||||
ALLOWED_TYPES = [1, 2, 6]
|
||||
|
||||
@celery.task()
|
||||
def importKrocksModList():
|
||||
contents = urllib.request.urlopen("http://krock-works.16mb.com/MTstuff/modList.php").read().decode("utf-8")
|
||||
list = json.loads(contents)
|
||||
username_to_user = {}
|
||||
|
||||
KrockForumTopic.query.delete()
|
||||
|
||||
for x in list:
|
||||
type = int(x["type"])
|
||||
if not type in ALLOWED_TYPES:
|
||||
continue
|
||||
|
||||
username = x["author"]
|
||||
user = username_to_user.get(username)
|
||||
if user is None:
|
||||
user = User.query.filter_by(forums_username=username).first()
|
||||
assert(user is not None)
|
||||
username_to_user[username] = user
|
||||
|
||||
import re
|
||||
tags = re.findall("\[([a-z0-9_]+)\]", x["title"])
|
||||
name = None
|
||||
for tag in reversed(tags):
|
||||
if len(tag) < 30 and not tag in BANNED_NAMES and \
|
||||
not re.match("^([a-z][0-9]+)$", tag):
|
||||
name = tag
|
||||
break
|
||||
|
||||
topic = KrockForumTopic()
|
||||
topic.topic_id = x["topicId"]
|
||||
topic.author_id = user.id
|
||||
topic.ttype = type
|
||||
topic.title = x["title"]
|
||||
topic.name = name
|
||||
topic.link = x.get("link")
|
||||
db.session.add(topic)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<select name="action">
|
||||
<option value="importusers">Create users from mod list</option>
|
||||
<option value="importmodlist">Import Krock's mod list</option>
|
||||
<option value="importscreenshots" selected>Import screenshots from VCS</option>
|
||||
<option value="importdepends">Import dependencies from downloads</option>
|
||||
<option value="modprovides">Set provides to mod name</option>
|
||||
|
||||
@@ -103,8 +103,9 @@
|
||||
{% endblock %}
|
||||
|
||||
<footer>
|
||||
Copyright © 2018 to <a href="https://rubenwardy.com/">rubenwardy</a> |
|
||||
ContentDB © 2018 to <a href="https://rubenwardy.com/">rubenwardy</a> |
|
||||
<a href="https://github.com/minetest/contentdb">GitHub</a> |
|
||||
<a href="{{ url_for('flatpage', path='help') }}">Help</a> |
|
||||
<a href="https://github.com/minetest/contentdb">GitHub</a>
|
||||
<a href="{{ url_for('flatpage', path='help/reporting') }}">Report / DMCA</a>
|
||||
</footer>
|
||||
</html>
|
||||
|
||||
@@ -11,9 +11,11 @@ Sign in
|
||||
<h2>{%trans%}Sign in{%endtrans%}</h2>
|
||||
|
||||
<form action="" method="POST" class="form box-body" role="form">
|
||||
<a href="{{ url_for('github_signin_page') }}">GitHub</a>
|
||||
<h3>Sign in with Github</h3>
|
||||
<p><a class="button" href="{{ url_for('github_signin_page') }}">GitHub</a></p>
|
||||
|
||||
|
||||
<h3>Sign in with username/password</h3>
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
{# Username or Email field #}
|
||||
@@ -71,7 +73,7 @@ Sign in
|
||||
<h2>New here?</h2>
|
||||
|
||||
<div class="box-body">
|
||||
<p>Create an account using your forum account.</p>
|
||||
<p>Create an account using your forum account or email.</p>
|
||||
|
||||
<a href="{{ url_for('user_claim_page') }}" class="button">{%trans%}Claim your account{%endtrans%}</a>
|
||||
</div>
|
||||
|
||||
@@ -20,6 +20,40 @@
|
||||
<script src="/static/tagselector.js"></script>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro package_lists() -%}
|
||||
<script>
|
||||
meta_packages = [
|
||||
{% for m in mpackages %}
|
||||
{# This is safe as name can only contain `[a-z0-9_]` #}
|
||||
{
|
||||
id: "{{ m.name }}",
|
||||
value: "{{ m.name }}",
|
||||
toString: function() { return "{{ m.name }}"; },
|
||||
},
|
||||
{% endfor %}
|
||||
]
|
||||
|
||||
function escape(unsafe) {
|
||||
return unsafe
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
all_packages = meta_packages.slice();
|
||||
|
||||
{% for p in packages %}
|
||||
all_packages.push({
|
||||
id: "{{ p.author.username }}/{{ p.name }}",
|
||||
value: escape({{ p.title | tojson }} + " by " + {{ p.author.display_name | tojson }}),
|
||||
toString: function() { return escape({{ p.title | tojson }} + " by " + {{ p.author.display_name | tojson }} + " only"); },
|
||||
});
|
||||
{% endfor %}
|
||||
</script>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_multiselect_field(field, label=None, label_visible=true, right_url=None, right_label=None) -%}
|
||||
<div class="form-group {% if field.errors %}has-error{% endif %} {{ kwargs.pop('class_', '') }}">
|
||||
{% if field.type != 'HiddenField' and label_visible %}
|
||||
|
||||
@@ -1,21 +1,42 @@
|
||||
{% macro render_pkgtile(package) -%}
|
||||
{% macro render_pkgtile(package, show_author) -%}
|
||||
<li><a href="{{ package.getDetailsURL() }}"
|
||||
style="background-image: url({{ package.getMainScreenshotURL() or '/static/placeholder.png' }});">
|
||||
style="background-image: url({{ package.getThumbnailURL() or '/static/placeholder.png' }});">
|
||||
<div class="packagegridscrub"></div>
|
||||
<div class="packagegridinfo">
|
||||
<h3>{{ package.title }} by {{ package.author.display_name }}</h3>
|
||||
<h3>
|
||||
{{ package.title }}
|
||||
|
||||
{% if show_author %}
|
||||
by {{ package.author.display_name }}
|
||||
{% endif %}
|
||||
</h3>
|
||||
|
||||
<p>
|
||||
{{ package.shortDesc }}
|
||||
</p>
|
||||
|
||||
|
||||
{% if not package.license.is_foss and not package.media_license.is_foss and package.type != package.type.TXP %}
|
||||
<p style="color:#f33;">
|
||||
<b>Warning:</b> Non-free code and media.
|
||||
</p>
|
||||
{% elif not package.license.is_foss and package.type != package.type.TXP %}
|
||||
<p style="color:#f33;">
|
||||
<b>Warning:</b> Non-free code.
|
||||
</p>
|
||||
{% elif not package.media_license.is_foss %}
|
||||
<p style="color:#f33;">
|
||||
<b>Warning:</b> Non-free media.
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</a></li>
|
||||
{% endmacro %}
|
||||
|
||||
{% macro render_pkggrid(packages) -%}
|
||||
{% macro render_pkggrid(packages, show_author=True) -%}
|
||||
<ul class="packagegrid">
|
||||
{% for p in packages %}
|
||||
{{ render_pkgtile(p) }}
|
||||
{{ render_pkgtile(p, show_author) }}
|
||||
{% else %}
|
||||
<li><i>No packages available</i></ul>
|
||||
{% endfor %}
|
||||
|
||||
@@ -7,7 +7,7 @@ Meta Packages
|
||||
{% block content %}
|
||||
<ul>
|
||||
{% for meta in mpackages %}
|
||||
<li><a href="{{ url_for('meta_package_page', name=meta.name) }}">{{ meta.name }}</a> ({{ meta.packages | count }} packages)</li>
|
||||
<li><a href="{{ url_for('meta_package_page', name=meta.name) }}">{{ meta.name }}</a> ({{ meta.packages.filter_by(soft_deleted=False, approved=True).all() | count }} packages)</li>
|
||||
{% else %}
|
||||
<li><i>No meta packages found.</i></li>
|
||||
{% endfor %}
|
||||
|
||||
@@ -5,8 +5,8 @@ Packages providing '{{ mpackage.name }}''
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Packages providing '{{ mpackage.name }}''</h1>
|
||||
<h1>Packages providing '{{ mpackage.name }}'</h1>
|
||||
|
||||
{% from "macros/packagegridtile.html" import render_pkggrid %}
|
||||
{{ render_pkggrid(mpackage.packages) }}
|
||||
{{ render_pkggrid(mpackage.packages.filter_by(approved=True, soft_deleted=False).all()) }}
|
||||
{% endblock %}
|
||||
|
||||
@@ -5,6 +5,12 @@ Notifications
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if current_user.notifications %}
|
||||
<form method="post" action="{{ url_for('clear_notifications_page') }}">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
<input type="submit" value="Clear All" />
|
||||
</form>
|
||||
{% endif %}
|
||||
<ul>
|
||||
{% for n in current_user.notifications %}
|
||||
<li><a href="{{ n.url }}">
|
||||
|
||||
@@ -10,40 +10,16 @@
|
||||
{% block content %}
|
||||
<h1>Create Package</h1>
|
||||
|
||||
<script>
|
||||
meta_packages = [
|
||||
{% for m in mpackages %}
|
||||
{# This is safe as name can only contain `[a-z0-9_]` #}
|
||||
{
|
||||
id: "{{ m.name }}",
|
||||
value: "{{ m.name }}",
|
||||
toString: function() { return "{{ m.name }}"; },
|
||||
},
|
||||
{% endfor %}
|
||||
]
|
||||
<div class="box box_grey alert alert-info">
|
||||
Have you read the Package Inclusion Policy and Guidance yet?
|
||||
|
||||
function escape(unsafe) {
|
||||
return unsafe
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
<a class="alert_right button" href="{{ url_for('flatpage', path='policy_and_guidance') }}">View</a>
|
||||
</div>
|
||||
|
||||
all_packages = meta_packages.slice();
|
||||
|
||||
{% for p in packages %}
|
||||
all_packages.push({
|
||||
id: "{{ p.author.username }}/{{ p.name }}",
|
||||
value: escape({{ p.title | tojson }} + " by " + {{ p.author.display_name | tojson }}),
|
||||
toString: function() { return escape({{ p.title | tojson }} + " by " + {{ p.author.display_name | tojson }} + " only"); },
|
||||
});
|
||||
{% endfor %}
|
||||
</script>
|
||||
|
||||
{% from "macros/forms.html" import render_field, render_submit_field, form_includes, render_multiselect_field, render_mpackage_field, render_deps_field %}
|
||||
{% from "macros/forms.html" import render_field, render_submit_field, form_includes, render_multiselect_field, render_mpackage_field, render_deps_field, package_lists %}
|
||||
{{ form_includes() }}
|
||||
{{ package_lists() }}
|
||||
|
||||
<form method="POST" action="" class="tableform">
|
||||
{{ form.hidden_tag() }}
|
||||
@@ -56,7 +32,10 @@
|
||||
{{ render_field(form.shortDesc, class_="pkg_meta") }}
|
||||
{{ render_field(form.desc, class_="pkg_meta") }}
|
||||
{{ render_multiselect_field(form.tags, class_="pkg_meta") }}
|
||||
{{ render_field(form.license, class_="pkg_meta") }}
|
||||
<div class="pkg_meta">
|
||||
{{ render_field(form.license, class_="not_txp") }}
|
||||
</div>
|
||||
{{ render_field(form.media_license, class_="pkg_meta") }}
|
||||
|
||||
<div class="pkg_meta">
|
||||
<h2 class="not_txp">Dependency Info</h2>
|
||||
|
||||
@@ -5,19 +5,30 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% from "macros/forms.html" import render_field, render_submit_field, form_includes, render_multiselect_field %}
|
||||
{% from "macros/forms.html" import render_field, render_submit_field, form_includes, render_multiselect_field, render_mpackage_field, render_deps_field, package_lists %}
|
||||
{{ form_includes() }}
|
||||
{{ package_lists() }}
|
||||
|
||||
<form method="POST" action="">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<h2 class="pkg_meta">Package</h2>
|
||||
|
||||
{{ render_field(form.type) }}
|
||||
{{ render_field(form.name) }}
|
||||
{{ render_field(form.title) }}
|
||||
{{ render_field(form.shortDesc) }}
|
||||
{{ render_field(form.desc) }}
|
||||
{{ render_field(form.type) }}
|
||||
{{ render_field(form.license) }}
|
||||
{{ render_multiselect_field(form.tags) }}
|
||||
|
||||
<h2 class="not_txp">Dependency Info</h2>
|
||||
|
||||
{{ render_mpackage_field(form.provides_str, class_="not_txp", placeholder="Comma separated list") }}
|
||||
{{ render_deps_field(form.harddep_str, class_="not_txp not_game", placeholder="Comma separated list") }}
|
||||
{{ render_deps_field(form.softdep_str, class_="not_txp not_game", placeholder="Comma separated list") }}
|
||||
|
||||
{{ render_field(form.license) }}
|
||||
{{ render_field(form.media_license) }}
|
||||
{{ render_field(form.repo) }}
|
||||
{{ render_field(form.website) }}
|
||||
{{ render_field(form.issueTracker) }}
|
||||
@@ -29,4 +40,6 @@
|
||||
{{ render_field(form.edit_desc) }}
|
||||
{{ render_submit_field(form.submit) }}
|
||||
</form>
|
||||
|
||||
<script src="/static/package_edit.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<input type="submit" value="Search" />
|
||||
|
||||
<p>
|
||||
Found {{ packages | count }} packages.
|
||||
Found {{ packages_count }} packages.
|
||||
</p>
|
||||
</form>
|
||||
|
||||
@@ -31,4 +31,10 @@
|
||||
|
||||
{% from "macros/packagegridtile.html" import render_pkggrid %}
|
||||
{{ render_pkggrid(packages) }}
|
||||
|
||||
<ul class="buttonset linedbuttonset">
|
||||
{% if prev_url %}<li><a href="{{ prev_url }}">Previous</a></li>{% endif %}
|
||||
<li>{{ page }} / {{ page_max }}</li>
|
||||
{% if next_url %}<li><a href="{{ next_url }}">Next</a></li> {% endif %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<form method="POST" action="" enctype="multipart/form-data">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
{{ render_field(form.title) }}
|
||||
{{ render_field(form.title, placeholder="Human readable. Eg: 1.0.0 or 2018-05-28") }}
|
||||
{{ render_field(form.uploadOpt) }}
|
||||
{% if package.canMakeReleaseFromVCS() %}
|
||||
{{ render_field(form.vcsLabel) }}
|
||||
|
||||
@@ -61,7 +61,21 @@
|
||||
|
||||
<aside class="asideright box box_grey">
|
||||
<h3>Details</h3>
|
||||
|
||||
<div class="box-body">
|
||||
{% if not package.license.is_foss and not package.media_license.is_foss and package.type != package.type.TXP %}
|
||||
<div class="box box_grey alert alert-error" style="margin-top: 0;">
|
||||
<b>Warning:</b> Non-free code and media.
|
||||
</div>
|
||||
{% elif not package.license.is_foss and package.type != package.type.TXP %}
|
||||
<div class="box box_grey alert alert-error" style="margin-top: 0;">
|
||||
<b>Warning:</b> Non-free code.
|
||||
</div>
|
||||
{% elif not package.media_license.is_foss %}
|
||||
<div class="box box_grey alert alert-error" style="margin-top: 0;">
|
||||
<b>Warning:</b> Non-free media.
|
||||
</div>
|
||||
{% endif %}
|
||||
<table>
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
@@ -92,7 +106,16 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td>License</td>
|
||||
<td>{{ package.license.name }}</td>
|
||||
<td>
|
||||
{% if package.license == package.media_license %}
|
||||
{{ package.license.name }}
|
||||
{% elif package.type == package.type.TXP %}
|
||||
{{ package.media_license.name }}
|
||||
{% else %}
|
||||
{{ package.license.name }} for code,<br />
|
||||
{{ package.media_license.name }} for media.
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Added</td>
|
||||
@@ -101,7 +124,7 @@
|
||||
</table>
|
||||
|
||||
<ul class="buttonset linedbuttonset">
|
||||
{% if package.getDownloadRelease() %}<li><a href="{{ package.getDownloadURL() }}">Download</a></li>{% endif %}
|
||||
{% if package.getDownloadRelease() %}<li><a href="{{ package.getDownloadURL() }}" class="btn_green">Download</a></li>{% endif %}
|
||||
{% if package.repo %}<li><a href="{{ package.repo }}">View Source</a></li>{% endif %}
|
||||
{% if package.forums %}<li><a href="https://forum.minetest.net/viewtopic.php?t={{ package.forums }}">Forums</a></li>{% endif %}
|
||||
{% if package.issueTracker %}<li><a href="{{ package.issueTracker }}">Issue Tracker</a></li>{% endif %}
|
||||
@@ -110,9 +133,9 @@
|
||||
<li><a href="{{ package.getEditURL() }}">Edit</a></li>
|
||||
<li><a href="{{ package.getNewScreenshotURL() }}">Add screenshot</a></li>
|
||||
{% endif %}
|
||||
{% if current_user.is_authenticated %}
|
||||
{# {% if current_user.is_authenticated %}
|
||||
<li><a href="{{ package.getCreateEditRequestURL() }}">Suggest Changes</a></li>
|
||||
{% endif %}
|
||||
{% endif %} #}
|
||||
{% if package.checkPerm(current_user, "MAKE_RELEASE") %}
|
||||
<li><a href="{{ package.getCreateReleaseURL() }}">Create Release</a></li>
|
||||
{% endif %}
|
||||
@@ -209,18 +232,52 @@
|
||||
</tr>
|
||||
</table> -->
|
||||
|
||||
{% if current_user.is_authenticated or requests %}
|
||||
<h3>Edit Requests</h3>
|
||||
{#
|
||||
{% if current_user.is_authenticated or requests %}
|
||||
<h3>Edit Requests</h3>
|
||||
|
||||
<ul>
|
||||
{% for r in requests %}
|
||||
<li>
|
||||
<a href="{{ r.getURL() }}">{{ r.title }}</a>
|
||||
by
|
||||
<a href="{{ url_for('user_profile_page', username=r.author.username) }}">{{ r.author.display_name }}</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li>No edit requests have been made.</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
#}
|
||||
|
||||
{% if alternatives %}
|
||||
<h3>Alternatives</h3>
|
||||
<ul>
|
||||
{% for r in requests %}
|
||||
{% for p in alternatives %}
|
||||
<li><a href="{{ p.getDetailsURL() }}">{{ p.title }} by {{ p.author.display_name }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if similar_topics %}
|
||||
<h3>Similar Forum Topics</h3>
|
||||
{% if not package.approved and package.type == package.type.MOD %}
|
||||
<div class="box box_grey alert alert-warning">
|
||||
Please make sure that this package has the right to
|
||||
the name '{{ package.name }}'.
|
||||
See the
|
||||
<a href="/policy_and_guidance/">Inclusion Policy</a>
|
||||
for more info.
|
||||
</div>
|
||||
{% endif %}
|
||||
<ul>
|
||||
{% for t in similar_topics %}
|
||||
<li>
|
||||
<a href="{{ r.getURL() }}">{{ r.title }}</a>
|
||||
by
|
||||
<a href="{{ url_for('user_profile_page', username=r.author.username) }}">{{ r.author.display_name }}</a>
|
||||
[{{ t.getType().value }}]
|
||||
<a href="https://forum.minetest.net/viewtopic.php?t={{ t.topic_id }}">
|
||||
{{ t.title }} by {{ t.author.display_name }}
|
||||
</a>
|
||||
</li>
|
||||
{% else %}
|
||||
<li>No edit requests have been made.</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% if canApproveNew %}
|
||||
<h2>Packages Awaiting Approval</h2>
|
||||
<h2>Awaiting Approval</h2>
|
||||
|
||||
{% if canApproveNew and packages %}
|
||||
<h3>Packages</h3>
|
||||
<ul>
|
||||
{% for p in packages %}
|
||||
<li><a href="{{ p.getDetailsURL() }}">
|
||||
@@ -18,8 +20,8 @@
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if canApproveScn %}
|
||||
<h2>Screenshots Awaiting Approval</h2>
|
||||
{% if canApproveScn and screenshots %}
|
||||
<h3>Screenshots</h3>
|
||||
<ul>
|
||||
{% for s in screenshots %}
|
||||
<li>
|
||||
@@ -35,8 +37,8 @@
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if canApproveRel %}
|
||||
<h2>Releases Awaiting Approval</h2>
|
||||
{% if canApproveRel and releases %}
|
||||
<h3>Releases</h3>
|
||||
<ul>
|
||||
{% for r in releases %}
|
||||
<li>
|
||||
@@ -51,4 +53,18 @@
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% if not (packages or screenshots or releases) %}
|
||||
<p>
|
||||
<i>All done!</i>
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<h2>Unadded Topic List</h2>
|
||||
|
||||
<p>
|
||||
There are
|
||||
<a href="{{ url_for('todo_topics_page') }}">{{ topics_to_add }} packages</a>
|
||||
to be added to cdb, based on forum topics picked up by Krock's mod search.
|
||||
</p>
|
||||
{% endblock %}
|
||||
34
app/templates/todo/topics.html
Normal file
34
app/templates/todo/topics.html
Normal file
@@ -0,0 +1,34 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
Topics to be Added
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Topics to be Added</h1>
|
||||
|
||||
<p>
|
||||
{{ total - (topics | count) }} / {{ total }} packages have been added.
|
||||
{{ topics | count }} remaining.
|
||||
</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>Title</th>
|
||||
<th>Author</th>
|
||||
<th>Name</th>
|
||||
<th>Link</th>
|
||||
</tr>
|
||||
{% for topic in topics %}
|
||||
<tr>
|
||||
<td>{{ topic.topic_id }}</td>
|
||||
<td>[{{ topic.getType().value }}] <a href="https://forum.minetest.net/viewtopic.php?t={{ topic.topic_id}}">{{ topic.title }}</a></td>
|
||||
<td><a href="{{ url_for('user_profile_page', username=topic.author.username) }}">{{ topic.author.display_name}}</a></td>
|
||||
<td>{{ topic.name or ""}}</td>
|
||||
<td><a href="{{ topic.link }}">{{ topic.link | domain }}</a></td>
|
||||
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endblock %}
|
||||
@@ -1,44 +1,45 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
Verify forum account
|
||||
Creating an Account
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="box box_grey">
|
||||
<h2>{{ self.title() }}</h2>
|
||||
|
||||
<p>
|
||||
Create an account by linking it to your forum account and optionally
|
||||
your github account.
|
||||
</p>
|
||||
|
||||
{% if current_user.is_authenticated %}
|
||||
<div class="box-body">
|
||||
<p>
|
||||
Please log out to continue.
|
||||
</p>
|
||||
<p>
|
||||
<a href="{{ url_for('user.logout', next=url_for('user_claim_page')) }}" class="button">Logout</a>
|
||||
</p>
|
||||
{% else %}
|
||||
<p>
|
||||
<b>Don't have a forum account?</b>
|
||||
Unfortunately, you need a forum account to register.
|
||||
This is because you also need to create forum topics for any packages
|
||||
you may upload.
|
||||
If you have a forum account, you'll need to prove that you own it
|
||||
to get an account on ContentDB.
|
||||
</p>
|
||||
|
||||
<a href="https://forum.minetest.net/ucp.php?mode=register">
|
||||
Create a Forum Account
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if current_user.is_authenticated %}
|
||||
<p>
|
||||
Please log out to continue.
|
||||
</p>
|
||||
<p>
|
||||
<a href="{{ url_for('user.logout', next=url_for('user_claim_page')) }}" class="button">Logout</a>
|
||||
</p>
|
||||
{% else %}
|
||||
<p>
|
||||
<b>Don't have a forum account?</b>
|
||||
You don't need one, however it's recommended to make the most
|
||||
out of the Minetest community.
|
||||
</p>
|
||||
|
||||
<a href="https://forum.minetest.net/ucp.php?mode=register">
|
||||
Create a Forum Account
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if not current_user.is_authenticated %}
|
||||
<div class="box box_grey">
|
||||
<h2>Option 1 - Use GitHub field in forum profile</h2>
|
||||
|
||||
<form method="post" action="{{ url_for('user_claim_page') }}">
|
||||
<form method="post" class="box-body" action="{{ url_for('user_claim_page') }}">
|
||||
<input type="hidden" name="claim_type" value="github">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
|
||||
@@ -59,10 +60,10 @@ Verify forum account
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!--<div class="box box_grey">
|
||||
<div class="box box_grey">
|
||||
<h2>Option 2 - Paste verification token into signature</h2>
|
||||
|
||||
<form method="post" action="{{ url_for('user_claim_page') }}">
|
||||
<form method="post" class="box-body" action="{{ url_for('user_claim_page') }}">
|
||||
<input type="hidden" name="claim_type" value="forum">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
|
||||
@@ -93,6 +94,22 @@ Verify forum account
|
||||
|
||||
<input type="submit" value="Next">
|
||||
</form>
|
||||
</div>-->
|
||||
</div>
|
||||
|
||||
<div class="box box_grey">
|
||||
<h2>Option 3 - Email/password sign up</h2>
|
||||
|
||||
<div class="box-body">
|
||||
<p>
|
||||
<b>Only do this if you don't have a forum account!</b>
|
||||
</p>
|
||||
<p>
|
||||
If you have a forum account, please use one of the other two
|
||||
options.
|
||||
</p>
|
||||
|
||||
<a class="button" href="{{ url_for('user.register') }}">Register</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
28
app/templates/users/set_password.html
Normal file
28
app/templates/users/set_password.html
Normal file
@@ -0,0 +1,28 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
Set Password
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Set Password</h1>
|
||||
|
||||
{% from "macros/forms.html" import render_field, render_submit_field %}
|
||||
<form action="" method="POST" class="form" role="form">
|
||||
<div class="row">
|
||||
<div class="col-sm-6 col-md-5 col-lg-4">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
{% if not current_user.email %}
|
||||
{{ render_field(form.email, tabindex=230) }}
|
||||
{% endif %}
|
||||
|
||||
{{ render_field(form.password, tabindex=230) }}
|
||||
{{ render_field(form.password2, tabindex=240) }}
|
||||
|
||||
{{ render_submit_field(form.submit, tabindex=280) }}
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
||||
@@ -6,6 +6,14 @@
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% if not current_user.is_authenticated and user.rank == user.rank.NOT_JOINED and user.forums_username %}
|
||||
<div class="box box_grey alert alert-info">
|
||||
Is this you? Claim your account now!
|
||||
|
||||
<a class="alert_right button" href="{{ url_for('user_claim_page', username=user.forums_username) }}">Claim</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="box box_grey">
|
||||
<h2>{{ user.display_name }}</h2>
|
||||
|
||||
@@ -24,7 +32,7 @@
|
||||
Minetest Forum
|
||||
</a>
|
||||
{% elif user == current_user %}
|
||||
<a href="">Link Forums Account</a>
|
||||
No forum account
|
||||
{% endif %}
|
||||
|
||||
{% if (user.forums_username and user.github_username) or user == current_user %}
|
||||
@@ -42,27 +50,19 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="box box_grey">
|
||||
<h2>Packages</h2>
|
||||
<div class="box-body">
|
||||
<ul>
|
||||
{% for p in user.packages %}
|
||||
<li><a href="{{ p.getDetailsURL() }}">
|
||||
{{ p.title }} by {{ p.author.display_name }}
|
||||
</a></li>
|
||||
{% else %}
|
||||
<li><i>No packages available</i></ul>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% if user == current_user or user.checkPerm(current_user, "CHANGE_AUTHOR") %}
|
||||
<p><a class="button" href="{{ url_for('create_edit_package_page', author=user.username) }}">
|
||||
Create
|
||||
</a></p>
|
||||
{% if user == current_user %}
|
||||
<tr>
|
||||
<td>Password:</td>
|
||||
<td>
|
||||
{% if user.password %}
|
||||
Set | <a href="{{ url_for('user.change_password') }}">Change</a>
|
||||
{% else %}
|
||||
Not set | <a href="{{ url_for('set_password_page') }}">Set</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</div>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% if form %}
|
||||
@@ -94,4 +94,38 @@
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% from "macros/packagegridtile.html" import render_pkggrid %}
|
||||
{{ render_pkggrid(packages, show_author=False) }}
|
||||
|
||||
{% if topics_to_add %}
|
||||
<div class="box box_grey">
|
||||
<h2>Unadded Packages</h2>
|
||||
|
||||
<div class="box-body">
|
||||
<p>
|
||||
List of your topics without a matching package.
|
||||
Powered by Krock's Mod Search.
|
||||
</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th>Title</th>
|
||||
<th>Name</th>
|
||||
<th>Link</th>
|
||||
</tr>
|
||||
{% for topic in topics_to_add %}
|
||||
<tr>
|
||||
<td>{{ topic.topic_id }}</td>
|
||||
<td>[{{ topic.getType().value }}] <a href="https://forum.minetest.net/viewtopic.php?t={{ topic.topic_id}}">{{ topic.title }}</a></td>
|
||||
<td>{{ topic.name or ""}}</td>
|
||||
<td><a href="{{ topic.link }}">{{ topic.link | domain }}</a></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
@@ -68,7 +68,7 @@ def _do_login_user(user, remember_me=False):
|
||||
|
||||
user.active = True
|
||||
if not user.rank.atLeast(UserRank.NEW_MEMBER):
|
||||
user.rank = UserRank.NEW_MEMBER
|
||||
user.rank = UserRank.MEMBER
|
||||
|
||||
db.session.commit()
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ def home_page():
|
||||
packages = query.order_by(db.desc(Package.created_at)).limit(15).all()
|
||||
return render_template("index.html", packages=packages, count=count)
|
||||
|
||||
from . import users, githublogin, packages, sass, tasks, admin, notifications, tagseditor, meta
|
||||
from . import users, githublogin, packages, sass, tasks, admin, notifications, tagseditor, meta, thumbnails
|
||||
|
||||
@menu.register_menu(app, ".help", "Help", order=19, endpoint_arguments_constructor=lambda: { 'path': 'help' })
|
||||
@app.route('/<path:path>/')
|
||||
@@ -62,7 +62,11 @@ def flatpage(path):
|
||||
|
||||
@app.before_request
|
||||
def do_something_whenever_a_request_comes_in():
|
||||
if current_user.is_authenticated and current_user.rank == UserRank.BANNED:
|
||||
flash("You have been banned.", "error")
|
||||
logout_user()
|
||||
return redirect(url_for('user.login'))
|
||||
if current_user.is_authenticated:
|
||||
if current_user.rank == UserRank.BANNED:
|
||||
flash("You have been banned.", "error")
|
||||
logout_user()
|
||||
return redirect(url_for('user.login'))
|
||||
elif current_user.rank == UserRank.NOT_JOINED:
|
||||
current_user.rank = UserRank.MEMBER
|
||||
db.session.commit()
|
||||
|
||||
@@ -21,7 +21,7 @@ from flask.ext import menu
|
||||
from app import app
|
||||
from app.models import *
|
||||
from app.tasks.importtasks import importRepoScreenshot, importAllDependencies
|
||||
from app.tasks.forumtasks import importUsersFromModList
|
||||
from app.tasks.forumtasks import importUsersFromModList, importKrocksModList
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import *
|
||||
from app.utils import loginUser, rank_required
|
||||
@@ -34,6 +34,9 @@ def admin_page():
|
||||
if action == "importusers":
|
||||
task = importUsersFromModList.delay()
|
||||
return redirect(url_for("check_task", id=task.id, r=url_for("user_list_page")))
|
||||
elif action == "importmodlist":
|
||||
task = importKrocksModList.delay()
|
||||
return redirect(url_for("check_task", id=task.id, r=url_for("todo_topics_page")))
|
||||
elif action == "importscreenshots":
|
||||
packages = Package.query \
|
||||
.filter_by(soft_deleted=False) \
|
||||
|
||||
@@ -22,13 +22,13 @@ from app.models import *
|
||||
|
||||
@app.route("/metapackages/")
|
||||
def meta_package_list_page():
|
||||
mpackages = MetaPackage.query.order_by(db.desc(MetaPackage.name)).all()
|
||||
return render_template("meta/list.html", mpackages=mpackages)
|
||||
mpackages = MetaPackage.query.order_by(db.asc(MetaPackage.name)).all()
|
||||
return render_template("meta/list.html", mpackages=mpackages)
|
||||
|
||||
@app.route("/metapackages/<name>/")
|
||||
def meta_package_page(name):
|
||||
mpackage = MetaPackage.query.filter_by(name=name).first()
|
||||
if mpackage is None:
|
||||
abort(404)
|
||||
mpackage = MetaPackage.query.filter_by(name=name).first()
|
||||
if mpackage is None:
|
||||
abort(404)
|
||||
|
||||
return render_template("meta/view.html", mpackage=mpackage)
|
||||
return render_template("meta/view.html", mpackage=mpackage)
|
||||
|
||||
@@ -23,4 +23,11 @@ from app.models import *
|
||||
@app.route("/notifications/")
|
||||
@login_required
|
||||
def notifications_page():
|
||||
return render_template("notifications/list.html")
|
||||
return render_template("notifications/list.html")
|
||||
|
||||
@app.route("/notifications/clear/", methods=["POST"])
|
||||
@login_required
|
||||
def clear_notifications_page():
|
||||
current_user.notifications.clear()
|
||||
db.session.commit()
|
||||
return redirect(url_for("notifications_page"))
|
||||
|
||||
@@ -51,16 +51,26 @@ def packages_page():
|
||||
|
||||
search = request.args.get("q")
|
||||
if search is not None:
|
||||
query = query.filter(Package.title.contains(search))
|
||||
query = query.filter(Package.title.ilike('%' + search + '%'))
|
||||
|
||||
if shouldReturnJson():
|
||||
pkgs = [package.getAsDictionary(app.config["BASE_URL"]) \
|
||||
for package in query.all() if package.getDownloadRelease() is not None]
|
||||
return jsonify(pkgs)
|
||||
else:
|
||||
page = int(request.args.get("page") or 1)
|
||||
num = min(42, int(request.args.get("n") or 100))
|
||||
query = query.paginate(page, num, True)
|
||||
|
||||
next_url = url_for("packages_page", type=type.toName(), q=search, page=query.next_num) \
|
||||
if query.has_next else None
|
||||
prev_url = url_for("packages_page", type=type.toName(), q=search, page=query.prev_num) \
|
||||
if query.has_prev else None
|
||||
|
||||
tags = Tag.query.all()
|
||||
return render_template("packages/list.html", title=title, packages=query.all(), \
|
||||
query=search, tags=tags, type=None if type is None else type.toName())
|
||||
return render_template("packages/list.html", title=title, packages=query.items, \
|
||||
query=search, tags=tags, type=None if type is None else type.toName(), \
|
||||
next_url=next_url, prev_url=prev_url, page=page, page_max=query.pages, packages_count=query.total)
|
||||
|
||||
|
||||
def getReleases(package):
|
||||
@@ -78,9 +88,30 @@ def package_page(package):
|
||||
else:
|
||||
clearNotifications(package.getDetailsURL())
|
||||
|
||||
alternatives = None
|
||||
if package.type == PackageType.MOD:
|
||||
alternatives = Package.query \
|
||||
.filter_by(name=package.name, type=PackageType.MOD, soft_deleted=False) \
|
||||
.filter(Package.id != package.id) \
|
||||
.order_by(db.asc(Package.title)) \
|
||||
.all()
|
||||
|
||||
show_similar_topics = current_user == package.author or \
|
||||
package.checkPerm(current_user, Permission.APPROVE_NEW)
|
||||
|
||||
similar_topics = None if not show_similar_topics else \
|
||||
KrockForumTopic.query \
|
||||
.filter_by(name=package.name) \
|
||||
.filter(KrockForumTopic.topic_id != package.forums) \
|
||||
.filter(~ db.exists().where(Package.forums==KrockForumTopic.topic_id)) \
|
||||
.order_by(db.asc(KrockForumTopic.name), db.asc(KrockForumTopic.title)) \
|
||||
.all()
|
||||
|
||||
releases = getReleases(package)
|
||||
requests = [r for r in package.requests if r.status == 0]
|
||||
return render_template("packages/view.html", package=package, releases=releases, requests=requests)
|
||||
return render_template("packages/view.html", \
|
||||
package=package, releases=releases, requests=requests, \
|
||||
alternatives=alternatives, similar_topics=similar_topics)
|
||||
|
||||
|
||||
@app.route("/packages/<author>/<name>/download/")
|
||||
@@ -106,6 +137,7 @@ class PackageForm(FlaskForm):
|
||||
desc = TextAreaField("Long Description (Markdown)", [Optional(), Length(0,10000)])
|
||||
type = SelectField("Type", [InputRequired()], choices=PackageType.choices(), coerce=PackageType.coerce, default=PackageType.MOD)
|
||||
license = QuerySelectField("License", [InputRequired()], query_factory=lambda: License.query, get_pk=lambda a: a.id, get_label=lambda a: a.name)
|
||||
media_license = QuerySelectField("Media License", [InputRequired()], query_factory=lambda: License.query, get_pk=lambda a: a.id, get_label=lambda a: a.name)
|
||||
provides_str = StringField("Provides (mods included in package)", [Optional(), Length(0,1000)])
|
||||
tags = QuerySelectMultipleField('Tags', query_factory=lambda: Tag.query.order_by(db.asc(Tag.name)), get_pk=lambda a: a.id, get_label=lambda a: a.title)
|
||||
harddep_str = StringField("Hard Dependencies", [Optional(), Length(0,1000)])
|
||||
@@ -261,4 +293,4 @@ def delete_package_page(package):
|
||||
|
||||
return redirect(url)
|
||||
|
||||
from . import todo, screenshots, editrequests, releases
|
||||
from . import todo, screenshots, releases
|
||||
|
||||
@@ -58,8 +58,13 @@ def create_edit_editrequest_page(package, id=None):
|
||||
edited_package = Package(package)
|
||||
erequest.applyAll(edited_package)
|
||||
|
||||
|
||||
form = EditRequestForm(request.form, obj=edited_package)
|
||||
if request.method == "GET":
|
||||
deps = edited_package.dependencies
|
||||
form.harddep_str.data = ",".join([str(x) for x in deps if not x.optional])
|
||||
form.softdep_str.data = ",".join([str(x) for x in deps if x.optional])
|
||||
form.provides_str.data = MetaPackage.ListToSpec(edited_package.provides)
|
||||
|
||||
if request.method == "POST" and form.validate():
|
||||
if erequest is None:
|
||||
erequest = EditRequest()
|
||||
|
||||
@@ -30,16 +30,14 @@ from wtforms import *
|
||||
from wtforms.validators import *
|
||||
|
||||
class CreatePackageReleaseForm(FlaskForm):
|
||||
name = StringField("Name")
|
||||
title = StringField("Title")
|
||||
title = StringField("Title", [InputRequired(), Length(1, 30)])
|
||||
uploadOpt = RadioField ("Method", choices=[("upload", "File Upload")], default="upload")
|
||||
vcsLabel = StringField("VCS Commit or Branch", default="master")
|
||||
fileUpload = FileField("File Upload")
|
||||
submit = SubmitField("Save")
|
||||
|
||||
class EditPackageReleaseForm(FlaskForm):
|
||||
name = StringField("Name")
|
||||
title = StringField("Title")
|
||||
title = StringField("Title", [InputRequired(), Length(1, 30)])
|
||||
url = StringField("URL", [URL])
|
||||
task_id = StringField("Task ID")
|
||||
approved = BooleanField("Is Approved")
|
||||
|
||||
@@ -40,6 +40,25 @@ def todo_page():
|
||||
if canApproveScn:
|
||||
screenshots = PackageScreenshot.query.filter_by(approved=False).all()
|
||||
|
||||
return render_template("todo.html", title="Reports and Work Queue",
|
||||
|
||||
topics_to_add = KrockForumTopic.query \
|
||||
.filter(~ db.exists().where(Package.forums==KrockForumTopic.topic_id)) \
|
||||
.count()
|
||||
|
||||
return render_template("todo/list.html", title="Reports and Work Queue",
|
||||
packages=packages, releases=releases, screenshots=screenshots,
|
||||
canApproveNew=canApproveNew, canApproveRel=canApproveRel, canApproveScn=canApproveScn)
|
||||
canApproveNew=canApproveNew, canApproveRel=canApproveRel, canApproveScn=canApproveScn,
|
||||
topics_to_add=topics_to_add)
|
||||
|
||||
|
||||
@app.route("/todo/topics/")
|
||||
@login_required
|
||||
def todo_topics_page():
|
||||
total = KrockForumTopic.query.count()
|
||||
|
||||
topics = KrockForumTopic.query \
|
||||
.filter(~ db.exists().where(Package.forums==KrockForumTopic.topic_id)) \
|
||||
.order_by(db.asc(KrockForumTopic.name), db.asc(KrockForumTopic.title)) \
|
||||
.all()
|
||||
|
||||
return render_template("todo/topics.html", topics=topics, total=total)
|
||||
|
||||
@@ -27,7 +27,7 @@ from app.utils import rank_required
|
||||
@app.route("/tags/")
|
||||
@rank_required(UserRank.MODERATOR)
|
||||
def tag_list_page():
|
||||
return render_template("tags/list.html", tags=Tag.query.all())
|
||||
return render_template("tags/list.html", tags=Tag.query.order_by(db.asc(Tag.title)).all())
|
||||
|
||||
class TagForm(FlaskForm):
|
||||
title = StringField("Title", [InputRequired(), Length(3,100)])
|
||||
|
||||
46
app/views/thumbnails.py
Normal file
46
app/views/thumbnails.py
Normal file
@@ -0,0 +1,46 @@
|
||||
# Content DB
|
||||
# Copyright (C) 2018 rubenwardy
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from flask import *
|
||||
from app import app
|
||||
|
||||
import glob, os
|
||||
from PIL import Image
|
||||
|
||||
ALLOWED_RESOLUTIONS=[(350,233)]
|
||||
|
||||
def mkdir(path):
|
||||
if not os.path.isdir(path):
|
||||
os.mkdir(path)
|
||||
|
||||
mkdir("app/public/thumbnails/")
|
||||
|
||||
@app.route("/thumbnails/<img>")
|
||||
@app.route("/thumbnails/<int:w>x<int:h>/<img>")
|
||||
def make_thumbnail(img, w=350, h=233):
|
||||
if not (w, h) in ALLOWED_RESOLUTIONS:
|
||||
abort(403)
|
||||
|
||||
mkdir("app/public/thumbnails/{}x{}/".format(w, h))
|
||||
|
||||
cache_filepath = "public/thumbnails/{}x{}/{}".format(w, h, img)
|
||||
source_filepath = "public/uploads/" + img
|
||||
|
||||
im = Image.open("app/" + source_filepath)
|
||||
im.thumbnail((w, h), Image.ANTIALIAS)
|
||||
im.save("app/" + cache_filepath, optimize=True)
|
||||
return send_file(cache_filepath)
|
||||
@@ -25,15 +25,16 @@ from flask_wtf import FlaskForm
|
||||
from flask_user.forms import RegisterForm
|
||||
from wtforms import *
|
||||
from wtforms.validators import *
|
||||
from app.utils import rank_required, randomString
|
||||
from app.utils import rank_required, randomString, loginUser
|
||||
from app.tasks.forumtasks import checkForumAccount
|
||||
from app.tasks.emails import sendVerifyEmail
|
||||
from app.tasks.phpbbparser import getProfile
|
||||
|
||||
# Define the User profile form
|
||||
class UserProfileForm(FlaskForm):
|
||||
display_name = StringField("Display name", [InputRequired(), Length(2, 20)])
|
||||
email = StringField("Email")
|
||||
rank = SelectField("Rank", [InputRequired()], choices=UserRank.choices(), coerce=UserRank.coerce, default=UserRank.NEW_MEMBER)
|
||||
display_name = StringField("Display name", [Optional(), Length(2, 20)])
|
||||
email = StringField("Email", [Optional(), Email()])
|
||||
rank = SelectField("Rank", [Optional()], choices=UserRank.choices(), coerce=UserRank.coerce, default=UserRank.NEW_MEMBER)
|
||||
submit = SubmitField("Save")
|
||||
|
||||
@app.route("/users/", methods=["GET"])
|
||||
@@ -90,12 +91,78 @@ def user_profile_page(username):
|
||||
# Redirect to home page
|
||||
return redirect(url_for("user_profile_page", username=username))
|
||||
|
||||
packages = user.packages.filter_by(soft_deleted=False)
|
||||
if not current_user.is_authenticated or (user != current_user and not current_user.canAccessTodoList()):
|
||||
packages = packages.filter_by(approved=True)
|
||||
packages = packages.order_by(db.asc(Package.title))
|
||||
|
||||
topics_to_add = None
|
||||
if current_user == user or user.checkPerm(current_user, Permission.CHANGE_AUTHOR):
|
||||
topics_to_add = KrockForumTopic.query \
|
||||
.filter_by(author_id=user.id) \
|
||||
.filter(~ db.exists().where(Package.forums==KrockForumTopic.topic_id)) \
|
||||
.order_by(db.asc(KrockForumTopic.name), db.asc(KrockForumTopic.title)) \
|
||||
.all()
|
||||
|
||||
# Process GET or invalid POST
|
||||
return render_template("users/user_profile_page.html",
|
||||
user=user, form=form)
|
||||
user=user, form=form, packages=packages, topics_to_add=topics_to_add)
|
||||
|
||||
class SetPasswordForm(FlaskForm):
|
||||
email = StringField("Email (Optional)", [Optional(), Email()])
|
||||
password = PasswordField("New password", [InputRequired(), Length(2, 20)])
|
||||
password2 = PasswordField("Verify password", [InputRequired(), Length(2, 20)])
|
||||
submit = SubmitField("Save")
|
||||
|
||||
@app.route("/user/set-password/", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def set_password_page():
|
||||
if current_user.password is not None:
|
||||
return redirect(url_for("user.change_password"))
|
||||
|
||||
form = SetPasswordForm(request.form)
|
||||
if request.method == "POST" and form.validate():
|
||||
one = form.password.data
|
||||
two = form.password2.data
|
||||
if one == two:
|
||||
# Hash password
|
||||
hashed_password = user_manager.hash_password(form.password.data)
|
||||
|
||||
# Change password
|
||||
user_manager.update_password(current_user, hashed_password)
|
||||
|
||||
# Send 'password_changed' email
|
||||
if user_manager.enable_email and user_manager.send_password_changed_email and current_user.email:
|
||||
emails.send_password_changed_email(current_user)
|
||||
|
||||
# Send password_changed signal
|
||||
signals.user_changed_password.send(current_app._get_current_object(), user=current_user)
|
||||
|
||||
# Prepare one-time system message
|
||||
flash('Your password has been changed successfully.', 'success')
|
||||
|
||||
newEmail = form["email"].data
|
||||
if newEmail != current_user.email and newEmail.strip() != "":
|
||||
token = randomString(32)
|
||||
|
||||
ver = UserEmailVerification()
|
||||
ver.user = current_user
|
||||
ver.token = token
|
||||
ver.email = newEmail
|
||||
db.session.add(ver)
|
||||
db.session.commit()
|
||||
|
||||
task = sendVerifyEmail.delay(newEmail, token)
|
||||
return redirect(url_for("check_task", id=task.id, r=url_for("user_profile_page", username=current_user.username)))
|
||||
else:
|
||||
return redirect(url_for("user_profile_page", username=current_user.username))
|
||||
else:
|
||||
flash("Passwords do not match", "error")
|
||||
|
||||
return render_template("users/set_password.html", form=form)
|
||||
|
||||
|
||||
@app.route("/users/claim/", methods=["GET", "POST"])
|
||||
@app.route("/user/claim/", methods=["GET", "POST"])
|
||||
def user_claim_page():
|
||||
username = request.args.get("username")
|
||||
if username is None:
|
||||
@@ -116,8 +183,15 @@ def user_claim_page():
|
||||
if user is not None and method == "github":
|
||||
return redirect(url_for("github_signin_page"))
|
||||
|
||||
token = None
|
||||
if "forum_token" in session:
|
||||
token = session["forum_token"]
|
||||
else:
|
||||
token = randomString(32)
|
||||
session["forum_token"] = token
|
||||
|
||||
if request.method == "POST":
|
||||
ctype = request.form.get("claim_type")
|
||||
ctype = request.form.get("claim_type")
|
||||
username = request.form.get("username")
|
||||
|
||||
if username is None or len(username.strip()) < 2:
|
||||
@@ -126,12 +200,41 @@ def user_claim_page():
|
||||
task = checkForumAccount.delay(username)
|
||||
return redirect(url_for("check_task", id=task.id, r=url_for("user_claim_page", username=username, method="github")))
|
||||
elif ctype == "forum":
|
||||
token = request.form.get("token")
|
||||
flash("Unimplemented", "error")
|
||||
user = User.query.filter_by(forums_username=username).first()
|
||||
if user is not None and user.rank.atLeast(UserRank.NEW_MEMBER):
|
||||
flash("That user has already been claimed!", "error")
|
||||
return redirect(url_for("user_claim_page"))
|
||||
|
||||
# Get signature
|
||||
sig = None
|
||||
try:
|
||||
profile = getProfile("https://forum.minetest.net", username)
|
||||
sig = profile.signature
|
||||
except IOError:
|
||||
flash("Unable to get forum signature - does the user exist?", "error")
|
||||
return redirect(url_for("user_claim_page", username=username))
|
||||
|
||||
# Look for key
|
||||
if token in sig:
|
||||
if user is None:
|
||||
user = User(username)
|
||||
user.forums_username = username
|
||||
db.session.add(user)
|
||||
db.session.commit()
|
||||
|
||||
if loginUser(user):
|
||||
return redirect(url_for("set_password_page"))
|
||||
else:
|
||||
flash("Unable to login as user", "error")
|
||||
return redirect(url_for("user_claim_page", username=username))
|
||||
|
||||
else:
|
||||
flash("Could not find the key in your signature!", "error")
|
||||
return redirect(url_for("user_claim_page", username=username))
|
||||
else:
|
||||
flash("Unknown claim type", "error")
|
||||
|
||||
return render_template("users/claim.html", username=username, key=randomString(32))
|
||||
return render_template("users/claim.html", username=username, key=token)
|
||||
|
||||
@app.route("/users/verify/")
|
||||
def verify_email_page():
|
||||
|
||||
35
migrations/versions/28a427cbd4cf_.py
Normal file
35
migrations/versions/28a427cbd4cf_.py
Normal file
@@ -0,0 +1,35 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: 28a427cbd4cf
|
||||
Revises: e9f534df23a8
|
||||
Create Date: 2018-06-03 01:47:33.006039
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import sqlalchemy.types as ty
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '28a427cbd4cf'
|
||||
down_revision = 'e9f534df23a8'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('user','username', type_=ty.VARCHAR(50, collation='NOCASE'))
|
||||
op.alter_column('user','github_username', type_=ty.VARCHAR(50, collation='NOCASE'))
|
||||
op.alter_column('user','forums_username', type_=ty.VARCHAR(50, collation='NOCASE'))
|
||||
op.create_index(op.f('ix_user_username'), 'user', ['username'], unique=True)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('user','username', type_=ty.VARCHAR(50))
|
||||
op.alter_column('user','github_username', type_=ty.VARCHAR(50))
|
||||
op.alter_column('user','forums_username', type_=ty.VARCHAR(50))
|
||||
op.drop_index(op.f('ix_user_username'), table_name='user')
|
||||
# ### end Alembic commands ###
|
||||
34
migrations/versions/aa6d21889d22_.py
Normal file
34
migrations/versions/aa6d21889d22_.py
Normal file
@@ -0,0 +1,34 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: aa6d21889d22
|
||||
Revises: b254f55eadd2
|
||||
Create Date: 2018-05-29 18:28:28.540416
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'aa6d21889d22'
|
||||
down_revision = 'b254f55eadd2'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('user', 'password',
|
||||
existing_type=sa.VARCHAR(length=255),
|
||||
nullable=True,
|
||||
existing_server_default=sa.text("''"))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('user', 'password',
|
||||
existing_type=sa.VARCHAR(length=255),
|
||||
nullable=False,
|
||||
existing_server_default=sa.text("''"))
|
||||
# ### end Alembic commands ###
|
||||
35
migrations/versions/aa6d7b595a94_.py
Normal file
35
migrations/versions/aa6d7b595a94_.py
Normal file
@@ -0,0 +1,35 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: aa6d7b595a94
|
||||
Revises: aa6d21889d22
|
||||
Create Date: 2018-05-29 20:09:56.647358
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'aa6d7b595a94'
|
||||
down_revision = 'aa6d21889d22'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('package', sa.Column('media_license_id', sa.Integer()))
|
||||
op.execute('UPDATE package SET media_license_id=license_id')
|
||||
op.alter_column('package', 'media_license_id', nullable=False)
|
||||
op.alter_column('package', 'license_id', existing_type=sa.INTEGER(), nullable=False)
|
||||
op.create_foreign_key(None, 'package', 'license', ['media_license_id'], ['id'])
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('package', 'license_id',
|
||||
existing_type=sa.INTEGER(),
|
||||
nullable=True)
|
||||
op.drop_column('package', 'media_license_id')
|
||||
# ### end Alembic commands ###
|
||||
37
migrations/versions/adad68a5e370_.py
Normal file
37
migrations/versions/adad68a5e370_.py
Normal file
@@ -0,0 +1,37 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: adad68a5e370
|
||||
Revises: d0bec9e5698e
|
||||
Create Date: 2018-06-02 18:23:18.123340
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'adad68a5e370'
|
||||
down_revision = 'd0bec9e5698e'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.create_table('krock_forum_topic',
|
||||
sa.Column('topic_id', sa.Integer(), autoincrement=False, nullable=False),
|
||||
sa.Column('author_id', sa.Integer(), nullable=False),
|
||||
sa.Column('ttype', sa.Integer(), nullable=False),
|
||||
sa.Column('title', sa.String(length=200), nullable=False),
|
||||
sa.Column('name', sa.String(length=30), nullable=True),
|
||||
sa.Column('link', sa.String(length=50), nullable=True),
|
||||
sa.ForeignKeyConstraint(['author_id'], ['user.id'], ),
|
||||
sa.PrimaryKeyConstraint('topic_id')
|
||||
)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_table('krock_forum_topic')
|
||||
# ### end Alembic commands ###
|
||||
28
migrations/versions/d0bec9e5698e_.py
Normal file
28
migrations/versions/d0bec9e5698e_.py
Normal file
@@ -0,0 +1,28 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: d0bec9e5698e
|
||||
Revises: aa6d7b595a94
|
||||
Create Date: 2018-05-29 21:23:43.847738
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'd0bec9e5698e'
|
||||
down_revision = 'aa6d7b595a94'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.add_column('license', sa.Column('is_foss', sa.Boolean(), nullable=False, server_default="true"))
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.drop_column('license', 'is_foss')
|
||||
# ### end Alembic commands ###
|
||||
34
migrations/versions/e9f534df23a8_.py
Normal file
34
migrations/versions/e9f534df23a8_.py
Normal file
@@ -0,0 +1,34 @@
|
||||
"""empty message
|
||||
|
||||
Revision ID: e9f534df23a8
|
||||
Revises: adad68a5e370
|
||||
Create Date: 2018-06-02 18:30:54.234366
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = 'e9f534df23a8'
|
||||
down_revision = 'adad68a5e370'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('krock_forum_topic', 'link',
|
||||
existing_type=sa.VARCHAR(length=50),
|
||||
type_=sa.String(length=200),
|
||||
existing_nullable=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
# ### commands auto generated by Alembic - please adjust! ###
|
||||
op.alter_column('package_release', 'link',
|
||||
existing_type=sa.String(length=200),
|
||||
type_=sa.VARCHAR(length=50),
|
||||
existing_nullable=False)
|
||||
# ### end Alembic commands ###
|
||||
@@ -12,3 +12,4 @@ beautifulsoup4==4.6.0
|
||||
lxml==4.2.1
|
||||
Flask-FlatPages==0.6
|
||||
Flask-Migrate==2.1.1
|
||||
pillow==5.1.0
|
||||
|
||||
8
setup.py
8
setup.py
@@ -36,6 +36,7 @@ def defineDummyData(licenses, tags, ruben):
|
||||
|
||||
jeija = User("Jeija")
|
||||
jeija.github_username = "Jeija"
|
||||
jeija.forums_username = "Jeija"
|
||||
db.session.add(jeija)
|
||||
|
||||
|
||||
@@ -358,11 +359,16 @@ for tag in ["Inventory", "Mapgen", "Building", \
|
||||
licenses = {}
|
||||
for license in ["GPLv2.1", "GPLv3", "LGPLv2.1", "LGPLv3", "AGPLv2.1", "AGPLv3",
|
||||
"Apache", "BSD 3-Clause", "BSD 2-Clause", "CC0", "CC-BY-SA",
|
||||
"CC-BY", "CC-BY-NC-SA", "MIT", "ZLib"]:
|
||||
"CC-BY", "MIT", "ZLib"]:
|
||||
row = License(license)
|
||||
licenses[row.name] = row
|
||||
db.session.add(row)
|
||||
|
||||
for license in ["CC-BY-NC-SA"]:
|
||||
row = License(license, False)
|
||||
licenses[row.name] = row
|
||||
db.session.add(row)
|
||||
|
||||
if test_data:
|
||||
defineDummyData(licenses, tags, ruben)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user