From df32bb51a7ffb80e812e08be521def15189a104b Mon Sep 17 00:00:00 2001 From: Alexandr Topilski Date: Tue, 11 Jun 2019 07:16:46 +0300 Subject: [PATCH] Subscribers (#4) * Subscribers start impl * Send subscribers to service * Add subscribers * Review * User agent --- CHANGELOG | 5 + app/client/client.py | 5 +- app/common_entries.py | 2 +- app/common_forms.py | 8 +- app/config/public_config.py | 2 +- app/constants.py | 19 ++++ app/service/forms.py | 4 + app/service/server_entry.py | 8 ++ app/service/service.py | 4 +- app/service/service_client.py | 35 +++++-- app/service/service_entry.py | 20 ++++ app/service/view.py | 20 +++- app/stream/stream_entry.py | 47 +++++++++ app/subscriber/forms.py | 26 +++++ app/subscriber/subscriber_entry.py | 105 +++++++++++++++++++++ app/templates/service/base.html | 4 + app/templates/service/subscriber/add.html | 4 + app/templates/service/subscriber/base.html | 45 +++++++++ app/templates/service/subscriber/edit.html | 4 + app/templates/service/user/edit.html | 2 +- app/templates/user/dashboard.html | 10 +- app/templates/user/settings.html | 37 +++++++- 22 files changed, 388 insertions(+), 28 deletions(-) create mode 100644 app/subscriber/forms.py create mode 100644 app/subscriber/subscriber_entry.py create mode 100644 app/templates/service/subscriber/add.html create mode 100644 app/templates/service/subscriber/base.html create mode 100644 app/templates/service/subscriber/edit.html diff --git a/CHANGELOG b/CHANGELOG index 73188d2..895d43a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +1.2.0 / +[Alexandr Topilski] +- User agents +- Subscribers + 1.1.0 / June 6, 2019 [Alexandr Topilski] - User type diff --git a/app/client/client.py b/app/client/client.py index 6921531..9253790 100644 --- a/app/client/client.py +++ b/app/client/client.py @@ -44,6 +44,7 @@ class Client: VODS_IN_DIRECTORY = 'vods_in_directory' VODS_DIRECTORY = 'vods_directory' STREAMS = 'streams' + SUBSCRIBERS = 'subscribers' STREAM_ID = 'id' LICENSE_KEY = 'license_key' PATH = 'path' @@ -126,8 +127,8 @@ class Client: self._send_request(command_id, Commands.PREPARE_SERVICE_COMMAND, command_args) @is_active_decorator - def sync_service(self, command_id: int, streams: list): - command_args = {Client.STREAMS: streams} + def sync_service(self, command_id: int, streams: list, subscribers: list): + command_args = {Client.STREAMS: streams, Client.SUBSCRIBERS: subscribers} self._send_request(command_id, Commands.SYNC_SERVICE_COMMAND, command_args) @is_active_decorator diff --git a/app/common_entries.py b/app/common_entries.py index 020085c..5483746 100644 --- a/app/common_entries.py +++ b/app/common_entries.py @@ -19,7 +19,7 @@ class Url(EmbeddedDocument): class InputUrl(Url): - pass + user_agent = IntField(default=constants.UserAgent.GSTREAMER, required=True) class OutputUrl(Url): diff --git a/app/common_forms.py b/app/common_forms.py index d683195..594f0d3 100644 --- a/app/common_forms.py +++ b/app/common_forms.py @@ -1,6 +1,6 @@ from wtforms import Form from flask_babel import lazy_gettext -from wtforms.fields import StringField, FieldList, IntegerField, FormField, FloatField +from wtforms.fields import StringField, FieldList, IntegerField, FormField, FloatField, SelectField from wtforms.validators import InputRequired, Length, NumberRange import app.constants as constants @@ -16,7 +16,9 @@ class UrlForm(Form): class InputUrlForm(UrlForm): - pass + user_agent = SelectField(lazy_gettext(u'User agent:'), + validators=[InputRequired()], + choices=constants.AVAILABLE_USER_AGENTS, coerce=constants.UserAgent.coerce) class InputUrlsForm(Form): @@ -25,7 +27,7 @@ class InputUrlsForm(Form): def get_data(self) -> InputUrls: urls = InputUrls() for url in self.data['urls']: - urls.urls.append(InputUrl(url['id'], url['uri'])) + urls.urls.append(InputUrl(url['id'], url['uri'], url['user_agent'])) return urls diff --git a/app/config/public_config.py b/app/config/public_config.py index 9a63d23..7e130d9 100644 --- a/app/config/public_config.py +++ b/app/config/public_config.py @@ -3,4 +3,4 @@ PUBLIC_CONFIG = {'site': {'title': 'FastoCloud', 'keywords': 'video,cloud,iptv,s 'support': {'contact_email': 'support@fastogt.com', 'contact_address': 'Republic of Belarus, Minsk, Stadionnay str. 5', 'community_channel': 'https://discord.gg/cnUXsws'}, - 'project': {'version': '1.1.0', 'version_type': 'release'}} + 'project': {'version': '1.2.0', 'version_type': 'release'}} diff --git a/app/constants.py b/app/constants.py index 29a19da..75f61a0 100644 --- a/app/constants.py +++ b/app/constants.py @@ -195,3 +195,22 @@ class Roles(IntEnum): def __str__(self): return str(self.value) + + +class UserAgent(IntEnum): + GSTREAMER = 0 + VLC = 1 + + @classmethod + def choices(cls): + return [(choice, choice.name) for choice in cls] + + @classmethod + def coerce(cls, item): + return cls(int(item)) if not isinstance(item, cls) else item + + def __str__(self): + return str(self.value) + + +AVAILABLE_USER_AGENTS = [(UserAgent.GSTREAMER, 'GStreamer'), (UserAgent.VLC, 'VLC'), ] diff --git a/app/service/forms.py b/app/service/forms.py index e2037d5..b201921 100644 --- a/app/service/forms.py +++ b/app/service/forms.py @@ -13,6 +13,8 @@ class ServiceSettingsForm(FlaskForm): host = FormField(HostAndPortForm, lazy_gettext(u'Host:'), validators=[]) http_host = FormField(HostAndPortForm, lazy_gettext(u'Http host:'), validators=[]) vods_host = FormField(HostAndPortForm, lazy_gettext(u'Vods host:'), validators=[]) + subscribers_host = FormField(HostAndPortForm, lazy_gettext(u'Subscribers host:'), validators=[]) + bandwidth_host = FormField(HostAndPortForm, lazy_gettext(u'Bandwidth host:'), validators=[]) feedback_directory = StringField(lazy_gettext(u'Feedback directory:'), validators=[InputRequired()]) timeshifts_directory = StringField(lazy_gettext(u'Timeshifts directory:'), validators=[InputRequired()]) @@ -32,6 +34,8 @@ class ServiceSettingsForm(FlaskForm): settings.host = self.host.get_data() settings.http_host = self.http_host.get_data() settings.vods_host = self.vods_host.get_data() + settings.subscribers_host = self.subscribers_host.get_data() + settings.bandwidth_host = self.bandwidth_host.get_data() settings.feedback_directory = self.feedback_directory.data settings.timeshifts_directory = self.timeshifts_directory.data diff --git a/app/service/server_entry.py b/app/service/server_entry.py index 7d7472b..0cd6a1e 100644 --- a/app/service/server_entry.py +++ b/app/service/server_entry.py @@ -24,6 +24,10 @@ class ServerSettings: DEFAULT_SERVICE_HTTP_PORT = 8000 DEFAULT_SERVICE_VODS_HOST = 'localhost' DEFAULT_SERVICE_VODS_PORT = 7000 + DEFAULT_SERVICE_SUBSCRIBERS_HOST = 'localhost' + DEFAULT_SERVICE_SUBSCRIBERS_PORT = 6000 + DEFAULT_SERVICE_BANDWIDTH_HOST = 'localhost' + DEFAULT_SERVICE_BANDWIDTH_PORT = 5000 name = StringField(unique=True, default=DEFAULT_SERVICE_NAME, max_length=MAX_SERVICE_NAME_LENGTH, min_length=MIN_SERVICE_NAME_LENGTH) @@ -32,6 +36,10 @@ class ServerSettings: port=DEFAULT_SERVICE_HTTP_PORT)) vods_host = EmbeddedDocumentField(HostAndPort, default=HostAndPort(host=DEFAULT_SERVICE_VODS_HOST, port=DEFAULT_SERVICE_VODS_PORT)) + subscribers_host = EmbeddedDocumentField(HostAndPort, default=HostAndPort(host=DEFAULT_SERVICE_SUBSCRIBERS_HOST, + port=DEFAULT_SERVICE_SUBSCRIBERS_PORT)) + bandwidth_host = EmbeddedDocumentField(HostAndPort, default=HostAndPort(host=DEFAULT_SERVICE_BANDWIDTH_HOST, + port=DEFAULT_SERVICE_BANDWIDTH_PORT)) feedback_directory = StringField(default=DEFAULT_FEEDBACK_DIR_PATH) timeshifts_directory = StringField(default=DEFAULT_TIMESHIFTS_DIR_PATH) diff --git a/app/service/service.py b/app/service/service.py index 731a92a..03a89b0 100644 --- a/app/service/service.py +++ b/app/service/service.py @@ -54,7 +54,7 @@ class Service(IStreamHandler): self._settings = settings self.__reload_from_db() # other fields - self._client = ServiceClient(self, settings) + self._client = ServiceClient(settings.id, self, settings) self._host = host self._port = port self._socketio = socketio @@ -69,7 +69,7 @@ class Service(IStreamHandler): return self._client.stop_service(delay) def get_log_service(self): - return self._client.get_log_service(self._host, self._port, self.id) + return self._client.get_log_service(self._host, self._port) def ping(self): return self._client.ping_service() diff --git a/app/service/service_client.py b/app/service/service_client.py index fff106e..0e2fa59 100644 --- a/app/service/service_client.py +++ b/app/service/service_client.py @@ -1,3 +1,5 @@ +from bson.objectid import ObjectId + from app.client.client import Client from app.client.client_handler import IClientHandler from app.client.json_rpc import Request, Response @@ -11,6 +13,8 @@ import app.constants as constants class ServiceClient(IClientHandler): HTTP_HOST = 'http_host' VODS_HOST = 'vods_host' + SUBSCRIBERS_HOST = 'subscribers_host' + BANDWIDTH_HOST = 'bandwidth_host' VERSION = 'version' @staticmethod @@ -25,7 +29,8 @@ class ServiceClient(IClientHandler): def get_pipeline_stream_path(host: str, port: int, stream_id: str): return constants.DEFAULT_STREAM_PIPELINE_PATH_TEMPLATE_3SIS.format(host, port, stream_id) - def __init__(self, handler: IStreamHandler, settings: ServiceSettings): + def __init__(self, sid: ObjectId, handler: IStreamHandler, settings: ServiceSettings): + self.id = sid self._request_id = 0 self._handler = handler self._service_settings = settings @@ -50,8 +55,9 @@ class ServiceClient(IClientHandler): def stop_service(self, delay: int): return self._client.stop_service(self._gen_request_id(), delay) - def get_log_service(self, host: str, port: int, sid: str): - return self._client.get_log_service(self._gen_request_id(), ServiceClient.get_log_service_path(host, port, sid)) + def get_log_service(self, host: str, port: int): + return self._client.get_log_service(self._gen_request_id(), + ServiceClient.get_log_service_path(host, port, str(self.id))) def start_stream(self, config: dict): return self._client.start_stream(self._gen_request_id(), config) @@ -74,7 +80,12 @@ class ServiceClient(IClientHandler): streams = [] for stream in self._service_settings.streams: streams.append(stream.config()) - return self._client.sync_service(self._gen_request_id(), streams) + + subscribers = [] + for subs in self._service_settings.subscribers: + subscribers.append(subs.to_service(self.id)) + + return self._client.sync_service(self._gen_request_id(), streams, subscribers) def get_http_host(self) -> str: return self._http_host @@ -82,6 +93,12 @@ class ServiceClient(IClientHandler): def get_vods_host(self) -> str: return self._vods_host + def get_subscribers_host(self) -> str: + return self._subscribers_host + + def get_bandwidth_host(self) -> str: + return self._bandwidth_host + def get_vods_in(self) -> list: return self._vods_in @@ -98,7 +115,9 @@ class ServiceClient(IClientHandler): self.sync_service() if self._handler: self._set_runtime_fields(resp.result[ServiceClient.HTTP_HOST], - resp.result[ServiceClient.VODS_HOST], resp.result[ServiceClient.VERSION]) + resp.result[ServiceClient.VODS_HOST], resp.result[ServiceClient.VODS_HOST], + resp.result[ServiceClient.SUBSCRIBERS_HOST], + resp.result[ServiceClient.BANDWIDTH_HOST]) self._handler.on_service_statistic_received(resp.result) if req.method == Commands.PREPARE_SERVICE_COMMAND and resp.is_message(): @@ -127,9 +146,13 @@ class ServiceClient(IClientHandler): self._handler.on_client_state_changed(status) # private - def _set_runtime_fields(self, http_host=None, vods_host=None, version=None, vods_in=None): + def _set_runtime_fields(self, http_host=None, vods_host=None, subscribers_host=None, bandwidth_host=None, + version=None, + vods_in=None): self._http_host = http_host self._vods_host = vods_host + self._subscribers_host = subscribers_host + self._bandwidth_host = bandwidth_host self._version = version self._vods_in = vods_in diff --git a/app/service/service_entry.py b/app/service/service_entry.py index bfceb2a..348e766 100644 --- a/app/service/service_entry.py +++ b/app/service/service_entry.py @@ -20,6 +20,7 @@ class ServiceSettings(Document, ServerSettings): streams = ListField(EmbeddedDocumentField(Stream), default=[]) users = ListField(EmbeddedDocumentField(UserPair), default=[]) + subscribers = ListField(ReferenceField('Subscriber'), default=[]) def generate_playlist(self) -> str: result = '#EXTM3U\n' @@ -47,4 +48,23 @@ class ServiceSettings(Document, ServerSettings): for user in self.users: if user.id == uid: self.users.remove(user) + break self.save() + + def add_subscriber(self, sid): + self.subscribers.append(sid) + self.save() + + def remove_subscriber(self, sid): + for subscriber in self.subscribers: + if subscriber == sid: + self.subscribers.remove(subscriber) + break + self.save() + + def find_stream_settings_by_id(self, sid): + for stream in self.streams: + if stream.id == sid: + return stream + + return None diff --git a/app/service/view.py b/app/service/view.py index f0b9d27..c7d8cd6 100644 --- a/app/service/view.py +++ b/app/service/view.py @@ -6,9 +6,11 @@ from flask_login import login_required, current_user from app import get_runtime_folder from app.service.forms import ServiceSettingsForm, ActivateForm, UploadM3uForm, UserServerForm +from app.subscriber.forms import SubscriberForm from app.service.service_entry import ServiceSettings, UserPair from app.utils.m3u_parser import M3uParser from app.home.user_loging_manager import User +from app.subscriber.subscriber_entry import Subscriber import app.constants as constants @@ -157,11 +159,25 @@ class ServiceView(FlaskView): return render_template('service/user/add.html', form=form) + @login_required + @route('/subscriber/add/', methods=['GET', 'POST']) + def subscriber_add(self, sid): + form = SubscriberForm(obj=Subscriber()) + if request.method == 'POST' and form.validate_on_submit(): + server = ServiceSettings.objects(id=sid).first() + if server: + new_entry = form.make_entry() + new_entry.add_server(server) + + server.add_subscriber(new_entry) + return jsonify(status='ok'), 200 + + return render_template('service/subscriber/add.html', form=form) + @login_required @route('/add', methods=['GET', 'POST']) def add(self): - model = ServiceSettings() - form = ServiceSettingsForm(obj=model) + form = ServiceSettingsForm(obj=ServiceSettings()) if request.method == 'POST' and form.validate_on_submit(): new_entry = form.make_entry() admin = UserPair(current_user.id, constants.Roles.ADMIN) diff --git a/app/stream/stream_entry.py b/app/stream/stream_entry.py index 98edaac..aecf015 100644 --- a/app/stream/stream_entry.py +++ b/app/stream/stream_entry.py @@ -65,6 +65,46 @@ class StreamFields: TIMESTAMP = 'timestamp' +class EpgInfo: + ID_FIELD = 'id' + URL_FIELD = 'url' + TITLE_FIELD = 'display_name' + ICON_FIELD = 'icon' + PROGRAMS_FIELD = 'programs' + + id = str + url = str + title = str + icon = str + programs = [] + + def __init__(self, eid: str, url: str, title: str, icon: str, programs=list()): + self.id = eid + self.url = url + self.title = title + self.icon = icon + self.programs = programs + + def to_dict(self) -> dict: + return {EpgInfo.ID_FIELD: self.id, EpgInfo.URL_FIELD: self.url, EpgInfo.TITLE_FIELD: self.title, + EpgInfo.ICON_FIELD: self.icon, EpgInfo.PROGRAMS_FIELD: self.programs} + + +class ChannelInfo: + EPG_FIELD = 'epg' + VIDEO_ENABLE_FIELD = 'video' + AUDIO_ENABLE_FIELD = 'audio' + + def __init__(self, epg: EpgInfo, have_video=True, have_audio=True): + self.have_video = have_video + self.have_audio = have_audio + self.epg = epg + + def to_dict(self) -> dict: + return {ChannelInfo.EPG_FIELD: self.epg.to_dict(), ChannelInfo.VIDEO_ENABLE_FIELD: self.have_video, + ChannelInfo.AUDIO_ENABLE_FIELD: self.have_audio} + + class Stream(EmbeddedDocument): meta = {'allow_inheritance': True, 'auto_create_index': True} @@ -159,6 +199,13 @@ class Stream(EmbeddedDocument): conf[AUDIO_SELECT_FIELD] = audio_select return conf + def to_channel_info(self) -> [ChannelInfo]: + ch = [] + for out in self.output.urls: + epg = EpgInfo(self.get_id(), out.uri, self.name, self.icon) + ch.append(ChannelInfo(epg)) + return ch + def generate_feedback_dir(self): return '{0}/{1}/{2}'.format(self._settings.feedback_directory, self.get_type(), self.get_id()) diff --git a/app/subscriber/forms.py b/app/subscriber/forms.py new file mode 100644 index 0000000..af69ab3 --- /dev/null +++ b/app/subscriber/forms.py @@ -0,0 +1,26 @@ +from flask_wtf import FlaskForm +from flask_babel import lazy_gettext + +from wtforms.fields import StringField, PasswordField, SubmitField, SelectField +from wtforms.validators import InputRequired, Length, Email + +from app.subscriber.subscriber_entry import Subscriber + + +class SubscriberForm(FlaskForm): + AVAILABLE_COUNTRIES = [('UK', 'United kingdom'), ('RU', 'Russian'), ('BY', 'Belarus')] + email = StringField(lazy_gettext(u'Email:'), + validators=[InputRequired(), Email(message=lazy_gettext(u'Invalid email')), Length(max=30)]) + password = PasswordField(lazy_gettext(u'Password:'), validators=[InputRequired(), Length(min=4, max=80)]) + country = SelectField(lazy_gettext(u'Locale:'), coerce=str, validators=[InputRequired()], + choices=AVAILABLE_COUNTRIES) + apply = SubmitField(lazy_gettext(u'Apply')) + + def make_entry(self): + return self.update_entry(Subscriber()) + + def update_entry(self, subscriber: Subscriber): + subscriber.email = self.email.data + subscriber.password = Subscriber.make_md5_hash_from_password(self.password.data) + subscriber.country = self.country.data + return subscriber diff --git a/app/subscriber/subscriber_entry.py b/app/subscriber/subscriber_entry.py new file mode 100644 index 0000000..f331830 --- /dev/null +++ b/app/subscriber/subscriber_entry.py @@ -0,0 +1,105 @@ +from datetime import datetime +from hashlib import md5 +from bson.objectid import ObjectId +from enum import IntEnum + +from mongoengine import Document, EmbeddedDocument, StringField, DateTimeField, IntField, ListField, ReferenceField, \ + PULL, ObjectIdField, EmbeddedDocumentField + +from app.service.service_entry import ServiceSettings + + +class Device(EmbeddedDocument): + DEFAULT_DEVICE_NAME = 'Device' + MIN_DEVICE_NAME_LENGTH = 3 + MAX_DEVICE_NAME_LENGTH = 32 + + meta = {'allow_inheritance': False, 'auto_create_index': True} + id = ObjectIdField(required=True, default=ObjectId, unique=True, primary_key=True) + created_date = DateTimeField(default=datetime.now) + name = StringField(default=DEFAULT_DEVICE_NAME, min_length=MIN_DEVICE_NAME_LENGTH, + max_length=MAX_DEVICE_NAME_LENGTH, + required=True) + + +class Subscriber(Document): + ID_FIELD = "id" + EMAIL_FIELD = "login" + PASSWORD_FIELD = "password" + STATUS_FIELD = "status" + DEVICES_FIELD = "devices" + STREAMS_FIELD = "channels" + + class Status(IntEnum): + NO_ACTIVE = 0 + ACTIVE = 1 + BANNED = 2 + + class Type(IntEnum): + USER = 0, + SUPPORT = 1 + + SUBSCRIBER_HASH_LENGHT = 32 + + meta = {'collection': 'subscribers', 'auto_create_index': False} + + email = StringField(max_length=30, required=True) + password = StringField(min_length=SUBSCRIBER_HASH_LENGHT, max_length=SUBSCRIBER_HASH_LENGHT, required=True) + created_date = DateTimeField(default=datetime.now) + status = IntField(default=Status.ACTIVE) + type = IntField(default=Type.USER) + country = StringField(min_length=2, max_length=3, required=True) + servers = ListField(ReferenceField(ServiceSettings, reverse_delete_rule=PULL), default=[]) + devices = ListField(EmbeddedDocumentField(Device), default=[]) + streams = ListField(ObjectIdField(), default=[]) + + def add_server(self, server: ServiceSettings): + self.servers.append(server) + self.save() + + def add_stream(self, stream): + self.streams.append(stream.id) + self.save() + + def remove_stream(self, sid: ObjectId): + for stream in self.streams: + if stream == sid: + self.streams.remove(stream) + break + self.save() + + def to_service(self, sid: ObjectId) -> dict: + for serv in self.servers: + if serv.id == sid: + devices = [] + for dev in self.devices: + devices.append(str(dev.id)) + + streams = [] + for stream in self.streams: + founded_stream = serv.find_stream_settings_by_id(stream) + if founded_stream: + channels = founded_stream.to_channel_info() + for ch in channels: + streams.append(ch.to_dict()) + + conf = { + Subscriber.ID_FIELD: str(self.id), Subscriber.EMAIL_FIELD: self.email, + Subscriber.PASSWORD_FIELD: self.password, Subscriber.STATUS_FIELD: self.status, + Subscriber.DEVICES_FIELD: devices, Subscriber.STREAMS_FIELD: streams} + return conf + + return {} + + @staticmethod + def make_md5_hash_from_password(password: str) -> str: + m = md5() + m.update(password.encode()) + return m.hexdigest() + + @classmethod + def md5_user(cls, email: str, password: str, country: str): + return cls(email=email, password=Subscriber.make_md5_hash_from_password(password), country=country) + + +Subscriber.register_delete_rule(ServiceSettings, "subscribers", PULL) diff --git a/app/templates/service/base.html b/app/templates/service/base.html index 847cf58..5674563 100644 --- a/app/templates/service/base.html +++ b/app/templates/service/base.html @@ -38,6 +38,10 @@
{{ render_bootstrap_form(form.vods_host) }}
+ {{ render_bootstrap_form(form.subscribers_host) }} +
+ {{ render_bootstrap_form(form.bandwidth_host) }} +
{{ render_bootstrap_field(form.feedback_directory) }}
{{ render_bootstrap_field(form.timeshifts_directory) }} diff --git a/app/templates/service/subscriber/add.html b/app/templates/service/subscriber/add.html new file mode 100644 index 0000000..7082fe3 --- /dev/null +++ b/app/templates/service/subscriber/add.html @@ -0,0 +1,4 @@ +{% extends 'service/subscriber/base.html' %} +{% block title %} +Add subscriber to server +{% endblock %} \ No newline at end of file diff --git a/app/templates/service/subscriber/base.html b/app/templates/service/subscriber/base.html new file mode 100644 index 0000000..2300f37 --- /dev/null +++ b/app/templates/service/subscriber/base.html @@ -0,0 +1,45 @@ +{% from 'bootstrap/wtf.html' import form_field %} +{% macro render_bootstrap_field(field) %} +
+ +
+ {{ field(class='form-control')|safe }} +
+
+{% endmacro %} + +{% macro render_bootstrap_form(form) %} +
+ +
+ {{ form() }} +
+
+{% endmacro %} + +
+ + + +
diff --git a/app/templates/service/subscriber/edit.html b/app/templates/service/subscriber/edit.html new file mode 100644 index 0000000..4b8aba8 --- /dev/null +++ b/app/templates/service/subscriber/edit.html @@ -0,0 +1,4 @@ +{% extends 'service/subscriber/base.html' %} +{% block title %} +Edit subscriber +{% endblock %} \ No newline at end of file diff --git a/app/templates/service/user/edit.html b/app/templates/service/user/edit.html index f81b63c..97c665a 100644 --- a/app/templates/service/user/edit.html +++ b/app/templates/service/user/edit.html @@ -1,4 +1,4 @@ -{% extends 'service/base.html' %} +{% extends 'service/user/base.html' %} {% block title %} Edit service {% endblock %} \ No newline at end of file diff --git a/app/templates/user/dashboard.html b/app/templates/user/dashboard.html index b6bd938..d6e106c 100644 --- a/app/templates/user/dashboard.html +++ b/app/templates/user/dashboard.html @@ -588,11 +588,12 @@ Dashboard | {{ config['PUBLIC_CONFIG'].site.title }} } function edit_stream_entry(url) { + var data_ser = $('#stream_entry_form').serialize(); $.ajax({ url: url, type: "POST", dataType: 'json', - data: $('#stream_entry_form').serialize(), + data: data_ser, success: function (response) { console.log(response); $('#stream_dialog').modal('hide'); @@ -808,12 +809,5 @@ Dashboard | {{ config['PUBLIC_CONFIG'].site.title }} } }); } - - - - - - - {% endblock %} diff --git a/app/templates/user/settings.html b/app/templates/user/settings.html index b7628f6..df91fbe 100644 --- a/app/templates/user/settings.html +++ b/app/templates/user/settings.html @@ -74,6 +74,10 @@ Settings | {{ config['PUBLIC_CONFIG'].site.title }} onclick="add_user_to_server('{{ server.id }}')"> {% trans %}Add user{% endtrans %} +