mirror of
https://github.com/minetest/contentdb.git
synced 2024-11-08 08:33:45 +01:00
Initial commit
This commit is contained in:
commit
366a2302d0
172
.gitignore
vendored
Normal file
172
.gitignore
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
config.cfg
|
||||
*.sqlite
|
||||
main.css
|
||||
|
||||
|
||||
# Created by https://www.gitignore.io/api/linux,macos,python,windows
|
||||
|
||||
### Linux ###
|
||||
*~
|
||||
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
# Linux trash folder which might appear on any partition or disk
|
||||
.Trash-*
|
||||
|
||||
# .nfs files are created when an open file is removed but is still being accessed
|
||||
.nfs*
|
||||
|
||||
### macOS ###
|
||||
*.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### Python ###
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
.pytest_cache/
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
.hypothesis/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# celery beat schedule file
|
||||
celerybeat-schedule.*
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
|
||||
### Windows ###
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Folder config file
|
||||
Desktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
|
||||
# End of https://www.gitignore.io/api/linux,macos,python,windows
|
7
app/__init__.py
Normal file
7
app/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
from flask import *
|
||||
from flask_user import *
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config.from_pyfile('../config.cfg')
|
||||
|
||||
import models, views
|
72
app/models.py
Normal file
72
app/models.py
Normal file
@ -0,0 +1,72 @@
|
||||
from flask import Flask, url_for
|
||||
from flask.ext.sqlalchemy import SQLAlchemy
|
||||
from app import app
|
||||
from datetime import datetime
|
||||
from sqlalchemy.orm import validates
|
||||
from flask_user import login_required, UserManager, UserMixin, SQLAlchemyAdapter
|
||||
|
||||
# Initialise database
|
||||
db = SQLAlchemy(app)
|
||||
|
||||
def title_to_url(title):
|
||||
return title.lower().replace(" ", "_")
|
||||
|
||||
def url_to_title(url):
|
||||
return url.replace("_", " ")
|
||||
|
||||
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='')
|
||||
reset_password_token = db.Column(db.String(100), nullable=False, server_default='')
|
||||
|
||||
# User email information
|
||||
email = db.Column(db.String(255), nullable=True, unique=True)
|
||||
confirmed_at = db.Column(db.DateTime())
|
||||
|
||||
# User information
|
||||
active = db.Column('is_active', db.Boolean, nullable=False, server_default='0')
|
||||
display_name = db.Column(db.String(100), nullable=False, server_default='')
|
||||
|
||||
# Content
|
||||
mods = db.relationship('Mod', backref='author', lazy='dynamic')
|
||||
|
||||
def __init__(self, username):
|
||||
import datetime
|
||||
|
||||
self.username = username
|
||||
self.confirmed_at = datetime.datetime.now() - datetime.timedelta(days=6000)
|
||||
|
||||
def isClaimed(self):
|
||||
return self.password is not None and self.password != ""
|
||||
|
||||
class Role(db.Model):
|
||||
id = db.Column(db.Integer(), primary_key=True)
|
||||
name = db.Column(db.String(50), unique=True)
|
||||
description = db.Column(db.String(255))
|
||||
|
||||
class UserRoles(db.Model):
|
||||
id = db.Column(db.Integer(), primary_key=True)
|
||||
user_id = db.Column(db.Integer(), db.ForeignKey('user.id', ondelete='CASCADE'))
|
||||
role_id = db.Column(db.Integer(), db.ForeignKey('role.id', ondelete='CASCADE'))
|
||||
|
||||
class Mod(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
|
||||
# Basic details
|
||||
author_id = db.Column(db.Integer, db.ForeignKey('user.id'))
|
||||
name = db.Column(db.String(100), nullable=False)
|
||||
title = db.Column(db.String(100), nullable=False)
|
||||
desc = db.Column(db.Text, nullable=True)
|
||||
|
||||
# Downloads
|
||||
repo = db.Column(db.String(200), nullable=True)
|
||||
website = db.Column(db.String(200), nullable=True)
|
||||
issueTracker = db.Column(db.String(200), nullable=True)
|
||||
forums = db.Column(db.String(200), nullable=False)
|
||||
|
||||
# Setup Flask-User
|
||||
db_adapter = SQLAlchemyAdapter(db, User) # Register the User model
|
||||
user_manager = UserManager(db_adapter, app) # Initialize Flask-User
|
BIN
app/static/screenshot.png
Normal file
BIN
app/static/screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 224 KiB |
245
app/static/style.css
Normal file
245
app/static/style.css
Normal file
@ -0,0 +1,245 @@
|
||||
html, body {
|
||||
font-family: "Arial", sans-serif;
|
||||
background: #222;
|
||||
color: #ddd;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
h2, h3 {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #0be;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #0df;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Containers */
|
||||
|
||||
.box {
|
||||
border-radius: 5px;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.box_grey {
|
||||
padding: 10px;
|
||||
background: #333;
|
||||
border: 1px solid #444;
|
||||
}
|
||||
|
||||
.ul_boxes {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.ul_boxes > li {
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.box_link {
|
||||
display: block;
|
||||
color: #ddd;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.box_link:hover{
|
||||
background: #3a3a3a;
|
||||
}
|
||||
|
||||
/*
|
||||
buttonset
|
||||
*/
|
||||
|
||||
.buttonset, .buttonset li {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.buttonset {
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.buttonset li a {
|
||||
text-align: center;
|
||||
color: #ddd;
|
||||
text-decoration: none;
|
||||
margin: 5px 0 !important;
|
||||
}
|
||||
|
||||
.buttonset li a:hover {
|
||||
background: #444;
|
||||
}
|
||||
|
||||
.btn_green {
|
||||
background: #363 !important;
|
||||
border: 1px solid #473;
|
||||
}
|
||||
|
||||
.btn_green:hover {
|
||||
background: #474 !important;
|
||||
}
|
||||
|
||||
/* Alerts */
|
||||
|
||||
#alerts {
|
||||
list-style: none;
|
||||
position: fixed;
|
||||
bottom: 15px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
#alerts .alert {
|
||||
margin: 5px 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#alerts .close {
|
||||
float: right;
|
||||
color: white;
|
||||
}
|
||||
|
||||
#alerts .close:hover {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.alert-error {
|
||||
background: #933;
|
||||
border: 1px solid #c44;
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
background: #963;
|
||||
border: 1px solid #c96;
|
||||
}
|
||||
|
||||
/* Nav */
|
||||
|
||||
nav, main, #alerts {
|
||||
width: 90%;
|
||||
max-width: 960px;
|
||||
margin: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
nav {
|
||||
margin: 15px auto 5px auto;
|
||||
list-style: none;
|
||||
background: #333;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #444;
|
||||
}
|
||||
|
||||
nav .navbar-nav {
|
||||
float: left;
|
||||
}
|
||||
|
||||
nav .navbar-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
nav ul {
|
||||
margin: 0 auto 0 auto;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
nav li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
nav li a {
|
||||
color: #ddd;
|
||||
margin: 0;
|
||||
padding: 10px 20px;
|
||||
display: block;
|
||||
border-left: 1px solid #444;
|
||||
}
|
||||
|
||||
nav a:hover {
|
||||
color: #eee;
|
||||
background: #444;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
||||
/* Footer */
|
||||
|
||||
footer {
|
||||
width: 80%;
|
||||
max-width: 860px;
|
||||
margin: auto;
|
||||
padding: 50px 0 20px 0;
|
||||
}
|
||||
|
||||
footer a {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
|
||||
/* Mod */
|
||||
|
||||
.box_img {
|
||||
position: relative;
|
||||
background-position: center;
|
||||
background-size: cover;
|
||||
background-image: url("screenshot.png");
|
||||
min-height: 220px;
|
||||
border-radius: 5px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.box_img > h2 {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
bottom: 15px;
|
||||
left: 15px;
|
||||
}
|
||||
|
||||
.sidebar_container {
|
||||
display: block;
|
||||
position: relative;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.sidebar_container .right, .sidebar_container .left{
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: 10px;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.sidebar_container .right {
|
||||
right: 0;
|
||||
width: 280px;
|
||||
}
|
||||
|
||||
.sidebar_container .left {
|
||||
right: 295px;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.sidebar_container .right > *:first-child, .sidebar_container .left > *:first-child {
|
||||
margin-top: 0;
|
||||
}
|
74
app/templates/base.html
Normal file
74
app/templates/base.html
Normal file
@ -0,0 +1,74 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>{% block title %}title{% endblock %} - {{ config.USER_APP_NAME }}</title>
|
||||
<link rel="stylesheet" type="text/css" href="/static/style.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav>
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="/">{{ config.USER_APP_NAME }}</a></li>
|
||||
{% for item in current_menu.children recursive %}
|
||||
<li{% if item.children %} class="dropdown"{% endif %}>
|
||||
<a href="{{ item.url }}"
|
||||
{% if item.children %}
|
||||
class="dropdown-toggle"
|
||||
data-toggle="dropdown"
|
||||
role="button"
|
||||
aria-expanded="false"
|
||||
{% endif %}>
|
||||
{{ item.text }}
|
||||
{% if item.children %}
|
||||
<span class="caret"></span>
|
||||
{% endif %}
|
||||
</a>
|
||||
{% if item.children %}
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
{{ loop(item.children) }}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
{% if current_user.is_authenticated %}
|
||||
<li><a href="{{ url_for('user_profile_page') }}">{{ current_user.first_name or current_user.username }}</a></li>
|
||||
<li><a href="{{ url_for('user.logout') }}">Sign out</a></li>
|
||||
{% else %}
|
||||
<li><a href="{{ url_for('user.login') }}">Sign in</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<div style="clear:both;"></div>
|
||||
</nav>
|
||||
|
||||
|
||||
{% block flash_messages %}
|
||||
{%- with messages = get_flashed_messages(with_categories=true) -%}
|
||||
{% if messages %}
|
||||
<ul id="alerts">
|
||||
{% for category, message in messages %}
|
||||
<li class="box box_grey alert alert-{{category}}">
|
||||
<span class="icon_message"></span>
|
||||
|
||||
{{ message|safe }}
|
||||
|
||||
<div style="clear: both;"></div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{%- endwith %}
|
||||
{% endblock %}
|
||||
|
||||
{% block container %}
|
||||
<main>
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
{% endblock %}
|
||||
</html>
|
76
app/templates/flask_user/login.html
Normal file
76
app/templates/flask_user/login.html
Normal file
@ -0,0 +1,76 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
Sign in
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="sidebar_container">
|
||||
<div class="left box box_grey">
|
||||
{% from "flask_user/_macros.html" import render_field, render_checkbox_field, render_submit_field %}
|
||||
<h2>{%trans%}Sign in{%endtrans%}</h2>
|
||||
|
||||
<form action="" method="POST" class="form" role="form">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
{# Username or Email field #}
|
||||
{% set field = form.username if user_manager.enable_username else form.email %}
|
||||
<div class="form-group {% if field.errors %}has-error{% endif %}">
|
||||
{# Label on left, "New here? Register." on right #}
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<label for="{{ field.id }}" class="control-label">{{ field.label.text }}</label>
|
||||
</div>
|
||||
</div>
|
||||
{{ field(class_='form-control', tabindex=110) }}
|
||||
{% if field.errors %}
|
||||
{% for e in field.errors %}
|
||||
<p class="help-block">{{ e }}</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{# Password field #}
|
||||
{% set field = form.password %}
|
||||
<div class="form-group {% if field.errors %}has-error{% endif %}">
|
||||
{# Label on left, "Forgot your Password?" on right #}
|
||||
<div class="row">
|
||||
<div class="col-xs-6">
|
||||
<label for="{{ field.id }}" class="control-label">{{ field.label.text }}</label>
|
||||
</div>
|
||||
<div class="col-xs-6 text-right">
|
||||
{% if user_manager.enable_forgot_password %}
|
||||
<a href="{{ url_for('user.forgot_password') }}" tabindex='195'>
|
||||
{%trans%}Forgot your Password?{%endtrans%}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{{ field(class_='form-control', tabindex=120) }}
|
||||
{% if field.errors %}
|
||||
{% for e in field.errors %}
|
||||
<p class="help-block">{{ e }}</p>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{# Remember me #}
|
||||
{% if user_manager.enable_remember_me %}
|
||||
{{ render_checkbox_field(login_form.remember_me, tabindex=130) }}
|
||||
{% endif %}
|
||||
|
||||
{# Submit button #}
|
||||
{{ render_submit_field(form.submit, tabindex=180) }}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<aside class="box box_grey">
|
||||
<h2>New here?</h2>
|
||||
|
||||
{% if user_manager.enable_register and not user_manager.require_invitation %}
|
||||
<a href="">{%trans%}Create an account{%endtrans%}</a>
|
||||
{% endif %}
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
10
app/templates/flask_user/public_base.html
Normal file
10
app/templates/flask_user/public_base.html
Normal file
@ -0,0 +1,10 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block container %}
|
||||
<main>
|
||||
<div class="box box_grey">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</main>
|
||||
{% endblock %}
|
35
app/templates/index.html
Normal file
35
app/templates/index.html
Normal file
@ -0,0 +1,35 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}
|
||||
Dashboard
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="box box_grey">
|
||||
<h2>{{ self.title() }}</h2>
|
||||
|
||||
{% if current_user.is_authenticated %}
|
||||
<p>
|
||||
Hello user!
|
||||
</p>
|
||||
{% else %}
|
||||
<p>
|
||||
Please login!
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="2box">
|
||||
<div class="box box_grey">
|
||||
<h2>Top Mods</h2>
|
||||
</div>
|
||||
<div class="box box_grey">
|
||||
<h2>Statistics</h2>
|
||||
<ul>
|
||||
<li>Total mods: 543</li>
|
||||
<li>Missing mods: 1020</li>
|
||||
<li>Downloads/day: 200</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
74
app/views/__init__.py
Normal file
74
app/views/__init__.py
Normal file
@ -0,0 +1,74 @@
|
||||
from app import app
|
||||
from flask import *
|
||||
from flask_user import *
|
||||
from flask_login import login_user, logout_user
|
||||
from app.models import *
|
||||
from flask.ext import menu, markdown
|
||||
from sqlalchemy import func
|
||||
from werkzeug.contrib.cache import SimpleCache
|
||||
cache = SimpleCache()
|
||||
|
||||
menu.Menu(app=app)
|
||||
markdown.Markdown(app, extensions=['fenced_code'])
|
||||
|
||||
# TODO: remove on production!
|
||||
@app.route('/static/<path:path>')
|
||||
def send_static(path):
|
||||
return send_from_directory('static', path)
|
||||
|
||||
@app.route('/')
|
||||
@menu.register_menu(app, '.', 'Home')
|
||||
def home_page():
|
||||
return render_template('index.html')
|
||||
|
||||
# Define the User registration form
|
||||
# It augments the Flask-User RegisterForm with additional fields
|
||||
from flask_user.forms import RegisterForm
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import StringField, SubmitField, validators
|
||||
class MyRegisterForm(RegisterForm):
|
||||
first_name = StringField('First name', validators=[
|
||||
validators.DataRequired('First name is required')])
|
||||
last_name = StringField('Last name', validators=[
|
||||
validators.DataRequired('Last name is required')])
|
||||
|
||||
# Define the User profile form
|
||||
class UserProfileForm(FlaskForm):
|
||||
first_name = StringField('First name', validators=[
|
||||
validators.DataRequired('First name is required')])
|
||||
last_name = StringField('Last name', validators=[
|
||||
validators.DataRequired('Last name is required')])
|
||||
submit = SubmitField('Save')
|
||||
|
||||
@app.route('/user/', methods=['GET', 'POST'])
|
||||
@app.route('/user/<username>/', methods=['GET'])
|
||||
def user_profile_page(username=None):
|
||||
user = None
|
||||
form = None
|
||||
if username is None:
|
||||
if not current_user.is_authenticated:
|
||||
return current_app.login_manager.unauthorized()
|
||||
user = current_user
|
||||
else:
|
||||
user = User.query.filter_by(username=username).first()
|
||||
if not user:
|
||||
abort(404)
|
||||
|
||||
if user == current_user:
|
||||
# Initialize form
|
||||
form = UserProfileForm(request.form, current_user)
|
||||
|
||||
# Process valid POST
|
||||
if request.method=='POST' and form.validate():
|
||||
# Copy form fields to user_profile fields
|
||||
form.populate_obj(current_user)
|
||||
|
||||
# Save user_profile
|
||||
db.session.commit()
|
||||
|
||||
# Redirect to home page
|
||||
return redirect(url_for('home_page'))
|
||||
|
||||
# Process GET or invalid POST
|
||||
return render_template('users/user_profile_page.html',
|
||||
user=user, form=form)
|
6
config.example.cfg
Normal file
6
config.example.cfg
Normal file
@ -0,0 +1,6 @@
|
||||
USER_APP_NAME="Content DB"
|
||||
|
||||
SECRET_KEY=""
|
||||
WTF_CSRF_SECRET_KEY=""
|
||||
|
||||
SQLALCHEMY_DATABASE_URI = "sqlite:///../db.sqlite"
|
5
requirements.txt
Normal file
5
requirements.txt
Normal file
@ -0,0 +1,5 @@
|
||||
Flask>=0.10.1
|
||||
Flask-SQLAlchemy>=2.1
|
||||
Flask-User>=0.6.9
|
||||
Flask-Menu>=0.5.0
|
||||
Flask-Markdown>=0.3
|
3
rundebug.py
Normal file
3
rundebug.py
Normal file
@ -0,0 +1,3 @@
|
||||
from app import app
|
||||
|
||||
app.run(host='0.0.0.0', port=5000, debug=True)
|
17
setup.py
Normal file
17
setup.py
Normal file
@ -0,0 +1,17 @@
|
||||
import os, datetime
|
||||
|
||||
delete_db = False
|
||||
|
||||
if delete_db and os.path.isfile("app/data.sqlite"):
|
||||
os.remove("app/data.sqlite")
|
||||
|
||||
if not os.path.isfile("app/data.sqlite"):
|
||||
from app import models
|
||||
|
||||
print("Creating database tables...")
|
||||
models.db.create_all()
|
||||
|
||||
print("Filling database...")
|
||||
models.db.session.commit()
|
||||
else:
|
||||
print("Database already exists")
|
Loading…
Reference in New Issue
Block a user