Преглед на файлове

User now have an actor; Returns Actor json via Accept: header

pull/1/head
Dashie der otter преди 1 година
родител
ревизия
3c1fa9ed60
променени са 10 файла, в които са добавени 653 реда и са изтрити 256 реда
  1. 1
    0
      .gitignore
  2. 28
    0
      activitypub/utils.py
  3. 11
    1
      app.py
  4. 40
    1
      controllers/users.py
  5. 167
    116
      messages.pot
  6. 67
    0
      migrations/versions/25_32f48de123f3_.py
  7. 26
    0
      migrations/versions/26_d3c41a9e2688_.py
  8. 111
    11
      models.py
  9. 1
    0
      shelltools.py
  10. 201
    127
      translations/fr/LC_MESSAGES/messages.po

+ 1
- 0
.gitignore Целия файл

@@ -11,3 +11,4 @@
/reel2bits.egg-info
/.eggs
/configtest.py
/errors_app.log

+ 28
- 0
activitypub/utils.py Целия файл

@@ -0,0 +1,28 @@
from flask import current_app


def ap_url(klass, username):
if klass == "url":
return f"https://{current_app.config['AP_DOMAIN']}/user/{username}"
elif klass == "shared_inbox":
return f"https://{current_app.config['AP_DOMAIN']}/inbox"
elif klass == "inbox":
return f"https://{current_app.config['AP_DOMAIN']}" \
f"/user/{username}/inbox"
elif klass == "outbox":
return f"https://{current_app.config['AP_DOMAIN']}" \
f"/user/{username}/outbox"
else:
return None


def full_url(path):
if path.startswith("http://") or path.startswith("https://"):
return path
root = current_app.config['AP_DOMAIN']
if path.startswith('/'):
return root + path[1:]
elif path.startswith('/'):
return root + "/" + path
else:
return root + path

+ 11
- 1
app.py Целия файл

@@ -24,7 +24,7 @@ from controllers.api.v1.well_known import bp_wellknown
from controllers.api.v1.nodeinfo import bp_nodeinfo

from forms import ExtendedRegisterForm
from models import db, Config, user_datastore, Role
from models import db, Config, user_datastore, Role, create_actor
from utils import InvalidUsage, is_admin, duration_elapsed_human, \
duration_song_human, add_user_log

@@ -111,6 +111,16 @@ def create_app(config_filename="config.py"):
add_user_log(user.id, user.id, "user", "info",
"Password reset instructions sent.")

@FlaskSecuritySignals.user_registered.connect_via(app)
def create_actor_for_registered_user(app, user, confirm_token):
if not user:
return
actor = create_actor(user)
actor.user = user
actor.user_id = user.id
db.session.add(actor)
db.session.commit()

git_version = ""
gitpath = os.path.join(os.getcwd(), ".git")
if os.path.isdir(gitpath):

+ 40
- 1
controllers/users.py Целия файл

@@ -1,12 +1,13 @@
import pytz
from flask import Blueprint, render_template, request, \
redirect, url_for, flash, Response, json
redirect, url_for, flash, Response, json, jsonify
from flask_babelex import gettext
from flask_security import login_required, current_user

from forms import UserProfileForm
from models import db, User, UserLogging, Sound, Album
from utils import add_user_log
from flask_accept import accept

bp_users = Blueprint('bp_users', __name__)

@@ -44,6 +45,7 @@ def logs_delete(log_id):


@bp_users.route('/user/<string:name>', methods=['GET'])
@accept('text/html')
def profile(name):
pcfg = {"title": gettext(u"%(value)s' profile", value=name)}

@@ -64,6 +66,43 @@ def profile(name):
user=user, sounds=sounds)


@bp_users.route('/user/<string:name>', methods=['GET'])
@profile.support('application/json')
def actor_json(name):
user = User.query.filter(User.name == name).first()
if not user:
return Response("", status=404)
actors = user.actor
if len(actors) <= 0:
return Response("", status=500)
actor = actors[0]

resp = {
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1"
],
"id": actor.url,
"type": actor.type.code,
"preferredUsername": actor.preferred_username,
"inbox": actor.inbox_url,
"outbox": actor.outbox_url,
"manuallyApprovesFollowers": actor.manually_approves_followers,
"publicKey": {
"id": actor.private_key_id(),
"owner": actor.url,
"publicKeyPem": actor.public_key
},
"endpoints": {
"sharedInbox": actor.shared_inbox_url
}
}

response = jsonify(resp)
response.mimetype = "application/activity+json; charset=utf-8"
return response


@bp_users.route('/user/<string:name>/sets', methods=['GET'])
def profile_albums(name):
pcfg = {"title": gettext(u"%(value)s' profile", value=name)}

+ 167
- 116
messages.pot Целия файл

@@ -1,147 +1,183 @@
# Translations template for PROJECT.
# Copyright (C) 2017 ORGANIZATION
# Copyright (C) 2018 ORGANIZATION
# This file is distributed under the same license as the PROJECT project.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2017.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2018.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2017-01-02 07:47+0100\n"
"POT-Creation-Date: 2018-08-04 21:43+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.4\n"
"Generated-By: Babel 2.6.0\n"

#: forms.py:42
msgid "Username required"
#: app.py:143 controllers/admin.py:53
msgid "Config not found"
msgstr ""

#: app.py:181 app.py:188 app.py:195 app.py:203
msgid "Whoops, something failed."
msgstr ""

#: app.py:182
msgid "Page not found"
msgstr ""

#: app.py:189
msgid "Access forbidden"
msgstr ""

#: app.py:196
msgid "Gone"
msgstr ""

#: app.py:205
msgid "Something is broken"
msgstr ""

#: forms.py:46
msgid "Username required"
msgstr ""

#: forms.py:51
msgid "Username already taken"
msgstr ""

#: forms.py:53
#: forms.py:58
msgid "Password"
msgstr ""

#: forms.py:54
#: forms.py:59
msgid "Name"
msgstr ""

#: forms.py:55
#: forms.py:60
msgid "Email"
msgstr ""

#: forms.py:56
#: forms.py:61
msgid "Firstname"
msgstr ""

#: forms.py:57
#: forms.py:62
msgid "Lastname"
msgstr ""

#: forms.py:58
#: forms.py:63
msgid "Timezone"
msgstr ""

#: forms.py:59
#: forms.py:65
msgid "Locale"
msgstr ""

#: forms.py:60
#: forms.py:67
msgid "Update profile"
msgstr ""

#: forms.py:64
msgid "App Name"
#: forms.py:72
msgid "Instance Name"
msgstr ""

#: forms.py:66
#: forms.py:73
msgid "Instance description"
msgstr ""

#: forms.py:75
msgid "Update config"
msgstr ""

#: forms.py:74 forms.py:90 forms.py:106
#: forms.py:87 forms.py:109 forms.py:130
msgid "Title"
msgstr ""

#: forms.py:75
#: forms.py:88
msgid "File"
msgstr ""

#: forms.py:76 forms.py:91 forms.py:108
#: forms.py:90 forms.py:110 forms.py:133
msgid "Private"
msgstr ""

#: forms.py:79 forms.py:95
#: forms.py:93 forms.py:114
msgid "Album"
msgstr ""

#: forms.py:84 forms.py:100
#: forms.py:94 forms.py:115
msgid "No album"
msgstr ""

#: forms.py:98 forms.py:119 templates/sound/show.jinja2:114
msgid "Licence"
msgstr ""

#: forms.py:103 forms.py:124
msgid "Cannot put private sound in public album"
msgstr ""

#: forms.py:86 templates/layout.jinja2:56
#: forms.py:105 templates/layout.jinja2:63
msgid "Upload"
msgstr ""

#: forms.py:92 forms.py:107
#: forms.py:111 forms.py:132
msgid "Description"
msgstr ""

#: forms.py:102
#: forms.py:126
msgid "Edit sound"
msgstr ""

#: forms.py:110
#: forms.py:135
msgid "Save"
msgstr ""

#: controllers/admin.py:17
#: controllers/admin.py:19
msgid "Application Logs"
msgstr ""

#: controllers/admin.py:44
#: controllers/admin.py:49
msgid "Application Config"
msgstr ""

#: controllers/admin.py:48
msgid "Config not found"
msgstr ""

#: controllers/admin.py:57
#: controllers/admin.py:63
msgid "Configuration updated"
msgstr ""

#: controllers/albums.py:14 templates/album/new.jinja2:7
#: templates/layout.jinja2:57
#: controllers/albums.py:17 templates/album/new.jinja2:7
#: templates/layout.jinja2:64
msgid "New album"
msgstr ""

#: controllers/albums.py:27
#: controllers/albums.py:35
msgid "Created !"
msgstr ""

#: controllers/albums.py:37 controllers/sound.py:19 controllers/sound.py:53
#: controllers/users.py:46 controllers/users.py:63 controllers/users.py:81
#: controllers/albums.py:48 controllers/sound.py:21 controllers/users.py:52
#: controllers/users.py:73 controllers/users.py:93
msgid "User not found"
msgstr ""

#: controllers/albums.py:41 controllers/albums.py:47 controllers/albums.py:50
#: controllers/albums.py:63 controllers/albums.py:94
#: controllers/albums.py:53 controllers/albums.py:59 controllers/albums.py:62
#: controllers/albums.py:80
msgid "Album not found"
msgstr ""

#: controllers/albums.py:66 controllers/sound.py:126
#: controllers/albums.py:84 controllers/sound.py:161 controllers/sound.py:203
msgid "Forbidden"
msgstr ""

#: controllers/albums.py:87 controllers/sound.py:164
#, python-format
msgid "Edit %(value)s"
msgstr ""

#: controllers/albums.py:85
#: controllers/albums.py:112
msgid "Public album cannot have private sounds"
msgstr ""

@@ -149,112 +185,128 @@ msgstr ""
msgid "Home"
msgstr ""

#: controllers/sound.py:23 controllers/sound.py:29 controllers/sound.py:32
#: controllers/sound.py:57 controllers/sound.py:63 controllers/sound.py:66
#: controllers/sound.py:123 controllers/sound.py:148
#: controllers/sound.py:33 controllers/sound.py:39 controllers/sound.py:42
#: controllers/sound.py:157 controllers/sound.py:199
msgid "Sound not found"
msgstr ""

#: controllers/sound.py:44
msgid "No HTML5 player supported actually"
msgstr ""

#: controllers/sound.py:82 templates/sound/upload.jinja2:7
#: controllers/sound.py:92 templates/sound/upload.jinja2:7
msgid "New upload"
msgstr ""

#: controllers/sound.py:109
#: controllers/sound.py:139
msgid "Uploaded !"
msgstr ""

#: controllers/users.py:16
#: controllers/users.py:18
msgid "User Logs"
msgstr ""

#: controllers/users.py:42 controllers/users.py:59
#: templates/users/profile.jinja2:5 templates/users/profile_albums.jinja2:5
#: controllers/users.py:48 controllers/users.py:69
#: templates/users/profile.jinja2:13 templates/users/profile_albums.jinja2:13
#, python-format
msgid "%(value)s' profile"
msgstr ""

#: controllers/users.py:77
#: controllers/users.py:89
msgid "Edit my profile"
msgstr ""

#: templates/layout.jinja2:46
#: controllers/users.py:111
msgid "Profile updated"
msgstr ""

#: templates/about.jinja2:19
#, python-format
msgid "reel2bits is developped by %(dashie)s and is available under MIT license."
msgstr ""

#: templates/about.jinja2:21
#, python-format
msgid "Source code is here: %(link)s"
msgstr ""

#: templates/about.jinja2:22
#, python-format
msgid "Issue tracker is here: %(link)s"
msgstr ""

#: templates/error_page.jinja2:7
#, python-format
msgid "Maybe you can go back to the %(link)s."
msgstr ""

#: templates/error_page.jinja2:7
msgid "index"
msgstr ""

#: templates/layout.jinja2:53
msgid "Toggle navigation"
msgstr ""

#: templates/layout.jinja2:58
#: templates/layout.jinja2:65
msgid "My Profile"
msgstr ""

#: templates/layout.jinja2:63
#: templates/layout.jinja2:70
msgid "About"
msgstr ""

#: templates/layout.jinja2:66
#: templates/layout.jinja2:73 templates/layout.jinja2:111
#, python-format
msgid "Logged as %(username)s"
msgstr ""

#: templates/admin/logs.jinja2:24 templates/layout.jinja2:68
#: templates/admin/logs.jinja2:24 templates/layout.jinja2:75
msgid "User"
msgstr ""

#: templates/layout.jinja2:69
#: templates/layout.jinja2:76
msgid "Profile"
msgstr ""

#: templates/layout.jinja2:70
#: templates/layout.jinja2:77
msgid "Logs"
msgstr ""

#: templates/layout.jinja2:73
#: templates/layout.jinja2:80
msgid "Admin"
msgstr ""

#: templates/layout.jinja2:74
#: templates/layout.jinja2:81
msgid "App config"
msgstr ""

#: templates/layout.jinja2:75
#: templates/layout.jinja2:82
msgid "App logs"
msgstr ""

#: templates/layout.jinja2:78
#: templates/layout.jinja2:85
msgid "Change password"
msgstr ""

#: templates/layout.jinja2:79
#: templates/layout.jinja2:86
msgid "Logout"
msgstr ""

#: templates/layout.jinja2:84
#: templates/layout.jinja2:91
msgid "Register"
msgstr ""

#: templates/layout.jinja2:86
#: templates/layout.jinja2:93
msgid "Login"
msgstr ""

#: templates/layout.jinja2:112
#: templates/layout.jinja2:111
#, python-format
msgid ""
"Use template for %(link1)s by %(link2)s and use %(link3)s, reel2bits "
"%(link4)s"
msgstr ""

#: templates/layout.jinja2:116
msgid "source code"
msgid "version: %(version)s"
msgstr ""

#: templates/layout.jinja2:118
#, python-format
msgid "Running version %(version)s"
#: templates/layout.jinja2:114
msgid "Sources"
msgstr ""

#: templates/layout.jinja2:120
#: templates/layout.jinja2:116
msgid "Back to top"
msgstr ""

@@ -306,24 +358,23 @@ msgstr ""
msgid "Actions"
msgstr ""

#: templates/admin/logs.jinja2:40 templates/album/show.jinja2:80
#: templates/album/show.jinja2:116 templates/sound/show.jinja2:78
#: templates/users/user_logs.jinja2:43
#: templates/admin/logs.jinja2:40 templates/album/show.jinja2:92
#: templates/album/show.jinja2:130 templates/sound/show.jinja2:89
#: templates/users/user_logs.jinja2:47
msgid "yes really"
msgstr ""

#: templates/admin/logs.jinja2:40 templates/album/show.jinja2:80
#: templates/album/show.jinja2:116 templates/sound/show.jinja2:78
#: templates/users/user_logs.jinja2:43
#: templates/admin/logs.jinja2:40 templates/album/show.jinja2:92
#: templates/album/show.jinja2:130 templates/sound/show.jinja2:89
#: templates/users/user_logs.jinja2:47
msgid "delete"
msgstr ""

#: templates/admin/logs.jinja2:64 templates/users/user_logs.jinja2:67
#: templates/admin/logs.jinja2:64 templates/users/user_logs.jinja2:71
msgid "Log deletion"
msgstr ""

#: templates/admin/logs.jinja2:64 templates/album/show.jinja2:153
#: templates/users/user_logs.jinja2:67
#: templates/admin/logs.jinja2:64 templates/users/user_logs.jinja2:71
msgid "An error occured"
msgstr ""

@@ -340,29 +391,25 @@ msgstr ""
msgid "Add a nice description ?"
msgstr ""

#: templates/album/edit.jinja2:19 templates/sound/edit.jinja2:23
#: templates/album/edit.jinja2:19 templates/sound/edit.jinja2:24
#: templates/users/edit.jinja2:27
msgid "Cancel edit"
msgstr ""

#: templates/album/show.jinja2:19 templates/sound/show.jinja2:23
#: templates/users/profile.jinja2:40 templates/users/profile_albums.jinja2:29
#: templates/album/show.jinja2:31 templates/sound/show.jinja2:39
#: templates/users/profile.jinja2:48 templates/users/profile_albums.jinja2:37
#, python-format
msgid "%(value)s ago"
msgstr ""

#: templates/album/show.jinja2:112 templates/sound/show.jinja2:74
#: templates/album/show.jinja2:126 templates/sound/show.jinja2:85
msgid "edit"
msgstr ""

#: templates/album/show.jinja2:122
#: templates/album/show.jinja2:136
msgid "This album is empty."
msgstr ""

#: templates/album/show.jinja2:153
msgid "Switch sound"
msgstr ""

#: templates/security/change_password.html:9
msgid "Set a new password"
msgstr ""
@@ -383,43 +430,47 @@ msgstr ""
msgid "Edit upload"
msgstr ""

#: templates/sound/edit.jinja2:19 templates/sound/upload.jinja2:19
#: templates/sound/edit.jinja2:18 templates/sound/upload.jinja2:18
msgid "If you want to add to a new album you need to create it separately."
msgstr ""

#: templates/sound/show.jinja2:12 templates/users/profile.jinja2:57
#: templates/sound/show.jinja2:24 templates/users/profile.jinja2:65
msgid "Please wait, song metadatas are processing..."
msgstr ""

#: templates/sound/show.jinja2:84 templates/users/profile.jinja2:49
#: templates/sound/show.jinja2:28
msgid "Please wait, transcoding is on her way..."
msgstr ""

#: templates/sound/show.jinja2:110 templates/users/profile.jinja2:57
msgid "In album:"
msgstr ""

#: templates/sound/show.jinja2:89
#: templates/sound/show.jinja2:130
msgid "Type"
msgstr ""

#: templates/sound/show.jinja2:90
#: templates/sound/show.jinja2:131
msgid "Codec"
msgstr ""

#: templates/sound/show.jinja2:91
#: templates/sound/show.jinja2:132
msgid "Format"
msgstr ""

#: templates/sound/show.jinja2:92
#: templates/sound/show.jinja2:133
msgid "Channels"
msgstr ""

#: templates/sound/show.jinja2:93
#: templates/sound/show.jinja2:134
msgid "Rate"
msgstr ""

#: templates/sound/show.jinja2:96 templates/sound/show.jinja2:99
#: templates/sound/show.jinja2:137 templates/sound/show.jinja2:140
msgid "Bitrate"
msgstr ""

#: templates/sound/show.jinja2:103
#: templates/sound/show.jinja2:144
msgid "Bitrate mode"
msgstr ""

@@ -428,28 +479,28 @@ msgstr ""
msgid "Edit profile - %(username)s"
msgstr ""

#: templates/users/profile.jinja2:10 templates/users/profile_albums.jinja2:10
#: templates/users/profile.jinja2:18 templates/users/profile_albums.jinja2:18
msgid "all sounds"
msgstr ""

#: templates/users/profile.jinja2:11 templates/users/profile_albums.jinja2:11
#: templates/users/profile.jinja2:19 templates/users/profile_albums.jinja2:19
msgid "all albums"
msgstr ""

#: templates/users/profile.jinja2:15 templates/users/profile_albums.jinja2:15
#: templates/users/profile.jinja2:23 templates/users/profile_albums.jinja2:23
msgid "Edit profile"
msgstr ""

#: templates/users/profile.jinja2:66
#: templates/users/profile.jinja2:74
msgid "This user haven't uploaded any sound :( !"
msgstr ""

#: templates/users/profile_albums.jinja2:30
#: templates/users/profile_albums.jinja2:38
#, python-format
msgid "%(nb)s tracks"
msgstr ""

#: templates/users/profile_albums.jinja2:40
#: templates/users/profile_albums.jinja2:48
msgid "This user haven't created any album !"
msgstr ""

@@ -459,6 +510,6 @@ msgid "%(username)s's logs (latest 100)"
msgstr ""

#: templates/users/user_logs.jinja2:24
msgid "Sound"
msgid "Item ID"
msgstr ""


+ 67
- 0
migrations/versions/25_32f48de123f3_.py Целия файл

@@ -0,0 +1,67 @@
"""Add actor federation

Revision ID: 32f48de123f3
Revises: be5369fae219
Create Date: 2018-08-05 10:42:47.048304

"""

# revision identifiers, used by Alembic.
revision = '32f48de123f3'
down_revision = 'be5369fae219'

from alembic import op # noqa: E402
import sqlalchemy as sa # noqa: E402
import sqlalchemy_utils # noqa: E402
from models import ACTOR_TYPE_CHOICES # noqa: E402


def upgrade():
op.create_table('actor',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('url', sqlalchemy_utils.types.url.URLType(),
nullable=True),
sa.Column('outbox_url',
sqlalchemy_utils.types.url.URLType(),
nullable=True),
sa.Column('inbox_url',
sqlalchemy_utils.types.url.URLType(),
nullable=True),
sa.Column('following_url',
sqlalchemy_utils.types.url.URLType(),
nullable=True),
sa.Column('followers_url',
sqlalchemy_utils.types.url.URLType(),
nullable=True),
sa.Column('shared_inbox_url',
sqlalchemy_utils.types.url.URLType(),
nullable=True),
sa.Column('type', sqlalchemy_utils.types.choice.ChoiceType(
choices=ACTOR_TYPE_CHOICES),
server_default='Person', nullable=True),
sa.Column('name', sa.String(length=200), nullable=True),
sa.Column('domain', sa.String(length=1000),
nullable=False),
sa.Column('summary', sa.String(length=500), nullable=True),
sa.Column('preferred_username', sa.String(length=200),
nullable=True),
sa.Column('public_key', sa.String(length=5000),
nullable=True),
sa.Column('private_key', sa.String(length=5000),
nullable=True),
sa.Column('creation_date', sa.DateTime(), nullable=True),
sa.Column('last_fetch_date', sa.DateTime(), nullable=True),
sa.Column('manually_approves_followers', sa.Boolean(),
nullable=True),
sa.Column('user_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
sa.PrimaryKeyConstraint('id'),
sa.UniqueConstraint('domain', 'preferred_username',
name='_domain_pref_username_uc')
)
op.create_index(op.f('ix_actor_url'), 'actor', ['url'], unique=True)


def downgrade():
op.drop_index(op.f('ix_actor_url'), table_name='actor')
op.drop_table('actor')

+ 26
- 0
migrations/versions/26_d3c41a9e2688_.py Целия файл

@@ -0,0 +1,26 @@
"""Generate Actor for every User

Revision ID: d3c41a9e2688
Revises: 32f48de123f3
Create Date: 2018-08-05 10:47:58.540699

"""

# revision identifiers, used by Alembic.
revision = 'd3c41a9e2688'
down_revision = '32f48de123f3'

from models import db, User, create_actor # noqa: E402


def upgrade():
for user in User.query.all():
a = create_actor(user)
a.user = user
a.user_id = user.id
db.session.add(a)
db.session.commit()


def downgrade():
pass

+ 111
- 11
models.py Целия файл

@@ -1,23 +1,42 @@
import datetime
import os

from flask import current_app
from flask_security import SQLAlchemyUserDatastore, UserMixin, RoleMixin
from flask_sqlalchemy import SQLAlchemy
from slugify import slugify
from sqlalchemy import UniqueConstraint
from sqlalchemy import event
from sqlalchemy.ext.hybrid import Comparator
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.sql import func
from sqlalchemy_searchable import make_searchable
from sqlalchemy.ext.hybrid import Comparator, hybrid_property
from sqlalchemy_utils.types.choice import ChoiceType
from sqlalchemy_utils.types.url import URLType
from little_boxes.key import Key as LittleBoxesKey
from activitypub.utils import ap_url

db = SQLAlchemy()
make_searchable(db.metadata)


# #### Base ####

class CaseInsensitiveComparator(Comparator):
def __eq__(self, other):
return func.lower(self.__clause_element__()) == func.lower(other)


class Config(db.Model):
__tablename__ = "config"

id = db.Column(db.Integer, primary_key=True)
app_name = db.Column(db.String(255), default=None)
app_description = db.Column(db.Text)


# #### User ####

roles_users = db.Table('roles_users',
db.Column('user_id',
db.Integer(),
@@ -110,13 +129,7 @@ class Apitoken(db.Model):
user_datastore = SQLAlchemyUserDatastore(db, User, Role)


class Config(db.Model):
__tablename__ = "config"

id = db.Column(db.Integer, primary_key=True)
app_name = db.Column(db.String(255), default=None)
app_description = db.Column(db.Text)

# #### Logging ####

class Logging(db.Model):
__tablename__ = "logging"
@@ -151,6 +164,8 @@ class UserLogging(db.Model):
__mapper_args__ = {"order_by": timestamp.desc()}


# #### Tracks ####

class SoundInfo(db.Model):
__tablename__ = "sound_info"

@@ -185,10 +200,10 @@ class Sound(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(255), nullable=True)
uploaded = db.Column(db.DateTime(timezone=False),
default=datetime.datetime.utcnow)
default=func.now())
updated = db.Column(db.DateTime(timezone=False),
default=datetime.datetime.utcnow,
onupdate=datetime.datetime.utcnow)
default=func.now(),
onupdate=func.now())
# TODO genre
# TODO tags
# TODO picture ?
@@ -347,3 +362,88 @@ def make_album_slug(mapper, connection, target):
Album.__table__.update().where(
Album.__table__.c.id == target.id).values(slug=slug)
)


# #### Federation ####

ACTOR_TYPE_CHOICES = [
("Person", "Person"),
("Application", "Application"),
("Group", "Group"),
("Organization", "Organization"),
("Service", "Service"),
]


class Actor(db.Model):
__tablename__ = "actor"
ap_type = "Actor"

id = db.Column(db.Integer, primary_key=True)

url = db.Column(URLType(), unique=True, index=True)
outbox_url = db.Column(URLType())
inbox_url = db.Column(URLType())
following_url = db.Column(URLType(), nullable=True)
followers_url = db.Column(URLType(), nullable=True)
shared_inbox_url = db.Column(URLType(), nullable=True)
type = db.Column(ChoiceType(ACTOR_TYPE_CHOICES), server_default="Person")
name = db.Column(db.String(200), nullable=True)
domain = db.Column(db.String(1000), nullable=False)
summary = db.Column(db.String(500), nullable=True)
preferred_username = db.Column(db.String(200), nullable=True)
public_key = db.Column(db.String(5000), nullable=True)
private_key = db.Column(db.String(5000), nullable=True)
creation_date = db.Column(db.DateTime(timezone=False),
default=func.now())
last_fetch_date = db.Column(db.DateTime(timezone=False),
default=func.now())
manually_approves_followers = db.Column(db.Boolean, nullable=True,
server_default=None)
followers = None # relation FIXME

user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=True)
user = db.relationship("User", backref=db.backref('actor'))

__table_args__ = (
UniqueConstraint('domain', 'preferred_username',
name='_domain_pref_username_uc'),
)

def webfinger_subject(self):
return f"{self.preferred_username}@{self.domain}"

def private_key_id(self):
return f"{self.url}#main-key"

def mention_username(self):
return f"@{self.preferred_username}@{self.domain}"

def is_local(self):
return self.domain == current_app.config['AP_DOMAIN']


def create_actor(user):
"""
:param user: an User object
:return: an Actor object
"""
actor = Actor()

# Init a new Keypair for this user
key = LittleBoxesKey(owner=user.name)
key.new()

actor.preferred_username = user.name
actor.domain = current_app.config['AP_DOMAIN']
actor.type = "Person"
actor.name = user.name
actor.manually_approves_followers = False
actor.url = ap_url("url", user.name)
actor.shared_inbox_url = ap_url("shared_inbox", user.name)
actor.inbox_url = ap_url("inbox", user.name)
actor.outbox_url = ap_url("outbox", user.name)
actor.private_key = key.privkey_pem
actor.public_key = key.pubkey_pem

return actor

+ 1
- 0
shelltools.py Целия файл

@@ -0,0 +1 @@
from models import * # noqa: F403, F401

+ 201
- 127
translations/fr/LC_MESSAGES/messages.po Целия файл

@@ -7,143 +7,178 @@ msgid ""
msgstr ""
"Project-Id-Version: PROJECT VERSION\n"
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
"POT-Creation-Date: 2017-01-02 07:47+0100\n"
"POT-Creation-Date: 2018-08-04 21:43+0200\n"
"PO-Revision-Date: 2017-01-02 08:23+0100\n"
"Last-Translator: \n"
"Language: fr\n"
"Language-Team: fr <LL@li.org>\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
"Plural-Forms: nplurals=2; plural=(n > 1)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Generated-By: Babel 2.3.4\n"
"X-Generator: Poedit 1.8.9\n"
"Generated-By: Babel 2.6.0\n"

#: forms.py:42
#: app.py:143 controllers/admin.py:53
msgid "Config not found"
msgstr "Configuration non trouvée"

#: app.py:181 app.py:188 app.py:195 app.py:203
msgid "Whoops, something failed."
msgstr ""

#: app.py:182
msgid "Page not found"
msgstr ""

#: app.py:189
msgid "Access forbidden"
msgstr ""

#: app.py:196
msgid "Gone"
msgstr ""

#: app.py:205
msgid "Something is broken"
msgstr ""

#: forms.py:46
msgid "Username required"
msgstr "Nom d'utilisateur requis"

#: forms.py:46
#: forms.py:51
msgid "Username already taken"
msgstr "Nom d'utilisateur déjà pris"

#: forms.py:53
#: forms.py:58
msgid "Password"
msgstr "Mot de passe"

#: forms.py:54
#: forms.py:59
msgid "Name"
msgstr "Nom"

#: forms.py:55
#: forms.py:60
msgid "Email"
msgstr "Email"

#: forms.py:56
#: forms.py:61
msgid "Firstname"
msgstr "Prénom"

#: forms.py:57
#: forms.py:62
msgid "Lastname"
msgstr "Nom"

#: forms.py:58
#: forms.py:63
msgid "Timezone"
msgstr "Fuseau horaire"

#: forms.py:59
#: forms.py:65
msgid "Locale"
msgstr "Langue"

#: forms.py:60
#: forms.py:67
msgid "Update profile"
msgstr "Mettre a jour le profil"

#: forms.py:64
msgid "App Name"
msgstr "Nom de l'application"
#: forms.py:72
msgid "Instance Name"
msgstr ""

#: forms.py:66
#: forms.py:73
msgid "Instance description"
msgstr ""

#: forms.py:75
msgid "Update config"
msgstr "Mettre à jour la configuration"

#: forms.py:74 forms.py:90 forms.py:106
#: forms.py:87 forms.py:109 forms.py:130
msgid "Title"
msgstr "Titre"

#: forms.py:75
#: forms.py:88
msgid "File"
msgstr "Fichier"

#: forms.py:76 forms.py:91 forms.py:108
#: forms.py:90 forms.py:110 forms.py:133
msgid "Private"
msgstr "Privé"

#: forms.py:79 forms.py:95
#: forms.py:93 forms.py:114
msgid "Album"
msgstr "Album"

#: forms.py:84 forms.py:100
#: forms.py:94 forms.py:115
msgid "No album"
msgstr ""

#: forms.py:98 forms.py:119 templates/sound/show.jinja2:114
msgid "Licence"
msgstr ""

#: forms.py:103 forms.py:124
msgid "Cannot put private sound in public album"
msgstr "Impossible de mettre un son privé dans un album public"

#: forms.py:86 templates/layout.jinja2:56
#: forms.py:105 templates/layout.jinja2:63
msgid "Upload"
msgstr "Uploader"

#: forms.py:92 forms.py:107
#: forms.py:111 forms.py:132
msgid "Description"
msgstr "Description"

#: forms.py:102
#: forms.py:126
msgid "Edit sound"
msgstr "Éditer le son"

#: forms.py:110
#: forms.py:135
msgid "Save"
msgstr "Sauver"

#: controllers/admin.py:17
#: controllers/admin.py:19
msgid "Application Logs"
msgstr "Logs applicatifs"

#: controllers/admin.py:44
#: controllers/admin.py:49
msgid "Application Config"
msgstr "Configuration Applicative"

#: controllers/admin.py:48
msgid "Config not found"
msgstr "Configuration non trouvée"

#: controllers/admin.py:57
#: controllers/admin.py:63
msgid "Configuration updated"
msgstr "Configuration mise-à-jour"

#: controllers/albums.py:14 templates/album/new.jinja2:7
#: templates/layout.jinja2:57
#: controllers/albums.py:17 templates/album/new.jinja2:7
#: templates/layout.jinja2:64
msgid "New album"
msgstr "Nouvel album"

#: controllers/albums.py:27
#: controllers/albums.py:35
msgid "Created !"
msgstr "Crée !"

#: controllers/albums.py:37 controllers/sound.py:19 controllers/sound.py:53
#: controllers/users.py:46 controllers/users.py:63 controllers/users.py:81
#: controllers/albums.py:48 controllers/sound.py:21 controllers/users.py:52
#: controllers/users.py:73 controllers/users.py:93
msgid "User not found"
msgstr "Utilisateur non trouvé"

#: controllers/albums.py:41 controllers/albums.py:47 controllers/albums.py:50
#: controllers/albums.py:63 controllers/albums.py:94
#: controllers/albums.py:53 controllers/albums.py:59 controllers/albums.py:62
#: controllers/albums.py:80
msgid "Album not found"
msgstr "Album non trouvé"

#: controllers/albums.py:66 controllers/sound.py:126
#: controllers/albums.py:84 controllers/sound.py:161 controllers/sound.py:203
msgid "Forbidden"
msgstr ""

#: controllers/albums.py:87 controllers/sound.py:164
#, python-format
msgid "Edit %(value)s"
msgstr "Éditer %(value)s"

#: controllers/albums.py:85
#: controllers/albums.py:112
msgid "Public album cannot have private sounds"
msgstr "Un album public ne peut avoir de son privé"

@@ -151,114 +186,128 @@ msgstr "Un album public ne peut avoir de son privé"
msgid "Home"
msgstr "Accueil"

#: controllers/sound.py:23 controllers/sound.py:29 controllers/sound.py:32
#: controllers/sound.py:57 controllers/sound.py:63 controllers/sound.py:66
#: controllers/sound.py:123 controllers/sound.py:148
#: controllers/sound.py:33 controllers/sound.py:39 controllers/sound.py:42
#: controllers/sound.py:157 controllers/sound.py:199
msgid "Sound not found"
msgstr "Son non trouvé"

#: controllers/sound.py:44
msgid "No HTML5 player supported actually"
msgstr "Aucun lecteur HTML5 actuellement supporté"

#: controllers/sound.py:82 templates/sound/upload.jinja2:7
#: controllers/sound.py:92 templates/sound/upload.jinja2:7
msgid "New upload"
msgstr "Nouvel upload"

#: controllers/sound.py:109
#: controllers/sound.py:139
msgid "Uploaded !"
msgstr "Envoyé !"

#: controllers/users.py:16
#: controllers/users.py:18
msgid "User Logs"
msgstr "Logs utilisateur"

#: controllers/users.py:42 controllers/users.py:59
#: templates/users/profile.jinja2:5 templates/users/profile_albums.jinja2:5
#: controllers/users.py:48 controllers/users.py:69
#: templates/users/profile.jinja2:13 templates/users/profile_albums.jinja2:13
#, python-format
msgid "%(value)s' profile"
msgstr "Profil de %(value)s"

#: controllers/users.py:77
#: controllers/users.py:89
msgid "Edit my profile"
msgstr "Éditer mon profile"

#: templates/layout.jinja2:46
#: controllers/users.py:111
msgid "Profile updated"
msgstr ""

#: templates/about.jinja2:19
#, python-format
msgid "reel2bits is developped by %(dashie)s and is available under MIT license."
msgstr ""

#: templates/about.jinja2:21
#, python-format
msgid "Source code is here: %(link)s"
msgstr ""

#: templates/about.jinja2:22
#, python-format
msgid "Issue tracker is here: %(link)s"
msgstr ""

#: templates/error_page.jinja2:7
#, python-format
msgid "Maybe you can go back to the %(link)s."
msgstr ""

#: templates/error_page.jinja2:7
msgid "index"
msgstr ""

#: templates/layout.jinja2:53
msgid "Toggle navigation"
msgstr "Inverser la navigation"

#: templates/layout.jinja2:58
#: templates/layout.jinja2:65
msgid "My Profile"
msgstr "Mon profil"

#: templates/layout.jinja2:63
#: templates/layout.jinja2:70
msgid "About"
msgstr "À propos"

#: templates/layout.jinja2:66
#: templates/layout.jinja2:73 templates/layout.jinja2:111
#, python-format
msgid "Logged as %(username)s"
msgstr "Connecté sous %(username)s"

#: templates/admin/logs.jinja2:24 templates/layout.jinja2:68
#: templates/admin/logs.jinja2:24 templates/layout.jinja2:75
msgid "User"
msgstr "Utilisateur"

#: templates/layout.jinja2:69
#: templates/layout.jinja2:76
msgid "Profile"
msgstr "Profile"

#: templates/layout.jinja2:70
#: templates/layout.jinja2:77
msgid "Logs"
msgstr "Logs"

#: templates/layout.jinja2:73
#: templates/layout.jinja2:80
msgid "Admin"
msgstr "Administrateur"

#: templates/layout.jinja2:74
#: templates/layout.jinja2:81
msgid "App config"
msgstr "Configuration applicative"

#: templates/layout.jinja2:75
#: templates/layout.jinja2:82
msgid "App logs"
msgstr "Logs applicatifs"

#: templates/layout.jinja2:78
#: templates/layout.jinja2:85
msgid "Change password"
msgstr "Changer mot de passe"

#: templates/layout.jinja2:79
#: templates/layout.jinja2:86
msgid "Logout"
msgstr "Se déconnecter"

#: templates/layout.jinja2:84
#: templates/layout.jinja2:91
msgid "Register"
msgstr "S'enregistrer"

#: templates/layout.jinja2:86
#: templates/layout.jinja2:93
msgid "Login"
msgstr "Se connecter"

#: templates/layout.jinja2:112
#: templates/layout.jinja2:111
#, python-format
msgid ""
"Use template for %(link1)s by %(link2)s and use %(link3)s, reel2bits "
"%(link4)s"
msgid "version: %(version)s"
msgstr ""
"Utilise une template pour %(link1)s par %(link2)s et utilise %(link3)s, "
"reel2bits %(link4)s"

#: templates/layout.jinja2:116
msgid "source code"
msgstr "code source"

#: templates/layout.jinja2:118
#, python-format
msgid "Running version %(version)s"
msgstr "Version en cours %(version)s"
#: templates/layout.jinja2:114
msgid "Sources"
msgstr ""

#: templates/layout.jinja2:120
#: templates/layout.jinja2:116
msgid "Back to top"
msgstr "Retour en haut"

@@ -310,24 +359,23 @@ msgstr "Message"
msgid "Actions"
msgstr "Actions"

#: templates/admin/logs.jinja2:40 templates/album/show.jinja2:80
#: templates/album/show.jinja2:116 templates/sound/show.jinja2:78
#: templates/users/user_logs.jinja2:43
#: templates/admin/logs.jinja2:40 templates/album/show.jinja2:92
#: templates/album/show.jinja2:130 templates/sound/show.jinja2:89
#: templates/users/user_logs.jinja2:47
msgid "yes really"
msgstr "oui vraiment"

#: templates/admin/logs.jinja2:40 templates/album/show.jinja2:80
#: templates/album/show.jinja2:116 templates/sound/show.jinja2:78
#: templates/users/user_logs.jinja2:43
#: templates/admin/logs.jinja2:40 templates/album/show.jinja2:92
#: templates/album/show.jinja2:130 templates/sound/show.jinja2:89
#: templates/users/user_logs.jinja2:47
msgid "delete"
msgstr "supprimer"

#: templates/admin/logs.jinja2:64 templates/users/user_logs.jinja2:67
#: templates/admin/logs.jinja2:64 templates/users/user_logs.jinja2:71
msgid "Log deletion"
msgstr "Suppression de logs"

#: templates/admin/logs.jinja2:64 templates/album/show.jinja2:153
#: templates/users/user_logs.jinja2:67
#: templates/admin/logs.jinja2:64 templates/users/user_logs.jinja2:71
msgid "An error occured"
msgstr "Une erreur est survenue"

@@ -344,29 +392,25 @@ msgstr "Quel est le titre ?"
msgid "Add a nice description ?"
msgstr "Une description sympathique ?"

#: templates/album/edit.jinja2:19 templates/sound/edit.jinja2:23
#: templates/album/edit.jinja2:19 templates/sound/edit.jinja2:24
#: templates/users/edit.jinja2:27
msgid "Cancel edit"
msgstr "Annuler l'édition"

#: templates/album/show.jinja2:19 templates/sound/show.jinja2:23
#: templates/users/profile.jinja2:40 templates/users/profile_albums.jinja2:29
#: templates/album/show.jinja2:31 templates/sound/show.jinja2:39
#: templates/users/profile.jinja2:48 templates/users/profile_albums.jinja2:37
#, python-format
msgid "%(value)s ago"
msgstr "il-y-à %(value)s"

#: templates/album/show.jinja2:112 templates/sound/show.jinja2:74
#: templates/album/show.jinja2:126 templates/sound/show.jinja2:85
msgid "edit"
msgstr "éditer"

#: templates/album/show.jinja2:122
#: templates/album/show.jinja2:136
msgid "This album is empty."
msgstr "Cet album est vide."

#: templates/album/show.jinja2:153
msgid "Switch sound"
msgstr "Changer le son"

#: templates/security/change_password.html:9
msgid "Set a new password"
msgstr "Changer le mot de passe"
@@ -387,44 +431,47 @@ msgstr "Enregistrer un utilisateur"
msgid "Edit upload"
msgstr "Éditer l'upload"

#: templates/sound/edit.jinja2:19 templates/sound/upload.jinja2:19
#: templates/sound/edit.jinja2:18 templates/sound/upload.jinja2:18
msgid "If you want to add to a new album you need to create it separately."
msgstr ""
"Si vous voulez ajouter à un nouvel album, vous devez le créer séparément."
msgstr "Si vous voulez ajouter à un nouvel album, vous devez le créer séparément."

#: templates/sound/show.jinja2:12 templates/users/profile.jinja2:57
#: templates/sound/show.jinja2:24 templates/users/profile.jinja2:65
msgid "Please wait, song metadatas are processing..."
msgstr "Veuillez attendre, les métadonnés sont en cours de traitement..."

#: templates/sound/show.jinja2:84 templates/users/profile.jinja2:49
#: templates/sound/show.jinja2:28
msgid "Please wait, transcoding is on her way..."
msgstr ""

#: templates/sound/show.jinja2:110 templates/users/profile.jinja2:57
msgid "In album:"
msgstr "Dans l'album:"

#: templates/sound/show.jinja2:89
#: templates/sound/show.jinja2:130
msgid "Type"
msgstr "Type"

#: templates/sound/show.jinja2:90
#: templates/sound/show.jinja2:131
msgid "Codec"
msgstr "Encodeur"

#: templates/sound/show.jinja2:91
#: templates/sound/show.jinja2:132
msgid "Format"
msgstr "Format"

#: templates/sound/show.jinja2:92
#: templates/sound/show.jinja2:133
msgid "Channels"
msgstr "Cannaux"

#: templates/sound/show.jinja2:93
#: templates/sound/show.jinja2:134
msgid "Rate"
msgstr "Rate"

#: templates/sound/show.jinja2:96 templates/sound/show.jinja2:99
#: templates/sound/show.jinja2:137 templates/sound/show.jinja2:140
msgid "Bitrate"
msgstr "Bitrate"

#: templates/sound/show.jinja2:103
#: templates/sound/show.jinja2:144
msgid "Bitrate mode"
msgstr "Bitrate mode"

@@ -433,28 +480,28 @@ msgstr "Bitrate mode"
msgid "Edit profile - %(username)s"
msgstr "Éditer le profil - %(username)s"

#: templates/users/profile.jinja2:10 templates/users/profile_albums.jinja2:10
#: templates/users/profile.jinja2:18 templates/users/profile_albums.jinja2:18
msgid "all sounds"
msgstr "tous les sons"

#: templates/users/profile.jinja2:11 templates/users/profile_albums.jinja2:11
#: templates/users/profile.jinja2:19 templates/users/profile_albums.jinja2:19
msgid "all albums"
msgstr "tous les albums"

#: templates/users/profile.jinja2:15 templates/users/profile_albums.jinja2:15
#: templates/users/profile.jinja2:23 templates/users/profile_albums.jinja2:23
msgid "Edit profile"
msgstr "Éditer le profil"

#: templates/users/profile.jinja2:66
#: templates/users/profile.jinja2:74
msgid "This user haven't uploaded any sound :( !"
msgstr "Cet utilisateur n'a rien uploadé :( !"

#: templates/users/profile_albums.jinja2:30
#: templates/users/profile_albums.jinja2:38
#, python-format
msgid "%(nb)s tracks"
msgstr "%(nb)s pistes"

#: templates/users/profile_albums.jinja2:40
#: templates/users/profile_albums.jinja2:48
msgid "This user haven't created any album !"
msgstr "Cet utilisateur n'a crée aucuns album !"

@@ -464,15 +511,15 @@ msgid "%(username)s's logs (latest 100)"
msgstr "Logs de %(username)s (100 derniers)"

#: templates/users/user_logs.jinja2:24
msgid "Sound"
msgstr "Son"
msgid "Item ID"
msgstr ""

#~ msgid ""
#~ "reel2bits is developped by %(dashie)s and is available under MIT "
#~ "license."
#~ "reel2bits is developped by %(dashie)s "
#~ "and is available under MIT license."
#~ msgstr ""
#~ "reel2bits est développé par %(dashie)s et est disponible sous licence "
#~ "MIT."
#~ "reel2bits est développé par %(dashie)s "
#~ "et est disponible sous licence MIT."

#~ msgid "Source code is here: %(link)s"
#~ msgstr "Code source disponible: %(link)s"
@@ -485,3 +532,30 @@ msgstr "Son"

#~ msgid "index"
#~ msgstr "accueil"

#~ msgid "App Name"
#~ msgstr "Nom de l'application"

#~ msgid "No HTML5 player supported actually"
#~ msgstr "Aucun lecteur HTML5 actuellement supporté"

#~ msgid ""
#~ "Use template for %(link1)s by %(link2)s"
#~ " and use %(link3)s, reel2bits %(link4)s"
#~ msgstr ""
#~ "Utilise une template pour %(link1)s par"
#~ " %(link2)s et utilise %(link3)s, reel2bits"
#~ " %(link4)s"

#~ msgid "source code"
#~ msgstr "code source"

#~ msgid "Running version %(version)s"
#~ msgstr "Version en cours %(version)s"

#~ msgid "Switch sound"
#~ msgstr "Changer le son"

#~ msgid "Sound"
#~ msgstr "Son"


Loading…
Отказ
Запис