diff --git a/application/__init__.py b/application/__init__.py index 9788fd65..4a89b739 100644 --- a/application/__init__.py +++ b/application/__init__.py @@ -43,6 +43,7 @@ def init_app(name, debug=False): def BeforeRequest(): g.version = __Version__ g.now = datetime.datetime.utcnow + g.allowSignup = app.config['ALLOW_SIGNUP'] connect_database() @app.teardown_request diff --git a/application/back_end/__init__.py b/application/back_end/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/application/back_end/db_models.py b/application/back_end/db_models.py index e8b2787a..38902b14 100644 --- a/application/back_end/db_models.py +++ b/application/back_end/db_models.py @@ -16,18 +16,19 @@ class KeUser(MyBaseModel): # kindleEar User name = CharField(unique=True) passwd = CharField() - expiration_days = IntegerField(default=0) #账号超期设置值,0为永久有效 + email = CharField() secret_key = CharField(default='') kindle_email = CharField(default='') - email = CharField(default='') #用于重置密码之类的操作 enable_send = BooleanField(default=False) send_days = JSONField(default=JSONField.list_default) send_time = IntegerField(default=0) timezone = IntegerField(default=0) - book_type = CharField(default='epub') #mobi,epub - device = CharField(default='') + expiration_days = IntegerField(default=0) #账号超期设置值,0为永久有效 expires = DateTimeField(null=True) #超过了此日期后账号自动停止推送 + created_time = DateTimeField(default=datetime.datetime.utcnow) + device = CharField(default='') + book_type = CharField(default='epub') #mobi,epub book_title = CharField(default='KindleEar') title_fmt = CharField(default='') #在元数据标题中添加日期的格式 author_format = CharField(default='') #修正Kindle 5.9.x固件的bug【将作者显示为日期】 @@ -36,10 +37,9 @@ class KeUser(MyBaseModel): # kindleEar User time_fmt = CharField(default='%Y-%m-%d') oldest_article = IntegerField(default=7) book_language = CharField(default='en') #自定义RSS的语言 - enable_custom_rss = BooleanField(default=True) + enable_custom_rss = BooleanField(default=False) share_links = JSONField(default=JSONField.dict_default) #evernote/wiz/pocket/instapaper包含子字典,微博/facebook/twitter等仅包含0/1 - covers = JSONField(default=JSONField.dict_default) #保存封面图片数据库ID {'order':,'cover0':,...'cover6':} send_mail_service = JSONField(default=JSONField.dict_default) #{'service':,...} custom = JSONField(default=JSONField.dict_default) #留着扩展,避免后续一些小特性还需要升级数据表结构 @@ -219,31 +219,38 @@ class LastDelivered(MyBaseModel): record = CharField(default='') datetime = DateTimeField(default=datetime.datetime.utcnow) -#当前使用: -#name='dbTableVersion'.int_value 行保存数据库格式版本 -#name='lastSharedRssTime'.time_value 保存共享库的最新更新日期 class AppInfo(MyBaseModel): - name = CharField() - int_value = IntegerField(default=0) - str_value = CharField(default='') - time_value = DateTimeField(default=datetime.datetime.utcnow) + name = CharField(unique=True) + value = CharField(default='') description = CharField(default='') - comment = CharField(default='') + lastSharedRssTime = 'lastSharedRssTime' + newUserMailService = 'newUserMailService' + signupType = 'signupType' + inviteCodes = 'inviteCodes' + + @classmethod + def get_value(cls, name, default): + dbItem = cls.get_or_none(AppInfo.name == name) + return dbItem.value if dbItem else default + @classmethod + def set_value(cls, name, value): + cls.replace(name=name, value=value).execute() + #创建数据库表格,一个数据库只需要创建一次 #如果是sql数据库,可以使用force=True删掉之前的数据库文件(小心) def create_database_tables(force=False): engine = os.getenv('DATABASE_ENGINE') - if engine == "sqlite" and dbName: - if not force and os.path.exists(dbName): - #print(f'[Error] Database "{dbName}" already exists') - return - elif os.path.exists(dbName): - try: - os.remove(dbName) - except: - pass + #if engine == "sqlite" and dbName: + # if not force and os.path.exists(dbName): + # #print(f'[Error] Database "{dbName}" already exists') + # return + # elif os.path.exists(dbName): + # try: + # os.remove(dbName) + # except: + # pass if engine not in ["datastore", "mongodb"]: #with dbInstance.connection_context(): @@ -252,5 +259,4 @@ def create_database_tables(force=False): SharedRss, SharedRssCategory, LastDelivered, AppInfo], safe=True) #close_database() - #AppInfo(name='dbTableVersion', int_value=DB_VERSION).save() #print(f'Create database "{dbName}" finished') diff --git a/application/back_end/send_mail_adpt.py b/application/back_end/send_mail_adpt.py index cbe3e21d..aae52c0c 100644 --- a/application/back_end/send_mail_adpt.py +++ b/application/back_end/send_mail_adpt.py @@ -28,6 +28,17 @@ except ImportError: smtp_send_mail = None +#返回当前可用的发送邮件服务列表 +def avaliable_sm_services(): + sm = ['local'] + if gae_mail: + sm.append('gae') + if SendGridAPIClient: + sm.append('sendgrid') + if smtp_send_mail: + sm.append('smtp') + return sm + #发送邮件 #title: 邮件标题 #attachment: 附件二进制内容,或元祖 (filename, content) diff --git a/application/back_end/task_queue_adpt.py b/application/back_end/task_queue_adpt.py index fa9edb46..25ee893d 100644 --- a/application/back_end/task_queue_adpt.py +++ b/application/back_end/task_queue_adpt.py @@ -2,7 +2,7 @@ # -*- coding:utf-8 -*- #封装后台的任务队列,以适用不同的平台部署要求 #Author: cdhigh -import os, json +import os, sys, json __TASK_QUEUE_SERVICE = os.getenv('TASK_QUEUE_SERVICE') if __TASK_QUEUE_SERVICE == "gae": @@ -42,7 +42,11 @@ def create_http_task(url, payload): #return client.create_task(tasks_v2.CreateTaskRequest(parent=taskParent, task=task)) elif __TASK_QUEUE_SERVICE == 'celery': + #启动celery + #celery -A main.celery_app worker --loglevel=info --logfile=d:\celery.log --concurrency=2 -P eventlet + #celery -A main.celery_app beat -s /home/celery/var/run/celerybeat-schedule --loglevel=info --logfile=d:\celery.log --concurrency=2 -P eventlet from celery import Celery, Task, shared_task + from celery.schedules import crontab from ..work.worker import WorkerImpl from ..work.url2book import Url2BookImpl @@ -53,17 +57,38 @@ def __call__(self, *args, **kwargs): return self.run(*args, **kwargs) app.config.from_mapping( - CELERY={'broker_url': app.config['CELERY_BROKER_URL'], - 'result_backend': app.config['CELERY_RESULT_BACKEND'], + CELERY={'broker_url': app.config['TASK_QUEUE_BROKER_URL'], + 'result_backend': app.config['TASK_QUEUE_RESULT_BACKEND'], 'task_ignore_result': True, },) celery_app = Celery(app.name, task_cls=FlaskTask) celery_app.config_from_object(app.config["CELERY"]) celery_app.set_default() + + celery_app.conf.beat_schedule = { + 'check_deliver': { + 'task': 'check_deliver', + 'schedule': crontab(minute=0, hour='*/1'), #每个小时 + 'args': [] + }, + 'remove_logs': { + 'task': 'remove_logs', #每天凌晨 + 'schedule': crontab(minute=0, hour=0, day_of_month='*/1'), + 'args': [] + }, + } app.extensions["celery"] = celery_app return celery_app + @shared_task(name="check_deliver", ignore_result=True) + def check_deliver(): + MultiUserDelivery() + + @shared_task(name="remove_logs", ignore_result=True) + def remove_logs(): + RemoveLogs() + @shared_task(ignore_result=True) def start_celery_worker_impl(**payload): return WorkerImpl(**payload) @@ -78,6 +103,48 @@ def create_delivery_task(payload: dict): def create_url2book_task(payload: dict): start_celery_url2book.delay(**payload) +elif __TASK_QUEUE_SERVICE == 'rq': + #启动rq + #set FLASK_APP=main.py + #flask rq worker + + from flask_rq2 import RQ + + rq = RQ() + + def init_task_queue_service(app): + app.config['RQ_REDIS_URL'] = app.config['TASK_QUEUE_BROKER_URL'] + rq.init_app(app) + #check_deliver.cron('0 */1 * * *', 'check_deliver') #每隔一个小时执行一次 + #remove_logs.cron('0 0 */1 * *', 'check_deliver') #每隔24小时执行一次 + return rq + + @rq.job + def check_deliver(): + from ..view.deliver import MultiUserDelivery + MultiUserDelivery() + + @rq.job + def remove_logs(): + from ..view.logs import RemoveLogs + RemoveLogs() + + @rq.job + def start_rq_worker_impl(**payload): + from ..work.worker import WorkerImpl + return WorkerImpl(**payload) + + @rq.job + def start_rq_url2book(**payload): + from ..work.url2book import Url2BookImpl + return Url2BookImpl(**payload) + + def create_delivery_task(payload: dict): + start_rq_worker_impl.queue(**payload) + + def create_url2book_task(payload: dict): + start_rq_url2book.queue(**payload) + elif not __TASK_QUEUE_SERVICE: from ..work.worker import WorkerImpl from ..work.url2book import Url2BookImpl diff --git a/application/base_handler.py b/application/base_handler.py index 7b945d34..34854bbc 100644 --- a/application/base_handler.py +++ b/application/base_handler.py @@ -12,14 +12,12 @@ #一些共同的工具函数,工具函数都是小写+下划线形式 #确认登录的装饰器 -#如果提供userName,则要求登录的用户名=userName -#forAjax:是返回一个json字典 -def login_required(userName=None, forAjax=False): +#forAjax:是否返回一个json字典 +def login_required(forAjax=False): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): - if ((session.get('login') == 1) and (not userName or (userName == session.get('userName'))) - and get_login_user()): + if ((session.get('login') == 1) and get_login_user()): return func(*args, **kwargs) else: return redirect(url_for("bpLogin.NeedLoginAjax") if forAjax else url_for("bpLogin.Login")) diff --git a/application/routes.py b/application/routes.py index 451419df..74f321ed 100644 --- a/application/routes.py +++ b/application/routes.py @@ -20,7 +20,6 @@ @bpHome.route('/') def Home(): - #worker.WorkerImpl('admin', ['builtin:adventuregamers', 'custom:Recipe:2447:KEY', 'custom:Recipe:4964:KEY']) return render_template('home.html') @bpHome.route('/env') diff --git a/application/static/base.css b/application/static/base.css index b17fc99c..b5a09297 100644 --- a/application/static/base.css +++ b/application/static/base.css @@ -129,7 +129,8 @@ button { .app-menu .pure-menu-selected a, .app-menu .pure-menu-selected span { - color: white; + color: white !important; + text-decoration: none; } .app-menu .pure-menu-item a:hover, @@ -609,7 +610,27 @@ select[id="shared_rss_lang_pick"] { font-weight: bold; } +.h3button { + background-color: rgb(0, 120, 231); + color: #fff; + border:solid 1px #ccc; + border-radius: 20px; + margin: 0px 40px; + padding: 2px 30px; + cursor: pointer; + font-size: 0.9em; + text-decoration: none; + white-space: nowrap; +} +.h3button:hover { + background-image: linear-gradient(transparent, rgba(0,0,0, 0.05) 40%, rgba(0,0,0, 0.10)); + color: white; +} + /* Feed列表的汉堡菜单按钮及其弹出子按钮 */ +.hamburger-btn-container { + position: relative; +} .hamburger-btn { position: absolute; top: 50%; @@ -648,7 +669,10 @@ select[id="shared_rss_lang_pick"] { box-shadow: 0 0 5px rgba(0, 0, 0, 0.5); transition: background-color 0.3s, box-shadow 0.3s; } - +.additional-btn-disabled { + pointer-events:none; + background: silver !important; +} .additional-btn:hover { box-shadow: 0 0 10px rgba(0, 0, 0, 0.5); } @@ -788,7 +812,7 @@ div[class="schedule_daytimes"] input { } .imgFileUploade .imgAll li{ list-style: none; - width: 210px;height: 323px;border:solid 1px #ccc;margin:8px 5px;float: left; + width: 195px;height: 300px;border:solid 1px #ccc;margin:8px 5px;float: left; position: relative;box-shadow: 0 0 10px #eee; display: flex; justify-content: center; diff --git a/application/static/base.js b/application/static/base.js index f16d5825..c08350dd 100644 --- a/application/static/base.js +++ b/application/static/base.js @@ -968,73 +968,13 @@ var AjaxFileUpload = { ///[end] adv_uploadcss.html ///[start] admin.html -//添加一个账号 -function AddAccount(name) { - var newName = $('#new_username').val(); - var newPwd1 = $('#new_u_pwd1').val(); - var newPwd2 = $('#new_u_pwd2').val(); - var newEmail = $('#new_email').val(); - var smService = $('#sm_service').val(); //send mail service - var expiration = $('#new_u_expiration').val(); - if (!newName || !newPwd1 || !newPwd2) { - alert(i18n.namePwdEmpty); - return; - } else if (newPwd1 != newPwd2) { - alert(i18n.pwdDismatch); - return; - } - - $.post("/admin", {actType: 'add', new_username: newName, new_u_pwd1: newPwd1, new_u_pwd2: newPwd2, - new_u_expiration: expiration, new_email: newEmail, sm_service: smService}, function (data) { - if (data.status == "ok") { - $('#new_username').val(''); - $('#new_u_pwd1').val(''); - $('#new_u_pwd2').val(''); - $('#new_email').val(''); - window.location.reload(true); - ShowSimpleModalDialog('

' + i18n.addAccountOk + '

'); - } else if (data.status == i18n.loginRequired) { - window.location.href = '/login'; - } else { - alert(data.status); - } - }); -} - -//修改账号的密码 -function ChangeAccountPassword(name) { - var oldPwd = $('#orgpwd').val(); - var newPwd1 = $('#newpwd1').val(); - var newPwd2 = $('#newpwd2').val(); - if (!oldPwd || !newPwd1 || !newPwd2) { - alert(i18n.namePwdEmpty); - return; - } else if (newPwd1 != newPwd2) { - alert(i18n.pwdDismatch); - return; - } - - $.post("/admin", {actType: 'change', name: name, op: oldPwd, p1: newPwd1, p2: newPwd2}, function (data) { - if (data.status == "ok") { - $('#orgpwd').val(''); - $('#newpwd1').val(''); - $('#newpwd2').val(''); - ShowSimpleModalDialog('

' + i18n.chPwdSuccess + '

'); - } else if (data.status == i18n.loginRequired) { - window.location.href = '/login'; - } else { - alert(data.status); - } - }); -} - //删除一个账号 function DeleteAccount(name) { if (!confirm(i18n.areYouSureDelete.format(name))) { return; } - $.post("/admin", {actType: 'delete', name: name}, function (data) { + $.post("/account/delete", {name: name}, function (data) { if (data.status == "ok") { ShowSimpleModalDialog('

' + i18n.accountDeleted + '

'); window.location.reload(true); diff --git a/application/static/library.js b/application/static/library.js index 0ae71595..3005de49 100644 --- a/application/static/library.js +++ b/application/static/library.js @@ -195,7 +195,7 @@ function CreatePageContent(category, page) { } if (pageData.data.length == 0) { - return '

' + i18n.noLinksInLang + '

'; + return '

' + i18n.noLinksFound + '

'; } var rssStr = ['
']; @@ -242,8 +242,7 @@ function CreatePageContent(category, page) { //转到某一页 function ToPage(category, page) { - var contentDiv = $("#librarycontent"); - contentDiv.html(CreatePageContent(category, page)); + $("#librarycontent").html(CreatePageContent(category, page)); }; //创建屏幕下部的分页按钮 @@ -304,10 +303,6 @@ function DoSearchInShared() { var $div = $("#all_recipes"); $div.empty(); var lang = $("#shared_rss_lang_pick").val(); - if (!lang) { - return; - } - BuildSharedRssByCategory(lang, txt); CreateCategoryMenu(); SelectCategory(".category-menu", i18n.catAll); //自动选择第一项 @@ -432,10 +427,13 @@ function InitSharedRssData(lastRssTime) { if (needLatestTime) { //向服务器发起请求,要求新的数据 $.ajax({url: "/library/mgr/latesttime", - type: "post", + type: "POST", async: false, //阻塞式ajax success: function (resp) { if (resp.status == "ok") { + if (resp.tips) { + $('#library_tips').html('
' + resp.tips + '
'); + } if (resp.data > latestTime) { //自从上次获取数据以来服务器有数据更新 needData = true; } @@ -447,7 +445,7 @@ function InitSharedRssData(lastRssTime) { } if (needData) { $.ajax({url: "/library/mgr/getrss", - type: "post", + type: "POST", async: false, //阻塞式ajax success: function (resp) { if (resp.status == "ok") { @@ -462,7 +460,7 @@ function InitSharedRssData(lastRssTime) { } } else { //浏览器不支持本地存储 $.ajax({url: "/library/mgr/getrss", - type: "post", + type: "POST", async: false, //阻塞式ajax success: function (resp) { if (resp.status == "ok") { diff --git a/application/templates/admin.html b/application/templates/admin.html index c3605e7d..aeb8235e 100644 --- a/application/templates/admin.html +++ b/application/templates/admin.html @@ -9,80 +9,38 @@ {% block content -%}
-
-
-

{{_('Change Password')}}

- {% if chpwdtips -%} -
{{chpwdtips|safe}}
- {% endif -%} -
- - -
-
- - -
-
- - -
-
- -
-
-
- {% if session.get('role') == 'admin' -%} -
+ {% if g.allowSignup -%} + + {% if tips -%} +
{{tips}}
+ {% endif -%}
-

{{_("Add Account")}}

- {% if actips -%} -
{{actips|safe}}
- {% endif -%} -
- - -
-
- - -
-
- - -
-
- - -
+

{{_("Signup settings")}}

- + +
- - + + +
-
- +
+ +
- + {% endif %}
-

{{_("Accounts")}}

+

{{_("Accounts")}} {{_("Add")}}

@@ -124,12 +82,11 @@ {% endif -%} diff --git a/application/templates/adminmgrpwd.html b/application/templates/adminmgrpwd.html deleted file mode 100644 index 273cdc37..00000000 --- a/application/templates/adminmgrpwd.html +++ /dev/null @@ -1,43 +0,0 @@ -{% extends "base.html" %} -{% block titleTag -%} -{{ _("Change password") }} - KindleEar -{% endblock -%} - -{% block menubar -%} -{% endblock -%} -{% block content -%} -
- {% if tips -%} -
{{tips|safe}}
- {% endif -%} -
-
- - -
-
- - -
-
- - -
-
- - -
-
- -
- -
-{% endblock -%} \ No newline at end of file diff --git a/application/templates/adv_archive.html b/application/templates/adv_archive.html index 06bf5e95..223371cb 100644 --- a/application/templates/adv_archive.html +++ b/application/templates/adv_archive.html @@ -48,13 +48,13 @@ {% if instapaper.get('enable') %} checked="1"{% endif %} /> {{appendStrs["Instapaper"]}} -
+
-
- +
+
- diff --git a/application/templates/adv_base.html b/application/templates/adv_base.html index 7b270b4d..9c38fb12 100644 --- a/application/templates/adv_base.html +++ b/application/templates/adv_base.html @@ -31,7 +31,7 @@ {% block content -%}
-
+
-
+
{% block advcontent -%} {% endblock -%}
diff --git a/application/templates/adv_import.html b/application/templates/adv_import.html index a923a2ef..a8116238 100644 --- a/application/templates/adv_import.html +++ b/application/templates/adv_import.html @@ -12,8 +12,9 @@
+
{{_("Download")}} diff --git a/application/templates/adv_uploadcover.html b/application/templates/adv_uploadcover.html index 3ac8e884..9422599f 100644 --- a/application/templates/adv_uploadcover.html +++ b/application/templates/adv_uploadcover.html @@ -13,7 +13,7 @@
- diff --git a/application/templates/base.html b/application/templates/base.html index e8ccd743..217ecc99 100644 --- a/application/templates/base.html +++ b/application/templates/base.html @@ -1,99 +1,99 @@ - - - - {% block titleTag -%} - {{ _(title) }} - KindleEar - {% endblock -%} - - - - - - - - + + + + {% block titleTag -%} + {{ _(title) }} - KindleEar + {% endblock -%} + + + + + + + + - {% autoescape off %} - - {% block javascript_inhead %} - {% endblock -%} - {% endautoescape %} - {% block cssfiles %}{% endblock -%} - {% block css %}{% endblock -%} + {% autoescape off %} + + {% block javascript_inhead %} + {% endblock -%} + {% endautoescape %} + {% block cssfiles %}{% endblock -%} + {% block css %}{% endblock -%} {% block bodytag -%} @@ -107,10 +107,13 @@ {% block header_loginfo -%}
{% block menubar -%} - {% if session.get('userName') -%} -
-
    - {% if tab == "my" -%} -
  • {{_("Feeds")}}
  • - {% else -%} -
  • {{_("Feeds")}}
  • - {% endif -%} - {% if tab == "set" -%} -
  • {{_("Settings")}}
  • - {% else -%} -
  • {{_("Settings")}}
  • - {% endif -%} - {% if tab == "logs" -%} -
  • {{_("Logs")}}
  • - {% else -%} -
  • {{_("Logs")}}
  • - {% endif -%} - {% if tab == "admin" -%} -
  • {{_("Admin")}}
  • - {% else -%} -
  • {{_("Admin")}}
  • - {% endif -%} - {% if tab == "advset" -%} -
  • {{_("Advanced")}}
  • - {% else -%} -
  • {{_("Advanced")}}
  • - {% endif -%} - {% if tab == "shared" -%} -
  • {{_("Shared")}}
  • - {% else -%} -
  • {{_("Shared")}}
  • - {% endif -%} -
-
- {% endif -%} + {% if session.get('userName') -%} + + {% endif -%} {% endblock -%} {% block content -%} @@ -174,7 +153,7 @@

2013-2024 © KindleEar @{{g.version}} | - FAQ | + DocsGithub pageApp Engine diff --git a/application/templates/change_password.html b/application/templates/change_password.html new file mode 100644 index 00000000..0c1655f0 --- /dev/null +++ b/application/templates/change_password.html @@ -0,0 +1,37 @@ +{% extends "base.html" %} +{% block titleTag -%} +{{ _("Change password") }} - KindleEar +{% endblock -%} + +{% block content -%} +

+ {% if tips -%} +
{{tips|safe}}
+ {% endif -%} +
+
+

{{_('Change Password')}}

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+
+ +
+{% endblock -%} \ No newline at end of file diff --git a/application/templates/delaccount.html b/application/templates/delaccount.html deleted file mode 100644 index d5c1997c..00000000 --- a/application/templates/delaccount.html +++ /dev/null @@ -1,24 +0,0 @@ -{% extends "base.html" %} -{% block titleTag -%} -{{ _("Delete account") }} - KindleEar -{% endblock -%} - -{% block menubar -%} -{% endblock -%} - -{% block content -%} -
- {% if tips -%} -
{{tips}}
- {% endif -%} -
-
- - -
-
- -
- -
-{% endblock -%} \ No newline at end of file diff --git a/application/templates/home.html b/application/templates/home.html index 464a8510..329c50d2 100644 --- a/application/templates/home.html +++ b/application/templates/home.html @@ -12,8 +12,8 @@

{{_("Sharing Joyful News Every Step of the Way")}}

{{_('Logout')}} {% else -%}
- - + + {% endif -%} diff --git a/application/templates/library.html b/application/templates/library.html index 9803202a..7d03c3f3 100644 --- a/application/templates/library.html +++ b/application/templates/library.html @@ -32,9 +32,11 @@ {% block content -%}
- {% if tips -%} -
{{tips|safe}}
- {% endif -%} +
+ {% if tips %} +
{{tips|safe}}
+ {% endif %} +
diff --git a/application/templates/login.html b/application/templates/login.html index a2bde5b9..acb7009e 100644 --- a/application/templates/login.html +++ b/application/templates/login.html @@ -3,16 +3,6 @@ {{ _("Login") }} - KindleEar {% endblock -%} -{% block css -%} - -{% endblock -%} - {% block header_loginfo -%} {% endblock %} @@ -24,19 +14,24 @@ {% if tips -%}
{{tips|safe}}
{% endif -%} -
-
- - -
-
- - -
-
- -
+ +
+

{{_("Login")}}

+
+ + +
+
+ + +
+
+ +
+
-

{{_("The website doesn't allow registration. You can ask the owner for an account.")}}

+ {% if not g.allowSignup -%} +

{{_("The website does not allow registration. You can ask the owner for an account.")}}

+ {% endif %}
{% endblock -%} \ No newline at end of file diff --git a/application/templates/my.html b/application/templates/my.html index d0d11c90..808a06df 100644 --- a/application/templates/my.html +++ b/application/templates/my.html @@ -9,7 +9,7 @@ {% block content -%}
-

{{_("Custom Rss")}} +

{{_("Custom RSS")}}
@@ -19,7 +19,7 @@
-
diff --git a/application/templates/setting.html b/application/templates/setting.html index cf5b7743..0ef12c2d 100644 --- a/application/templates/setting.html +++ b/application/templates/setting.html @@ -11,8 +11,7 @@
{% if user.expires -%}
- {{_("Your account will pause after {0}, please log in again before it expires.").format(user.expires.strftime("%Y-%m-%d")) }}
- {{_("Delete Account")}} + {{_("Your account will pause after {0}, please log in again before it expires.").format(user.expires.strftime("%Y-%m-%d")) }}
{% endif -%}
@@ -21,7 +20,7 @@ {% endif -%}
-

{{_("Base Setting")}}

+

{{_("Base")}}

-
+ {#
-
+
#}
@@ -180,13 +179,19 @@ {% set sm_srv_type = sm_srv.get('service', 'gae') -%} {% if session.get('role') == 'admin' or sm_srv_type != 'admin' -%}
-

{{_("Send mail service")}}

+

{{_("Send Mail Service")}}

@@ -218,7 +223,7 @@ {% endif -%}

{% autoescape off -%} - {{_("Important: Please activate your kindle firstly, then goto %(personal)s Page and add %(sender)s to 'Approved Personal Document E-mail List'.", personal='' + _("Personal Document Settings") + '', sender='' + mailSender + '')|safe}} + {{_("Important: Please activate your kindle firstly, then goto %(personal)s Page and add %(sender)s to 'Approved Personal Document E-mail List'.", personal='' + _("Personal Document Settings") + '', sender='' + src_mail + '')|safe}} {% endautoescape -%}

diff --git a/application/templates/signup.html b/application/templates/signup.html new file mode 100644 index 00000000..2e74bb7e --- /dev/null +++ b/application/templates/signup.html @@ -0,0 +1,48 @@ +{% extends "base.html" %} +{% block titleTag -%} +{{ _("Signup") }} - KindleEar +{% endblock -%} + +{% block header_loginfo -%} +{% endblock %} + +{% block menubar -%} +{% endblock -%} + +{% block content -%} +

+ {% if tips -%} +
{{tips|safe}}
+ {% endif -%} + +
+

{{_("Signup")}}

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ {% if inviteNeed %} +
+ + +
+ {% endif %} +
+ +
+
+ +
+{% endblock -%} diff --git a/application/templates/user_account.html b/application/templates/user_account.html new file mode 100644 index 00000000..518ccc23 --- /dev/null +++ b/application/templates/user_account.html @@ -0,0 +1,57 @@ +{% extends "base.html" %} +{% block titleTag -%} +{{ _("User account") }} - KindleEar +{% endblock -%} + +{% block content -%} +
+ {% if tips -%} +
{{tips|safe}}
+ {% endif -%} +
+
+

{{formTitle}}

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ +
+{% endblock -%} \ No newline at end of file diff --git a/application/translations/tr_TR/LC_MESSAGES/messages.mo b/application/translations/tr_TR/LC_MESSAGES/messages.mo index 0c452ec7..e26588a0 100644 Binary files a/application/translations/tr_TR/LC_MESSAGES/messages.mo and b/application/translations/tr_TR/LC_MESSAGES/messages.mo differ diff --git a/application/translations/tr_TR/LC_MESSAGES/messages.po b/application/translations/tr_TR/LC_MESSAGES/messages.po index b50cc142..7f771be3 100644 --- a/application/translations/tr_TR/LC_MESSAGES/messages.po +++ b/application/translations/tr_TR/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-02-14 21:49-0300\n" +"POT-Creation-Date: 2024-02-17 14:29-0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: tr_TR\n" @@ -18,141 +18,149 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.14.0\n" -#: application/templates/admin.html:3 application/templates/base.html:148 -#: application/templates/base.html:150 +#: application/templates/admin.html:3 application/templates/base.html:138 msgid "Admin" msgstr "Yönetim" -#: application/templates/admin.html:14 -msgid "Change Password" -msgstr "Şifre Değiştirme" +#: application/templates/admin.html:19 +msgid "Signup settings" +msgstr "Kayıt Ayarları" #: application/templates/admin.html:19 -msgid "Old password" -msgstr "Eski şifre" +msgid "Save" +msgstr "Kaydet" + +#: application/templates/admin.html:21 +#: application/templates/user_account.html:31 +msgid "Email service" +msgstr "E-posta servisi" #: application/templates/admin.html:23 -#: application/templates/adminmgrpwd.html:19 -#: application/templates/adminmgrpwd.html:20 -msgid "New password" -msgstr "Yeni şifre" +#: application/templates/user_account.html:34 +msgid "Same as admin" +msgstr "Admin ile aynı" -#: application/templates/admin.html:27 application/templates/admin.html:52 -#: application/templates/adminmgrpwd.html:23 -#: application/templates/adminmgrpwd.html:24 -msgid "Confirm password" -msgstr "Şifreyi onayla" +#: application/templates/admin.html:24 +#: application/templates/user_account.html:35 +msgid "Independent" +msgstr "Bağımsız" + +#: application/templates/admin.html:28 +msgid "Signup type" +msgstr "Kayıt Türü" + +#: application/templates/admin.html:30 +msgid "Public" +msgstr "Kamu" #: application/templates/admin.html:31 -#: application/templates/adminmgrpwd.html:39 -msgid "Confirm Change" -msgstr "Değişikliği Onayla" +msgid "One time code" +msgstr "Tek kullanımlık kod" + +#: application/templates/admin.html:32 +msgid "Permanent code" +msgstr "Kalıcı kod" + +#: application/templates/admin.html:36 +msgid "Invitation codes" +msgstr "Davetiye kodları" + +#: application/templates/admin.html:43 +msgid "Accounts" +msgstr "Hesaplar" -#: application/templates/admin.html:39 -msgid "Add Account" -msgstr "Hesap Ekle" +#: application/templates/admin.html:43 +#: application/templates/adv_whitelist.html:29 application/templates/my.html:32 +#: application/view/admin.py:60 application/view/admin.py:68 +#: application/view/admin.py:103 +msgid "Add" +msgstr "Ekle" -#: application/templates/admin.html:44 application/templates/admin.html:86 -#: application/templates/adminmgrpwd.html:15 -#: application/templates/adminmgrpwd.html:16 -#: application/templates/delaccount.html:16 application/templates/home.html:15 -#: application/templates/login.html:29 application/templates/login.html:30 -#: application/templates/logs.html:85 application/templates/setting.html:205 +#: application/templates/admin.html:54 application/templates/home.html:15 +#: application/templates/login.html:21 application/templates/logs.html:61 +#: application/templates/reset_password.html:19 +#: application/templates/reset_password.html:20 +#: application/templates/setting.html:211 application/templates/signup.html:21 +#: application/templates/user_account.html:15 msgid "Username" msgstr "Kullanıcı adı" -#: application/templates/admin.html:48 -#: application/templates/adv_archive.html:55 application/templates/base.html:47 -#: application/templates/home.html:16 application/templates/login.html:33 -#: application/templates/login.html:34 application/templates/setting.html:209 -msgid "Password" -msgstr "Şifre" +#: application/templates/admin.html:55 +msgid "AutoSend" +msgstr "Aktif" -#: application/templates/admin.html:56 application/templates/admin.html:88 -#: application/templates/adminmgrpwd.html:27 +#: application/templates/admin.html:56 +#: application/templates/change_password.html:27 +#: application/templates/reset_password.html:26 +#: application/templates/signup.html:33 +#: application/templates/user_account.html:27 +msgid "Email" +msgstr "E-posta" + +#: application/templates/admin.html:57 +#: application/templates/user_account.html:39 msgid "Expiration" msgstr "Son" -#: application/templates/admin.html:58 application/templates/admin.html:99 -#: application/templates/adminmgrpwd.html:29 +#: application/templates/admin.html:58 +msgid "Operation" +msgstr "İşlemler" + +#: application/templates/admin.html:65 +msgid "Yes" +msgstr "Evet" + +#: application/templates/admin.html:65 +msgid "No" +msgstr "Hayir" + +#: application/templates/admin.html:68 +#: application/templates/user_account.html:42 msgid "Never expire" msgstr "hiç sona ermeyen" -#: application/templates/admin.html:59 application/templates/admin.html:101 -#: application/templates/adminmgrpwd.html:30 -#: application/templates/setting.html:151 +#: application/templates/admin.html:70 application/templates/setting.html:150 +#: application/templates/user_account.html:43 msgid "7 Days" msgstr "7 Günlük" -#: application/templates/admin.html:60 application/templates/admin.html:103 -#: application/templates/adminmgrpwd.html:31 +#: application/templates/admin.html:72 +#: application/templates/user_account.html:44 msgid "1 Month" msgstr "1 ay" -#: application/templates/admin.html:61 application/templates/admin.html:105 -#: application/templates/adminmgrpwd.html:32 +#: application/templates/admin.html:74 +#: application/templates/user_account.html:45 msgid "3 Months" msgstr "3 ay" -#: application/templates/admin.html:62 application/templates/admin.html:107 -#: application/templates/adminmgrpwd.html:33 +#: application/templates/admin.html:76 +#: application/templates/user_account.html:46 msgid "6 Months" msgstr "6 ay" -#: application/templates/admin.html:63 application/templates/admin.html:109 -#: application/templates/adminmgrpwd.html:34 +#: application/templates/admin.html:78 +#: application/templates/user_account.html:47 msgid "1 Year" msgstr "1 yıl" -#: application/templates/admin.html:64 application/templates/admin.html:111 -#: application/templates/adminmgrpwd.html:35 +#: application/templates/admin.html:80 +#: application/templates/user_account.html:48 msgid "2 Years" msgstr "2 yıl" -#: application/templates/admin.html:68 -#: application/templates/adv_whitelist.html:29 application/templates/my.html:32 -msgid "Add" -msgstr "Ekle" - -#: application/templates/admin.html:74 -msgid "Accounts" -msgstr "Hesaplar" - #: application/templates/admin.html:85 -msgid "No." -msgstr "No:" - -#: application/templates/admin.html:87 -msgid "AutoSend" -msgstr "Aktif" +#: application/templates/change_password.html:3 +msgid "Change password" +msgstr "Şifre Değiştirme" #: application/templates/admin.html:89 -msgid "Operation" -msgstr "İşlemler" - -#: application/templates/admin.html:97 -msgid "Yes" -msgstr "Evet" - -#: application/templates/admin.html:97 -msgid "No" -msgstr "Hayir" - -#: application/templates/admin.html:117 application/templates/admin.html:120 -msgid "Change" -msgstr "Değiştir" - -#: application/templates/admin.html:118 application/templates/admin.html:121 #: application/templates/adv_uploadcss.html:31 #: application/templates/adv_whitelist.html:22 -#: application/templates/base.html:22 application/templates/delaccount.html:20 +#: application/templates/base.html:22 msgid "Delete" msgstr "Sil" -#: application/templates/adminmgrpwd.html:3 -msgid "Change password" -msgstr "Şifre Değiştirme" - #: application/templates/adv_archive.html:3 #: application/templates/adv_archive.html:13 #: application/templates/adv_base.html:59 @@ -176,12 +184,19 @@ msgstr "Yetki vermek" msgid "Email or Username" msgstr "Kullanıcı adı" +#: application/templates/adv_archive.html:55 application/templates/base.html:47 +#: application/templates/home.html:16 application/templates/login.html:25 +#: application/templates/setting.html:215 application/templates/signup.html:25 +#: application/templates/user_account.html:19 +msgid "Password" +msgstr "Şifre" + #: application/templates/adv_archive.html:58 application/templates/base.html:57 msgid "Verify" msgstr "Doğrulamak" #: application/templates/adv_archive.html:93 -#: application/templates/setting.html:223 +#: application/templates/setting.html:230 msgid "Save settings" msgstr "Ayarları kaydet" @@ -236,7 +251,7 @@ msgid "Deliver" msgstr "Şimdi gönder" #: application/templates/adv_import.html:3 -#: application/templates/adv_import.html:18 +#: application/templates/adv_import.html:19 msgid "Import" msgstr "İçeri aktar" @@ -245,10 +260,10 @@ msgid "Import custom rss from an OPML file." msgstr "Bir OPML dosyasındaki rss'leri içeri aktar." #: application/templates/adv_import.html:15 -msgid "Import as fulltext RSS by default" -msgstr "Varsayılan olarak tam metin RSS olarak içe aktar" +msgid "Import as fulltext rss by default" +msgstr "Varsayılan olarak tam metin rss olarak içe aktar" -#: application/templates/adv_import.html:19 +#: application/templates/adv_import.html:20 msgid "Download" msgstr "İndir" @@ -312,17 +327,14 @@ msgid "Please input mail address" msgstr "Lütfen e-posta adresini giriniz" #: application/templates/autoback.html:3 -#: application/templates/tipsandback.html:3 msgid "Auto back" msgstr "Geri dönmek için tıklayınız" #: application/templates/autoback.html:28 -#: application/templates/tipsandback.html:13 msgid "Auto back to previous page after 5 seconds" msgstr "5 saniye sonra otomatik olarak önceki sayfaya döneceksiniz." #: application/templates/autoback.html:29 -#: application/templates/tipsandback.html:14 #: application/templates/tipsback.html:15 msgid "Click to back" msgstr "Geri dönmek için tıklayınız" @@ -367,7 +379,7 @@ msgstr "Bağlantıları paylaş, mutluluk paylaş" msgid "Category" msgstr "Kategori" -#: application/templates/base.html:33 application/templates/setting.html:130 +#: application/templates/base.html:33 application/templates/setting.html:129 msgid "Language" msgstr "Dil" @@ -414,7 +426,7 @@ msgid "The recipe is already subscribed." msgstr "Tarif zaten abone edilmiş." #: application/templates/base.html:45 -msgid "Subscription info" +msgid "Website login lnformation" msgstr "Abonelik bilgisi" #: application/templates/base.html:46 @@ -434,7 +446,7 @@ msgstr "" "silecektir." #: application/templates/base.html:50 -msgid "Cannot set the subscription infomation, Error:" +msgid "Cannot set the website login information, Error:" msgstr "Abonelik bilgisi ayarlanamıyor, Hata:" #: application/templates/base.html:51 @@ -470,8 +482,8 @@ msgstr "Kindle ile oku" msgid "Verified" msgstr "Doğrulanmış" -#: application/templates/base.html:59 application/view/login.py:67 -#: application/view/share.py:154 +#: application/templates/base.html:59 application/view/login.py:73 +#: application/view/share.py:145 msgid "The username does not exist or password is wrong." msgstr "Kullanıcı adı mevcut değil veya şifre yanlış." @@ -508,7 +520,7 @@ msgid "[Uncategoried]" msgstr "[Kategorisiz]" #: application/templates/base.html:68 -msgid "No links found in the chosen language." +msgid "There are no links found." msgstr "Seçilen dilde bağlantı bulunamadı." #: application/templates/base.html:69 @@ -523,35 +535,35 @@ msgstr "Bu bağlantının geçersiz veya bulutta olmadığını onaylıyor musun msgid "Customize delivery time" msgstr "Teslimat zamanını özelleştir" -#: application/templates/base.html:72 application/templates/setting.html:50 +#: application/templates/base.html:72 application/templates/setting.html:49 msgid "Delivery days" msgstr "Teslimat günleri" -#: application/templates/base.html:73 application/templates/setting.html:52 +#: application/templates/base.html:73 application/templates/setting.html:51 msgid "Mon" msgstr "Pzt" -#: application/templates/base.html:74 application/templates/setting.html:54 +#: application/templates/base.html:74 application/templates/setting.html:53 msgid "Tue" msgstr "Sal" -#: application/templates/base.html:75 application/templates/setting.html:56 +#: application/templates/base.html:75 application/templates/setting.html:55 msgid "Wed" msgstr "Çar" -#: application/templates/base.html:76 application/templates/setting.html:58 +#: application/templates/base.html:76 application/templates/setting.html:57 msgid "Thu" msgstr "Prş" -#: application/templates/base.html:77 application/templates/setting.html:60 +#: application/templates/base.html:77 application/templates/setting.html:59 msgid "Fri" msgstr "Cum" -#: application/templates/base.html:78 application/templates/setting.html:62 +#: application/templates/base.html:78 application/templates/setting.html:61 msgid "Sat" msgstr "Cts" -#: application/templates/base.html:79 application/templates/setting.html:64 +#: application/templates/base.html:79 application/templates/setting.html:63 msgid "Sun" msgstr "Paz" @@ -567,13 +579,14 @@ msgstr "Tarif için özel teslimat zamanı başarıyla kaydedildi." msgid "The account have been deleted." msgstr "Hesap silindi." -#: application/templates/base.html:83 application/view/admin.py:46 -#: application/view/admin.py:66 +#: application/templates/base.html:83 application/view/admin.py:208 +#: application/view/share.py:135 msgid "The username or password is empty." msgstr "Kullanıcı adı veya şifre boş." -#: application/templates/base.html:84 application/view/admin.py:50 -#: application/view/admin.py:70 application/view/admin.py:143 +#: application/templates/base.html:84 application/view/admin.py:85 +#: application/view/admin.py:172 application/view/admin.py:212 +#: application/view/login.py:207 application/view/login.py:267 msgid "The two new passwords are dismatch." msgstr "İki yeni şifre uyuşmuyor." @@ -585,7 +598,7 @@ msgstr "Şifre başarıyla değiştirildi." msgid "Account added successfully." msgstr "Hesap başarıyla eklendi." -#: application/templates/base.html:87 application/view/login.py:106 +#: application/templates/base.html:87 application/view/login.py:117 msgid "login required" msgstr "Giriş yapılması gerekiyor" @@ -606,37 +619,62 @@ msgid "Logout" msgstr "Çıkış" #: application/templates/base.html:113 application/templates/home.html:17 -#: application/templates/login.html:3 application/templates/login.html:37 +#: application/templates/login.html:3 application/templates/login.html:19 +#: application/templates/login.html:29 msgid "Login" msgstr "Giriş" -#: application/templates/base.html:133 application/templates/base.html:135 -#: application/templates/home.html:11 application/templates/my.html:3 +#: application/templates/base.html:115 application/templates/signup.html:3 +#: application/templates/signup.html:19 application/templates/signup.html:43 +msgid "Signup" +msgstr "Kaydol" + +#: application/templates/base.html:135 application/templates/home.html:11 +#: application/templates/my.html:3 msgid "Feeds" msgstr "RSSler" -#: application/templates/base.html:138 application/templates/base.html:140 -#: application/templates/setting.html:3 +#: application/templates/base.html:136 application/templates/setting.html:3 msgid "Settings" msgstr "Ayarlar" -#: application/templates/base.html:143 application/templates/base.html:145 -#: application/templates/logs.html:3 +#: application/templates/base.html:137 application/templates/logs.html:3 msgid "Logs" msgstr "Kayıtlar" -#: application/templates/base.html:153 application/templates/base.html:155 +#: application/templates/base.html:139 msgid "Advanced" msgstr "Gelişmiş" -#: application/templates/base.html:158 application/templates/base.html:160 -#: application/templates/library.html:3 +#: application/templates/base.html:140 application/templates/library.html:3 msgid "Shared" msgstr "Paylaşılan" -#: application/templates/delaccount.html:3 -msgid "Delete account" -msgstr "Hesabı Sil" +#: application/templates/change_password.html:13 +msgid "Change Password" +msgstr "Şifre Değiştirme" + +#: application/templates/change_password.html:15 +msgid "Old password" +msgstr "Eski şifre" + +#: application/templates/change_password.html:19 +#: application/templates/reset_password.html:31 +#: application/templates/reset_password.html:32 +msgid "New password" +msgstr "Yeni şifre" + +#: application/templates/change_password.html:23 +#: application/templates/reset_password.html:35 +#: application/templates/reset_password.html:36 +#: application/templates/signup.html:29 +#: application/templates/user_account.html:23 +msgid "Confirm password" +msgstr "Şifreyi onayla" + +#: application/templates/change_password.html:32 +msgid "Confirm Change" +msgstr "Değişikliği Onayla" #: application/templates/home.html:3 msgid "Home" @@ -676,57 +714,57 @@ msgstr "" "ile siz de kendi serverınızı kurup Kindle'ınıza RSS'leri günlük " "gönderebilir veya arkadaşlarınızla paylaşabilirsiniz." -#: application/templates/library.html:48 application/templates/my.html:62 +#: application/templates/library.html:50 application/templates/my.html:62 msgid "Search" msgstr "Ara" -#: application/templates/login.html:40 +#: application/templates/login.html:34 application/view/login.py:184 +#: application/view/login.py:191 msgid "" -"The website doesn't allow registration. You can ask the owner for an " +"The website does not allow registration. You can ask the owner for an " "account." msgstr "" "Yeni bir hesap açmak için bir site yönetici ile iletişime geçmeniz " "gerekmektedir." -#: application/templates/logs.html:34 +#: application/templates/logs.html:10 msgid "Only display last 10 logs" msgstr "Sadece son 10 işlem kaydını gösterir." -#: application/templates/logs.html:45 application/templates/logs.html:86 +#: application/templates/logs.html:21 application/templates/logs.html:62 msgid "Time" msgstr "Tarih/Saat" -#: application/templates/logs.html:46 application/templates/logs.html:87 -#: application/templates/my.html:17 application/templates/setting.html:99 -#: application/templates/setting.html:100 +#: application/templates/logs.html:22 application/templates/logs.html:63 +#: application/templates/my.html:17 application/templates/setting.html:98 +#: application/templates/setting.html:99 application/templates/setting.html:100 #: application/templates/setting.html:101 -#: application/templates/setting.html:102 -#: application/templates/setting.html:126 +#: application/templates/setting.html:125 msgid "Title" msgstr "Başlık" -#: application/templates/logs.html:47 application/templates/logs.html:88 +#: application/templates/logs.html:23 application/templates/logs.html:64 msgid "Size" msgstr "Dosya Boyutu" -#: application/templates/logs.html:48 application/templates/logs.html:89 +#: application/templates/logs.html:24 application/templates/logs.html:65 msgid "To" msgstr "Kime" -#: application/templates/logs.html:49 application/templates/logs.html:90 +#: application/templates/logs.html:25 application/templates/logs.html:66 msgid "Status" msgstr "Durum" -#: application/templates/logs.html:69 +#: application/templates/logs.html:45 msgid "There is no log" msgstr "Kayıt yok." -#: application/templates/logs.html:73 +#: application/templates/logs.html:49 msgid "Logs of other users" msgstr "Diğer kullanıcıların işlem kayıtları" -#: application/templates/my.html:12 -msgid "Custom Rss" +#: application/templates/my.html:12 application/templates/setting.html:123 +msgid "Custom RSS" msgstr "Özel RSS" #: application/templates/my.html:23 @@ -761,173 +799,158 @@ msgstr "KindleEar'a ekle" msgid "Drag and drop this link to your bookmarks" msgstr "Bu bağlantıyı yer imlerinize sürükleyip bırakın" +#: application/templates/reset_password.html:3 +#: application/templates/reset_password.html:41 +msgid "Reset password" +msgstr "Şifreyi sıfırla" + #: application/templates/setting.html:14 msgid "Your account will pause after {0}, please log in again before it expires." msgstr "" "Bu hesabın gönderileri şu tarihte duraklatılacak: {0} duraklatılmaması " "için bu tarihten önce giriş yapınız." -#: application/templates/setting.html:15 -msgid "Delete Account" -msgstr "Hesabı Sil" +#: application/templates/setting.html:23 +msgid "Base" +msgstr "Temel" -#: application/templates/setting.html:24 -msgid "Base Setting" -msgstr "Temel Ayarlar" - -#: application/templates/setting.html:26 +#: application/templates/setting.html:25 msgid "Auto Delivery" msgstr "Otomatik Teslimat" -#: application/templates/setting.html:28 +#: application/templates/setting.html:27 msgid "Recipes and custom RSS" msgstr "Tarifler ve özel RSS" -#: application/templates/setting.html:29 +#: application/templates/setting.html:28 msgid "Recipes only" msgstr "Sadece tarifler" -#: application/templates/setting.html:30 +#: application/templates/setting.html:29 msgid "Disable all" msgstr "Tümünü devre dışı bırak" -#: application/templates/setting.html:34 +#: application/templates/setting.html:33 msgid "Kindle E-mail" msgstr "Kindle E-mail" -#: application/templates/setting.html:35 +#: application/templates/setting.html:34 msgid "Seperated by comma" msgstr "Virgülle ayrılmış" -#: application/templates/setting.html:38 +#: application/templates/setting.html:37 msgid "Time zone" msgstr "Saat dilimi" -#: application/templates/setting.html:67 +#: application/templates/setting.html:66 msgid "Delivery time" msgstr "Teslimat zamanı" -#: application/templates/setting.html:75 +#: application/templates/setting.html:74 msgid "Book type" msgstr "Dosya tipi" -#: application/templates/setting.html:82 +#: application/templates/setting.html:81 msgid "Device type" msgstr "Cihaz tipi" -#: application/templates/setting.html:92 +#: application/templates/setting.html:91 msgid "Others" msgstr "Diğerleri" -#: application/templates/setting.html:96 +#: application/templates/setting.html:95 msgid "Title format" msgstr "Başlık Formatı" -#: application/templates/setting.html:98 +#: application/templates/setting.html:97 msgid "Title Only" msgstr "Yalnızca Başlık" -#: application/templates/setting.html:106 -msgid "Book mode" -msgstr "Kitap modu" - -#: application/templates/setting.html:108 -msgid "Periodical" -msgstr "Periyodik" - -#: application/templates/setting.html:109 -msgid "Comic" -msgstr "Komik" - -#: application/templates/setting.html:113 +#: application/templates/setting.html:112 msgid "Remove hyperlinks" msgstr "Bağlantıları kaldır" -#: application/templates/setting.html:115 +#: application/templates/setting.html:114 msgid "Do not remove hyperlinks" msgstr "Bağlantıları kaldırma" -#: application/templates/setting.html:116 +#: application/templates/setting.html:115 msgid "Remove image links" msgstr "Resim bağlantılarını kaldır" -#: application/templates/setting.html:117 +#: application/templates/setting.html:116 msgid "Remove text links" msgstr "Metin bağlantılarını kaldır" -#: application/templates/setting.html:118 +#: application/templates/setting.html:117 msgid "Remove all hyperlinks" msgstr "Tüm bağlantıları kaldır" -#: application/templates/setting.html:124 -msgid "Custom Rss Setting" -msgstr "Özel RSS Ayarları" - -#: application/templates/setting.html:142 +#: application/templates/setting.html:141 msgid "Oldest article" msgstr "En eski içerik?" -#: application/templates/setting.html:144 +#: application/templates/setting.html:143 msgid "No limit" msgstr "Sınırlama yok." -#: application/templates/setting.html:145 +#: application/templates/setting.html:144 msgid "1 Day" msgstr "1 Günlük" -#: application/templates/setting.html:146 +#: application/templates/setting.html:145 msgid "2 Days" msgstr "2 Günlük" -#: application/templates/setting.html:147 +#: application/templates/setting.html:146 msgid "3 Days" msgstr "3 Günlük" -#: application/templates/setting.html:148 +#: application/templates/setting.html:147 msgid "4 Days" msgstr "4 Günlük" -#: application/templates/setting.html:149 +#: application/templates/setting.html:148 msgid "5 Days" msgstr "5 Günlük" -#: application/templates/setting.html:150 +#: application/templates/setting.html:149 msgid "6 Days" msgstr "6 Günlük" -#: application/templates/setting.html:155 +#: application/templates/setting.html:154 msgid "Time format" msgstr "Zaman formatı" -#: application/templates/setting.html:167 +#: application/templates/setting.html:166 msgid "Author format" msgstr "Başlık Formatı" #: application/templates/setting.html:182 -msgid "Send mail service" +msgid "Send Mail Service" msgstr "Posta servisi gönder" #: application/templates/setting.html:184 msgid "Service" msgstr "Servis" -#: application/templates/setting.html:193 +#: application/templates/setting.html:199 msgid "ApiKey" msgstr "ApiKey" -#: application/templates/setting.html:197 +#: application/templates/setting.html:203 msgid "Host" msgstr "Ana bilgisayar" -#: application/templates/setting.html:201 +#: application/templates/setting.html:207 msgid "Port" msgstr "Port" -#: application/templates/setting.html:213 +#: application/templates/setting.html:219 msgid "Save path" msgstr "Kayıt yolu" -#: application/templates/setting.html:219 +#: application/templates/setting.html:226 #, python-format msgid "" "Important: Please activate your kindle firstly, then goto %(personal)s " @@ -937,49 +960,79 @@ msgstr "" "Sayfasına gidin ve %(sender)s'yi 'Onaylanmış Kişisel Belge E-posta " "Listesi'ne ekleyin." -#: application/templates/setting.html:219 +#: application/templates/setting.html:226 msgid "Personal Document Settings" msgstr "Ayarları kaydet" -#: application/view/admin.py:43 application/view/admin.py:78 -#: application/view/admin.py:149 -msgid "The password includes non-ascii chars." -msgstr "Şifre ascii olmayan karakterler içeriyor." +#: application/templates/signup.html:38 +msgid "Invitation code" +msgstr "Davetiye kodu" -#: application/view/admin.py:48 -msgid "The old password is wrong." -msgstr "Eski şifre yanlış." +#: application/templates/user_account.html:3 +msgid "User account" +msgstr "Kullanıcı hesabı" + +#: application/view/admin.py:50 application/view/setting.py:96 +msgid "Settings Saved!" +msgstr "Ayarlar Kaydedildi!" + +#: application/view/admin.py:60 application/view/admin.py:68 +#: application/view/admin.py:103 +msgid "Add account" +msgstr "Hesap ekle" -#: application/view/admin.py:64 +#: application/view/admin.py:67 application/view/admin.py:116 +#: application/view/admin.py:144 msgid "You do not have sufficient privileges." msgstr "Yeterli yetkiniz yok." -#: application/view/admin.py:68 application/view/login.py:40 +#: application/view/admin.py:81 application/view/admin.py:159 +#: application/view/login.py:203 application/view/login.py:232 +#: application/view/setting.py:61 application/view/setting.py:63 +#: application/view/setting.py:65 application/view/share.py:35 +msgid "Some parameters are missing or wrong." +msgstr "Bazı parametreler eksik veya yanlış." + +#: application/view/admin.py:83 application/view/login.py:42 +#: application/view/login.py:209 msgid "The username includes unsafe chars." msgstr "Kullanıcı adı güvensiz karakterler içeriyor." -#: application/view/admin.py:72 +#: application/view/admin.py:87 application/view/login.py:211 msgid "Already exist the username." msgstr "Kullanıcı adı zaten var." -#: application/view/admin.py:96 application/view/admin.py:117 -#: application/view/admin.py:141 +#: application/view/admin.py:93 application/view/admin.py:178 +#: application/view/admin.py:205 application/view/login.py:258 +msgid "The password includes non-ascii chars." +msgstr "Şifre ascii olmayan karakterler içeriyor." + +#: application/view/admin.py:120 application/view/admin.py:141 +#: application/view/admin.py:170 msgid "The username '{}' does not exist." msgstr "'{}' kullanıcı adı mevcut değil." -#: application/view/admin.py:102 -msgid "The username is empty or you dont have right to delete it." -msgstr "Kullanıcı adı boş veya silme yetkiniz yok." +#: application/view/admin.py:136 +msgid "The password will not be changed if the fields are empties." +msgstr "Alanlar boş bırakılırsa şifre değiştirilmeyecek." -#: application/view/admin.py:119 -msgid "Please input new password to confirm." -msgstr "Onaylamak için yeni şifre girin." +#: application/view/admin.py:137 application/view/admin.py:195 +msgid "Change account" +msgstr "Hesabı değiştir" -#: application/view/admin.py:132 application/view/login.py:36 -msgid "Username is empty." -msgstr "Kullanıcı adı boş." +#: application/view/admin.py:138 application/view/admin.py:196 +msgid "Change" +msgstr "Değiştir" + +#: application/view/admin.py:193 +msgid "Change success." +msgstr "Değişim başarılı." -#: application/view/admin.py:159 +#: application/view/admin.py:210 +msgid "The old password is wrong." +msgstr "Eski şifre yanlış." + +#: application/view/admin.py:217 msgid "Change password success." msgstr "Şifre değiştirme başarılı." @@ -1038,15 +1091,15 @@ msgstr "tumblr" msgid "Open in browser" msgstr "Tarayıcıda aç" -#: application/view/adv.py:351 +#: application/view/adv.py:352 msgid "Authorization Error!
{}" msgstr "Yetkilendirme Hatası!
{}" -#: application/view/adv.py:374 +#: application/view/adv.py:375 msgid "Success authorized by Pocket!" msgstr "Pocket tarafından yetkilendirilen başarı!" -#: application/view/adv.py:380 +#: application/view/adv.py:381 msgid "" "Failed to request authorization of Pocket!
See details " "below:

{}" @@ -1054,17 +1107,18 @@ msgstr "" "Pocket yetkilendirme isteği başarısız oldu!
Aşağıdaki ayrıntılara " "bakın:

{}" -#: application/view/adv.py:390 +#: application/view/adv.py:391 msgid "Request type [{}] unsupported" msgstr "İstek türü [{}] desteklenmiyor" -#: application/view/adv.py:405 +#: application/view/adv.py:406 msgid "The Instapaper service encountered an error. Please try again later." msgstr "" "Instapaper servisi bir hata ile karşılaştı. Lütfen daha sonra tekrar " "deneyin." -#: application/view/deliver.py:68 +#: application/view/deliver.py:68 application/view/login.py:154 +#: application/view/share.py:39 msgid "The username does not exist or the email is empty." msgstr "Kullanıcı adı mevcut değil veya e-posta boş." @@ -1081,175 +1135,233 @@ msgid "Cannot fetch data from {}, status: {}" msgstr "{}, durumundan veri alınamıyor: {}" #: application/view/library.py:48 application/view/subscribe.py:192 -#: application/view/subscribe.py:301 application/view/subscribe.py:330 -#: application/view/subscribe.py:338 +#: application/view/subscribe.py:304 application/view/subscribe.py:333 +#: application/view/subscribe.py:341 msgid "The recipe does not exist." msgstr "Tarif mevcut değil." -#: application/view/login.py:20 -msgid "Please input username and password." -msgstr "Lütfen kullanıcı adı ve şifresini giriniz" - -#: application/view/login.py:22 +#: application/view/login.py:25 msgid "Please use {}/{} to login at first time." msgstr "İlk giriş için kullanıcı adı:'{}' ve şifre: '{}'" #: application/view/login.py:38 +msgid "Username is empty." +msgstr "Kullanıcı adı boş." + +#: application/view/login.py:40 msgid "The len of username reached the limit of 25 chars." msgstr "Kullanıcı adının uzunluğu 25 karakter sınırına ulaştı." -#: application/view/login.py:70 application/view/login.py:72 +#: application/view/login.py:74 msgid "Forgot password?" msgstr "Forgot password?" -#: application/view/setting.py:19 +#: application/view/login.py:133 application/view/login.py:269 +msgid "The token is wrong or expired." +msgstr "Belirteç yanlış veya süresi dolmuş." + +#: application/view/login.py:136 +msgid "Please input the correct username and email to reset password." +msgstr "" +"Şifreyi sıfırlamak için lütfen doğru kullanıcı adı ve e-posta adresini " +"girin." + +#: application/view/login.py:138 +msgid "The email of account '{name}' is {email}." +msgstr "'{name}' hesabının e-postası {email}." + +#: application/view/login.py:159 +msgid "Reset password success, Please close this page and login again." +msgstr "" +"Şifre sıfırlama başarılı, Lütfen bu sayfayı kapatın ve yeniden giriş " +"yapın." + +#: application/view/login.py:162 +msgid "The email you input is not associated with this account." +msgstr "Girdiğiniz e-posta bu hesapla ilişkilendirilmemiştir." + +#: application/view/login.py:173 +msgid "The link to reset your password has been sent to your email." +msgstr "Şifrenizi sıfırlamak için gerekli bağlantı e-postanıza gönderilmiştir." + +#: application/view/login.py:174 +msgid "Please check your email inbox within 24 hours." +msgstr "Lütfen e-posta gelen kutunuzu 24 saat içinde kontrol edin." + +#: application/view/login.py:205 +msgid "The invitation code is invalid." +msgstr "Davetiye kodu geçersiz." + +#: application/view/login.py:213 +msgid "" +"Failed to create an account. Please contact the administrator for " +"assistance." +msgstr "Bir hesap oluşturulamadı. Yardım için lütfen yöneticiyle iletişime geçin." + +#: application/view/login.py:223 +msgid "Successfully created account." +msgstr "Hesap başarıyla oluşturuldu." + +#: application/view/login.py:234 +msgid "Reset KindleEar password" +msgstr "KindleEar şifrenizi sıfırlama" + +#: application/view/login.py:235 +msgid "This is an automated email. Please do not reply to it." +msgstr "Bu otomatik bir e-postadır. Lütfen yanıt vermeyin." + +#: application/view/login.py:236 +msgid "You can click the following link to reset your KindleEar password." +msgstr "" +"KindleEar şifrenizi sıfırlamak için aşağıdaki bağlantıya " +"tıklayabilirsiniz." + +#: application/view/setting.py:57 +msgid "Kindle E-mail is requied!" +msgstr "Kindle E-mail adresi gerekli!" + +#: application/view/setting.py:59 +msgid "Title is requied!" +msgstr "Başlık zorunlu!" + +#: application/view/setting.py:124 msgid "Chinese" msgstr "Çince" -#: application/view/setting.py:20 +#: application/view/setting.py:125 msgid "English" msgstr "İngilizce" -#: application/view/setting.py:21 +#: application/view/setting.py:126 msgid "French" msgstr "Fransızca" -#: application/view/setting.py:22 +#: application/view/setting.py:127 msgid "Spanish" msgstr "İspanyolca" -#: application/view/setting.py:23 +#: application/view/setting.py:128 msgid "Portuguese" msgstr "Portekizce" -#: application/view/setting.py:24 +#: application/view/setting.py:129 msgid "German" msgstr "Almanca" -#: application/view/setting.py:25 +#: application/view/setting.py:130 msgid "Italian" msgstr "İtalyanca" -#: application/view/setting.py:26 +#: application/view/setting.py:131 msgid "Japanese" msgstr "Japonca" -#: application/view/setting.py:27 +#: application/view/setting.py:132 msgid "Russian" msgstr "Rusça" -#: application/view/setting.py:28 +#: application/view/setting.py:133 msgid "Turkish" msgstr "Türkçe" -#: application/view/setting.py:29 +#: application/view/setting.py:134 msgid "Korean" msgstr "Koreli" -#: application/view/setting.py:30 +#: application/view/setting.py:135 msgid "Arabic" msgstr "Arapça" -#: application/view/setting.py:31 +#: application/view/setting.py:136 msgid "Czech" msgstr "Çek" -#: application/view/setting.py:32 +#: application/view/setting.py:137 msgid "Dutch" msgstr "Flemenkçe" -#: application/view/setting.py:33 +#: application/view/setting.py:138 msgid "Greek" msgstr "Yunan" -#: application/view/setting.py:34 +#: application/view/setting.py:139 msgid "Hindi" msgstr "Hintçe" -#: application/view/setting.py:35 +#: application/view/setting.py:140 msgid "Malaysian" msgstr "Malezyalı" -#: application/view/setting.py:36 +#: application/view/setting.py:141 msgid "Bengali" msgstr "Bengal" -#: application/view/setting.py:37 +#: application/view/setting.py:142 msgid "Persian" msgstr "Farsça" -#: application/view/setting.py:38 +#: application/view/setting.py:143 msgid "Urdu" msgstr "Urduca" -#: application/view/setting.py:39 +#: application/view/setting.py:144 msgid "Swahili" msgstr "Svahili" -#: application/view/setting.py:40 +#: application/view/setting.py:145 msgid "Vietnamese" msgstr "Vietnam" -#: application/view/setting.py:41 +#: application/view/setting.py:146 msgid "Punjabi" msgstr "Pencap" -#: application/view/setting.py:42 +#: application/view/setting.py:147 msgid "Javanese" msgstr "Cava" -#: application/view/setting.py:43 +#: application/view/setting.py:148 msgid "Tagalog" msgstr "Tagalog" -#: application/view/setting.py:44 +#: application/view/setting.py:149 msgid "Hausa" msgstr "Hausa" -#: application/view/setting.py:78 -msgid "Kindle E-mail is requied!" -msgstr "Kindle E-mail adresi gerekli!" - -#: application/view/setting.py:80 -msgid "Title is requied!" -msgstr "Başlık zorunlu!" - -#: application/view/setting.py:82 application/view/setting.py:84 -#: application/view/setting.py:86 -msgid "Some parameters are missing or wrong." -msgstr "Bazı parametreler eksik veya yanlış." - -#: application/view/setting.py:116 -msgid "Settings Saved!" -msgstr "Ayarlar Kaydedildi!" +#: application/view/share.py:50 application/view/subscribe.py:240 +msgid "Unknown command: {}" +msgstr "Bilinmeyen komut: {}" -#: application/view/share.py:103 -msgid "'{title}'

Saved to {act} [{email}] success." -msgstr "'{title}'

{act} [{email}] başarısına kaydedildi." +#: application/view/share.py:56 +msgid "There is no {} email yet." +msgstr "Henüz {} e-postası bulunmamaktadır." -#: application/view/share.py:125 -msgid "Failed save to Pocket.
" -msgstr "Pocket'e kaydetme işlemi başarısız oldu.
" +#: application/view/share.py:96 application/view/share.py:121 +#: application/view/share.py:143 +msgid "Saved to your {} account." +msgstr "{} hesabınıza kaydedildi." -#: application/view/share.py:127 -msgid "'{}'

Saved to your Pocket account." -msgstr "'{}'

Pocket hesabınıza kaydedildi." +#: application/view/share.py:99 application/view/share.py:117 +#: application/view/share.py:146 +msgid "Failed save to {}." +msgstr "{}'e kaydetme işlemi başarısız oldu." -#: application/view/share.py:130 -msgid "See details below:

{}" -msgstr "Aşağıdaki ayrıntılara bakın:

{}" +#: application/view/share.py:100 application/view/share.py:118 +#: application/view/share.py:147 +msgid "Reason :" +msgstr "Neden :" -#: application/view/share.py:150 -msgid "'{}'

Saved to your Instapaper account." -msgstr "'{}'

Instapaper hesabınıza kaydedildi." +#: application/view/share.py:109 +msgid "Unauthorized {} account!" +msgstr "{} tarafından yetkilendirilen başarı!" -#: application/view/share.py:154 application/view/share.py:158 -msgid "Failed save to Instapaper
'{}'

Reason :" -msgstr "Instapaper'a kaydetme başarısız
'{}'

Neden :" +#: application/view/share.py:122 +msgid "See details below:" +msgstr "Aşağıdaki ayrıntılara bakın:" -#: application/view/share.py:158 -msgid "Unknown({})" -msgstr "Bilinmeyen({})" +#: application/view/share.py:145 +msgid "Unknown: {}" +msgstr "Bilinmeyen: {}" #: application/view/subscribe.py:55 msgid "Title or url is empty!" @@ -1271,7 +1383,7 @@ msgstr "Başlık veya URL boş." msgid "Failed to fetch the recipe." msgstr "Tarif alınamadı." -#: application/view/subscribe.py:123 application/view/subscribe.py:262 +#: application/view/subscribe.py:123 application/view/subscribe.py:265 msgid "Failed to save the recipe. Error:" msgstr "Tarif kaydedilemedi. Hata:" @@ -1279,19 +1391,19 @@ msgstr "Tarif kaydedilemedi. Hata:" msgid "You can only delete the uploaded recipe." msgstr "Yalnızca yüklenen tarifi silebilirsiniz." -#: application/view/subscribe.py:235 +#: application/view/subscribe.py:225 +msgid "The recipe have been subscribed, please unsubscribe it before delete." +msgstr "Tarif abone olunmuş, silmeden önce aboneliği iptal edin." + +#: application/view/subscribe.py:238 msgid "This recipe has not been subscribed to yet." msgstr "Bu tarife henüz abone olunmadı." -#: application/view/subscribe.py:237 -msgid "Unknown command: {}" -msgstr "Bilinmeyen komut: {}" - -#: application/view/subscribe.py:249 +#: application/view/subscribe.py:252 msgid "Can not read uploaded file, Error:" msgstr "Yüklenen dosya okunamıyor, Hata:" -#: application/view/subscribe.py:257 +#: application/view/subscribe.py:260 msgid "" "Failed to decode the recipe. Please ensure that your recipe is saved in " "utf-8 encoding." @@ -1299,15 +1411,15 @@ msgstr "" "Tarif çözümlenemedi. Lütfen tarifinizin utf-8 kodlamasında " "kaydedildiğinden emin olun." -#: application/view/subscribe.py:277 -msgid "The recipe is already in the library" -msgstr "Tarif zaten kütüphanede" +#: application/view/subscribe.py:280 +msgid "The recipe is already in the library." +msgstr "Tarif zaten kütüphanede." -#: application/view/subscribe.py:308 -msgid "The login information for this recipe has been cleared" -msgstr "Bu tarifin giriş bilgileri temizlendi" +#: application/view/subscribe.py:311 +msgid "The login information for this recipe has been cleared." +msgstr "Bu tarifin giriş bilgileri temizlendi." -#: application/view/subscribe.py:312 -msgid "The login information for this recipe has been saved" -msgstr "Bu tarifin giriş bilgileri kaydedildi" +#: application/view/subscribe.py:315 +msgid "The login information for this recipe has been saved." +msgstr "Bu tarifin giriş bilgileri kaydedildi." diff --git a/application/translations/zh/LC_MESSAGES/messages.mo b/application/translations/zh/LC_MESSAGES/messages.mo index 0900434d..949bd9ce 100644 Binary files a/application/translations/zh/LC_MESSAGES/messages.mo and b/application/translations/zh/LC_MESSAGES/messages.mo differ diff --git a/application/translations/zh/LC_MESSAGES/messages.po b/application/translations/zh/LC_MESSAGES/messages.po index bdf740b1..7fc6e0cb 100644 --- a/application/translations/zh/LC_MESSAGES/messages.po +++ b/application/translations/zh/LC_MESSAGES/messages.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: KindleEar v3.0.0\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-02-14 21:49-0300\n" +"POT-Creation-Date: 2024-02-17 14:29-0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language: zh\n" @@ -18,141 +18,149 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.14.0\n" -#: application/templates/admin.html:3 application/templates/base.html:148 -#: application/templates/base.html:150 +#: application/templates/admin.html:3 application/templates/base.html:138 msgid "Admin" -msgstr "账户管理" +msgstr "账号管理" -#: application/templates/admin.html:14 -msgid "Change Password" -msgstr "修改密码" +#: application/templates/admin.html:19 +msgid "Signup settings" +msgstr "注册设置" #: application/templates/admin.html:19 -msgid "Old password" -msgstr "原密码" +msgid "Save" +msgstr "保存" + +#: application/templates/admin.html:21 +#: application/templates/user_account.html:31 +msgid "Email service" +msgstr "发送邮件服务" #: application/templates/admin.html:23 -#: application/templates/adminmgrpwd.html:19 -#: application/templates/adminmgrpwd.html:20 -msgid "New password" -msgstr "新密码" +#: application/templates/user_account.html:34 +msgid "Same as admin" +msgstr "和管理员一致" -#: application/templates/admin.html:27 application/templates/admin.html:52 -#: application/templates/adminmgrpwd.html:23 -#: application/templates/adminmgrpwd.html:24 -msgid "Confirm password" -msgstr "确认密码" +#: application/templates/admin.html:24 +#: application/templates/user_account.html:35 +msgid "Independent" +msgstr "独立设置" + +#: application/templates/admin.html:28 +msgid "Signup type" +msgstr "用户注册类型" + +#: application/templates/admin.html:30 +msgid "Public" +msgstr "公开" #: application/templates/admin.html:31 -#: application/templates/adminmgrpwd.html:39 -msgid "Confirm Change" -msgstr "确认修改" +msgid "One time code" +msgstr "一次性邀请码" + +#: application/templates/admin.html:32 +msgid "Permanent code" +msgstr "永久邀请码" + +#: application/templates/admin.html:36 +msgid "Invitation codes" +msgstr "邀请码列表" + +#: application/templates/admin.html:43 +msgid "Accounts" +msgstr "用户账号列表" -#: application/templates/admin.html:39 -msgid "Add Account" -msgstr "添加用户账号" +#: application/templates/admin.html:43 +#: application/templates/adv_whitelist.html:29 application/templates/my.html:32 +#: application/view/admin.py:60 application/view/admin.py:68 +#: application/view/admin.py:103 +msgid "Add" +msgstr "添加" -#: application/templates/admin.html:44 application/templates/admin.html:86 -#: application/templates/adminmgrpwd.html:15 -#: application/templates/adminmgrpwd.html:16 -#: application/templates/delaccount.html:16 application/templates/home.html:15 -#: application/templates/login.html:29 application/templates/login.html:30 -#: application/templates/logs.html:85 application/templates/setting.html:205 +#: application/templates/admin.html:54 application/templates/home.html:15 +#: application/templates/login.html:21 application/templates/logs.html:61 +#: application/templates/reset_password.html:19 +#: application/templates/reset_password.html:20 +#: application/templates/setting.html:211 application/templates/signup.html:21 +#: application/templates/user_account.html:15 msgid "Username" msgstr "用户名" -#: application/templates/admin.html:48 -#: application/templates/adv_archive.html:55 application/templates/base.html:47 -#: application/templates/home.html:16 application/templates/login.html:33 -#: application/templates/login.html:34 application/templates/setting.html:209 -msgid "Password" -msgstr "密码" +#: application/templates/admin.html:55 +msgid "AutoSend" +msgstr "使能发送" + +#: application/templates/admin.html:56 +#: application/templates/change_password.html:27 +#: application/templates/reset_password.html:26 +#: application/templates/signup.html:33 +#: application/templates/user_account.html:27 +msgid "Email" +msgstr "Email" -#: application/templates/admin.html:56 application/templates/admin.html:88 -#: application/templates/adminmgrpwd.html:27 +#: application/templates/admin.html:57 +#: application/templates/user_account.html:39 msgid "Expiration" msgstr "有效期" -#: application/templates/admin.html:58 application/templates/admin.html:99 -#: application/templates/adminmgrpwd.html:29 +#: application/templates/admin.html:58 +msgid "Operation" +msgstr "操作" + +#: application/templates/admin.html:65 +msgid "Yes" +msgstr "是" + +#: application/templates/admin.html:65 +msgid "No" +msgstr "否" + +#: application/templates/admin.html:68 +#: application/templates/user_account.html:42 msgid "Never expire" msgstr "永久有效" -#: application/templates/admin.html:59 application/templates/admin.html:101 -#: application/templates/adminmgrpwd.html:30 -#: application/templates/setting.html:151 +#: application/templates/admin.html:70 application/templates/setting.html:150 +#: application/templates/user_account.html:43 msgid "7 Days" msgstr "一星期" -#: application/templates/admin.html:60 application/templates/admin.html:103 -#: application/templates/adminmgrpwd.html:31 +#: application/templates/admin.html:72 +#: application/templates/user_account.html:44 msgid "1 Month" msgstr "1个月" -#: application/templates/admin.html:61 application/templates/admin.html:105 -#: application/templates/adminmgrpwd.html:32 +#: application/templates/admin.html:74 +#: application/templates/user_account.html:45 msgid "3 Months" msgstr "3个月" -#: application/templates/admin.html:62 application/templates/admin.html:107 -#: application/templates/adminmgrpwd.html:33 +#: application/templates/admin.html:76 +#: application/templates/user_account.html:46 msgid "6 Months" msgstr "6个月" -#: application/templates/admin.html:63 application/templates/admin.html:109 -#: application/templates/adminmgrpwd.html:34 +#: application/templates/admin.html:78 +#: application/templates/user_account.html:47 msgid "1 Year" msgstr "1年" -#: application/templates/admin.html:64 application/templates/admin.html:111 -#: application/templates/adminmgrpwd.html:35 +#: application/templates/admin.html:80 +#: application/templates/user_account.html:48 msgid "2 Years" msgstr "2年" -#: application/templates/admin.html:68 -#: application/templates/adv_whitelist.html:29 application/templates/my.html:32 -msgid "Add" -msgstr "添加" - -#: application/templates/admin.html:74 -msgid "Accounts" -msgstr "用户账号列表" - #: application/templates/admin.html:85 -msgid "No." -msgstr "序号" - -#: application/templates/admin.html:87 -msgid "AutoSend" -msgstr "使能发送" +#: application/templates/change_password.html:3 +msgid "Change password" +msgstr "修改密码" #: application/templates/admin.html:89 -msgid "Operation" -msgstr "操作" - -#: application/templates/admin.html:97 -msgid "Yes" -msgstr "是" - -#: application/templates/admin.html:97 -msgid "No" -msgstr "否" - -#: application/templates/admin.html:117 application/templates/admin.html:120 -msgid "Change" -msgstr "修改" - -#: application/templates/admin.html:118 application/templates/admin.html:121 #: application/templates/adv_uploadcss.html:31 #: application/templates/adv_whitelist.html:22 -#: application/templates/base.html:22 application/templates/delaccount.html:20 +#: application/templates/base.html:22 msgid "Delete" msgstr "删除" -#: application/templates/adminmgrpwd.html:3 -msgid "Change password" -msgstr "修改密码" - #: application/templates/adv_archive.html:3 #: application/templates/adv_archive.html:13 #: application/templates/adv_base.html:59 @@ -176,12 +184,19 @@ msgstr "申请授权" msgid "Email or Username" msgstr "邮箱或用户名" +#: application/templates/adv_archive.html:55 application/templates/base.html:47 +#: application/templates/home.html:16 application/templates/login.html:25 +#: application/templates/setting.html:215 application/templates/signup.html:25 +#: application/templates/user_account.html:19 +msgid "Password" +msgstr "密码" + #: application/templates/adv_archive.html:58 application/templates/base.html:57 msgid "Verify" msgstr "校验" #: application/templates/adv_archive.html:93 -#: application/templates/setting.html:223 +#: application/templates/setting.html:230 msgid "Save settings" msgstr "保存设置" @@ -207,7 +222,7 @@ msgstr "导入订阅列表" #: application/templates/adv_base.html:77 #: application/templates/adv_base.html:81 msgid "Cover Image" -msgstr "封面图片" +msgstr "封面图像" #: application/templates/adv_base.html:86 #: application/templates/adv_base.html:90 @@ -236,7 +251,7 @@ msgid "Deliver" msgstr "推送" #: application/templates/adv_import.html:3 -#: application/templates/adv_import.html:18 +#: application/templates/adv_import.html:19 msgid "Import" msgstr "导入" @@ -245,10 +260,10 @@ msgid "Import custom rss from an OPML file." msgstr "从一个OPML文件中导入自定义RSS。" #: application/templates/adv_import.html:15 -msgid "Import as fulltext RSS by default" +msgid "Import as fulltext rss by default" msgstr "默认导入为全文订阅源" -#: application/templates/adv_import.html:19 +#: application/templates/adv_import.html:20 msgid "Download" msgstr "下载" @@ -308,17 +323,14 @@ msgid "Please input mail address" msgstr "请输入邮件地址" #: application/templates/autoback.html:3 -#: application/templates/tipsandback.html:3 msgid "Auto back" msgstr "直接返回" #: application/templates/autoback.html:28 -#: application/templates/tipsandback.html:13 msgid "Auto back to previous page after 5 seconds" msgstr "5秒钟后自动返回上一页" #: application/templates/autoback.html:29 -#: application/templates/tipsandback.html:14 #: application/templates/tipsback.html:15 msgid "Click to back" msgstr "点击直接返回" @@ -363,7 +375,7 @@ msgstr "分享链接,分享快乐" msgid "Category" msgstr "类别" -#: application/templates/base.html:33 application/templates/setting.html:130 +#: application/templates/base.html:33 application/templates/setting.html:129 msgid "Language" msgstr "语言" @@ -410,7 +422,7 @@ msgid "The recipe is already subscribed." msgstr "这个Recipe已经被订阅了。" #: application/templates/base.html:45 -msgid "Subscription info" +msgid "Website login lnformation" msgstr "网站登录信息" #: application/templates/base.html:46 @@ -428,7 +440,7 @@ msgid "" msgstr "如果任意文本框留空白,则此登录信息将从服务器删除。" #: application/templates/base.html:50 -msgid "Cannot set the subscription infomation, Error:" +msgid "Cannot set the website login information, Error:" msgstr "无法设置登录信息,错误描述:" #: application/templates/base.html:51 @@ -462,8 +474,8 @@ msgstr "在Kindle阅读" msgid "Verified" msgstr "已校验" -#: application/templates/base.html:59 application/view/login.py:67 -#: application/view/share.py:154 +#: application/templates/base.html:59 application/view/login.py:73 +#: application/view/share.py:145 msgid "The username does not exist or password is wrong." msgstr "用户名不存在或密码错误。" @@ -500,8 +512,8 @@ msgid "[Uncategoried]" msgstr "[未分类]" #: application/templates/base.html:68 -msgid "No links found in the chosen language." -msgstr "没有您选择的语种对应的链接。" +msgid "There are no links found." +msgstr "没有找到任何链接。" #: application/templates/base.html:69 msgid "Invalid report" @@ -515,35 +527,35 @@ msgstr "您确认这个链接已经失效或无法链接吗?" msgid "Customize delivery time" msgstr "自定义推送时间" -#: application/templates/base.html:72 application/templates/setting.html:50 +#: application/templates/base.html:72 application/templates/setting.html:49 msgid "Delivery days" msgstr "推送日" -#: application/templates/base.html:73 application/templates/setting.html:52 +#: application/templates/base.html:73 application/templates/setting.html:51 msgid "Mon" msgstr "一" -#: application/templates/base.html:74 application/templates/setting.html:54 +#: application/templates/base.html:74 application/templates/setting.html:53 msgid "Tue" msgstr "二" -#: application/templates/base.html:75 application/templates/setting.html:56 +#: application/templates/base.html:75 application/templates/setting.html:55 msgid "Wed" msgstr "三" -#: application/templates/base.html:76 application/templates/setting.html:58 +#: application/templates/base.html:76 application/templates/setting.html:57 msgid "Thu" msgstr "四" -#: application/templates/base.html:77 application/templates/setting.html:60 +#: application/templates/base.html:77 application/templates/setting.html:59 msgid "Fri" msgstr "五" -#: application/templates/base.html:78 application/templates/setting.html:62 +#: application/templates/base.html:78 application/templates/setting.html:61 msgid "Sat" msgstr "六" -#: application/templates/base.html:79 application/templates/setting.html:64 +#: application/templates/base.html:79 application/templates/setting.html:63 msgid "Sun" msgstr "日" @@ -559,13 +571,14 @@ msgstr "这个Recipe的自定义推送时间已经设定成功。" msgid "The account have been deleted." msgstr "这个账号已经被删除。" -#: application/templates/base.html:83 application/view/admin.py:46 -#: application/view/admin.py:66 +#: application/templates/base.html:83 application/view/admin.py:208 +#: application/view/share.py:135 msgid "The username or password is empty." msgstr "用户名或密码为空。" -#: application/templates/base.html:84 application/view/admin.py:50 -#: application/view/admin.py:70 application/view/admin.py:143 +#: application/templates/base.html:84 application/view/admin.py:85 +#: application/view/admin.py:172 application/view/admin.py:212 +#: application/view/login.py:207 application/view/login.py:267 msgid "The two new passwords are dismatch." msgstr "两个密码不匹配。" @@ -577,7 +590,7 @@ msgstr "修改密码成功。" msgid "Account added successfully." msgstr "添加账号成功。" -#: application/templates/base.html:87 application/view/login.py:106 +#: application/templates/base.html:87 application/view/login.py:117 msgid "login required" msgstr "需要登录" @@ -596,37 +609,62 @@ msgid "Logout" msgstr "退出" #: application/templates/base.html:113 application/templates/home.html:17 -#: application/templates/login.html:3 application/templates/login.html:37 +#: application/templates/login.html:3 application/templates/login.html:19 +#: application/templates/login.html:29 msgid "Login" msgstr "登录" -#: application/templates/base.html:133 application/templates/base.html:135 -#: application/templates/home.html:11 application/templates/my.html:3 +#: application/templates/base.html:115 application/templates/signup.html:3 +#: application/templates/signup.html:19 application/templates/signup.html:43 +msgid "Signup" +msgstr "注册" + +#: application/templates/base.html:135 application/templates/home.html:11 +#: application/templates/my.html:3 msgid "Feeds" msgstr "我的订阅" -#: application/templates/base.html:138 application/templates/base.html:140 -#: application/templates/setting.html:3 +#: application/templates/base.html:136 application/templates/setting.html:3 msgid "Settings" msgstr "设置" -#: application/templates/base.html:143 application/templates/base.html:145 -#: application/templates/logs.html:3 +#: application/templates/base.html:137 application/templates/logs.html:3 msgid "Logs" msgstr "投递日志" -#: application/templates/base.html:153 application/templates/base.html:155 +#: application/templates/base.html:139 msgid "Advanced" msgstr "高级设置" -#: application/templates/base.html:158 application/templates/base.html:160 -#: application/templates/library.html:3 +#: application/templates/base.html:140 application/templates/library.html:3 msgid "Shared" msgstr "网友分享" -#: application/templates/delaccount.html:3 -msgid "Delete account" -msgstr "删除此账户" +#: application/templates/change_password.html:13 +msgid "Change Password" +msgstr "修改密码" + +#: application/templates/change_password.html:15 +msgid "Old password" +msgstr "原密码" + +#: application/templates/change_password.html:19 +#: application/templates/reset_password.html:31 +#: application/templates/reset_password.html:32 +msgid "New password" +msgstr "新密码" + +#: application/templates/change_password.html:23 +#: application/templates/reset_password.html:35 +#: application/templates/reset_password.html:36 +#: application/templates/signup.html:29 +#: application/templates/user_account.html:23 +msgid "Confirm password" +msgstr "确认密码" + +#: application/templates/change_password.html:32 +msgid "Confirm Change" +msgstr "确认修改" #: application/templates/home.html:3 msgid "Home" @@ -660,55 +698,55 @@ msgid "" "share the service with friends." msgstr "使用开源 %(kindleear)s 应用,您可以部署您自己的网站,每天自动抓取全球新闻热点,并支持多账号,可以提供给多位好友同时使用。" -#: application/templates/library.html:48 application/templates/my.html:62 +#: application/templates/library.html:50 application/templates/my.html:62 msgid "Search" msgstr "搜索" -#: application/templates/login.html:40 +#: application/templates/login.html:34 application/view/login.py:184 +#: application/view/login.py:191 msgid "" -"The website doesn't allow registration. You can ask the owner for an " +"The website does not allow registration. You can ask the owner for an " "account." msgstr "这个网站不支持注册,您可以请求管理员创建账号。" -#: application/templates/logs.html:34 +#: application/templates/logs.html:10 msgid "Only display last 10 logs" msgstr "只显示最后10条日志" -#: application/templates/logs.html:45 application/templates/logs.html:86 +#: application/templates/logs.html:21 application/templates/logs.html:62 msgid "Time" msgstr "时间" -#: application/templates/logs.html:46 application/templates/logs.html:87 -#: application/templates/my.html:17 application/templates/setting.html:99 -#: application/templates/setting.html:100 +#: application/templates/logs.html:22 application/templates/logs.html:63 +#: application/templates/my.html:17 application/templates/setting.html:98 +#: application/templates/setting.html:99 application/templates/setting.html:100 #: application/templates/setting.html:101 -#: application/templates/setting.html:102 -#: application/templates/setting.html:126 +#: application/templates/setting.html:125 msgid "Title" msgstr "书籍标题" -#: application/templates/logs.html:47 application/templates/logs.html:88 +#: application/templates/logs.html:23 application/templates/logs.html:64 msgid "Size" msgstr "附件大小" -#: application/templates/logs.html:48 application/templates/logs.html:89 +#: application/templates/logs.html:24 application/templates/logs.html:65 msgid "To" msgstr "收件人" -#: application/templates/logs.html:49 application/templates/logs.html:90 +#: application/templates/logs.html:25 application/templates/logs.html:66 msgid "Status" msgstr "状态" -#: application/templates/logs.html:69 +#: application/templates/logs.html:45 msgid "There is no log" msgstr "还没有投递过" -#: application/templates/logs.html:73 +#: application/templates/logs.html:49 msgid "Logs of other users" msgstr "其他用户的日志" -#: application/templates/my.html:12 -msgid "Custom Rss" +#: application/templates/my.html:12 application/templates/setting.html:123 +msgid "Custom RSS" msgstr "自定义RSS" #: application/templates/my.html:23 @@ -743,171 +781,156 @@ msgstr "在KindleEar中订阅" msgid "Drag and drop this link to your bookmarks" msgstr "将链接拖动到书签栏" +#: application/templates/reset_password.html:3 +#: application/templates/reset_password.html:41 +msgid "Reset password" +msgstr "重置密码" + #: application/templates/setting.html:14 msgid "Your account will pause after {0}, please log in again before it expires." msgstr "你的投递将于 {0} 自动停止,每次登录后自动延期。" -#: application/templates/setting.html:15 -msgid "Delete Account" -msgstr "删除此账户" - -#: application/templates/setting.html:24 -msgid "Base Setting" +#: application/templates/setting.html:23 +msgid "Base" msgstr "基本设置" -#: application/templates/setting.html:26 +#: application/templates/setting.html:25 msgid "Auto Delivery" msgstr "自动推送" -#: application/templates/setting.html:28 +#: application/templates/setting.html:27 msgid "Recipes and custom RSS" msgstr "Recipe和自定义RSS" -#: application/templates/setting.html:29 +#: application/templates/setting.html:28 msgid "Recipes only" msgstr "仅Recipe" -#: application/templates/setting.html:30 +#: application/templates/setting.html:29 msgid "Disable all" msgstr "禁止" -#: application/templates/setting.html:34 +#: application/templates/setting.html:33 msgid "Kindle E-mail" msgstr "Kindle邮箱" -#: application/templates/setting.html:35 +#: application/templates/setting.html:34 msgid "Seperated by comma" msgstr "逗号分隔多个地址" -#: application/templates/setting.html:38 +#: application/templates/setting.html:37 msgid "Time zone" msgstr "所在时区" -#: application/templates/setting.html:67 +#: application/templates/setting.html:66 msgid "Delivery time" msgstr "推送时间" -#: application/templates/setting.html:75 +#: application/templates/setting.html:74 msgid "Book type" msgstr "投递格式" -#: application/templates/setting.html:82 +#: application/templates/setting.html:81 msgid "Device type" msgstr "设备类型" -#: application/templates/setting.html:92 +#: application/templates/setting.html:91 msgid "Others" msgstr "其他" -#: application/templates/setting.html:96 +#: application/templates/setting.html:95 msgid "Title format" msgstr "标题样式" -#: application/templates/setting.html:98 +#: application/templates/setting.html:97 msgid "Title Only" msgstr "仅标题" -#: application/templates/setting.html:106 -msgid "Book mode" -msgstr "书籍模式" - -#: application/templates/setting.html:108 -msgid "Periodical" -msgstr "期刊" - -#: application/templates/setting.html:109 -msgid "Comic" -msgstr "漫画" - -#: application/templates/setting.html:113 +#: application/templates/setting.html:112 msgid "Remove hyperlinks" msgstr "移除链接" -#: application/templates/setting.html:115 +#: application/templates/setting.html:114 msgid "Do not remove hyperlinks" msgstr "不移除链接" -#: application/templates/setting.html:116 +#: application/templates/setting.html:115 msgid "Remove image links" msgstr "移除图像上的链接" -#: application/templates/setting.html:117 +#: application/templates/setting.html:116 msgid "Remove text links" msgstr "移除文本上的链接" -#: application/templates/setting.html:118 +#: application/templates/setting.html:117 msgid "Remove all hyperlinks" msgstr "移除所有链接" -#: application/templates/setting.html:124 -msgid "Custom Rss Setting" -msgstr "自定义RSS设置" - -#: application/templates/setting.html:142 +#: application/templates/setting.html:141 msgid "Oldest article" msgstr "最旧文章" -#: application/templates/setting.html:144 +#: application/templates/setting.html:143 msgid "No limit" msgstr "不限制" -#: application/templates/setting.html:145 +#: application/templates/setting.html:144 msgid "1 Day" msgstr "一天" -#: application/templates/setting.html:146 +#: application/templates/setting.html:145 msgid "2 Days" msgstr "两天" -#: application/templates/setting.html:147 +#: application/templates/setting.html:146 msgid "3 Days" msgstr "三天" -#: application/templates/setting.html:148 +#: application/templates/setting.html:147 msgid "4 Days" msgstr "四天" -#: application/templates/setting.html:149 +#: application/templates/setting.html:148 msgid "5 Days" msgstr "五天" -#: application/templates/setting.html:150 +#: application/templates/setting.html:149 msgid "6 Days" msgstr "六天" -#: application/templates/setting.html:155 +#: application/templates/setting.html:154 msgid "Time format" msgstr "时间格式" -#: application/templates/setting.html:167 +#: application/templates/setting.html:166 msgid "Author format" msgstr "标题样式" #: application/templates/setting.html:182 -msgid "Send mail service" +msgid "Send Mail Service" msgstr "发送邮件服务" #: application/templates/setting.html:184 msgid "Service" msgstr "服务" -#: application/templates/setting.html:193 +#: application/templates/setting.html:199 msgid "ApiKey" msgstr "ApiKey" -#: application/templates/setting.html:197 +#: application/templates/setting.html:203 msgid "Host" msgstr "主机" -#: application/templates/setting.html:201 +#: application/templates/setting.html:207 msgid "Port" msgstr "端口" -#: application/templates/setting.html:213 +#: application/templates/setting.html:219 msgid "Save path" msgstr "保存路径" -#: application/templates/setting.html:219 +#: application/templates/setting.html:226 #, python-format msgid "" "Important: Please activate your kindle firstly, then goto %(personal)s " @@ -916,49 +939,79 @@ msgstr "" "注意:必须首先注册您的Kindle设备,同时请到亚马逊账户中心 %(personal)s 页面,将 %(sender)s 添加到 " "'已认可的发件人电子邮箱列表'。" -#: application/templates/setting.html:219 +#: application/templates/setting.html:226 msgid "Personal Document Settings" msgstr "个人文档设置" -#: application/view/admin.py:43 application/view/admin.py:78 -#: application/view/admin.py:149 -msgid "The password includes non-ascii chars." -msgstr "密码包含非ASCII字符。" +#: application/templates/signup.html:38 +msgid "Invitation code" +msgstr "邀请码" -#: application/view/admin.py:48 -msgid "The old password is wrong." -msgstr "原密码错误。" +#: application/templates/user_account.html:3 +msgid "User account" +msgstr "账号" -#: application/view/admin.py:64 +#: application/view/admin.py:50 application/view/setting.py:96 +msgid "Settings Saved!" +msgstr "恭喜,保存成功!" + +#: application/view/admin.py:60 application/view/admin.py:68 +#: application/view/admin.py:103 +msgid "Add account" +msgstr "添加账号" + +#: application/view/admin.py:67 application/view/admin.py:116 +#: application/view/admin.py:144 msgid "You do not have sufficient privileges." msgstr "您没有足够的权限。" -#: application/view/admin.py:68 application/view/login.py:40 +#: application/view/admin.py:81 application/view/admin.py:159 +#: application/view/login.py:203 application/view/login.py:232 +#: application/view/setting.py:61 application/view/setting.py:63 +#: application/view/setting.py:65 application/view/share.py:35 +msgid "Some parameters are missing or wrong." +msgstr "一些参数为空或错误。" + +#: application/view/admin.py:83 application/view/login.py:42 +#: application/view/login.py:209 msgid "The username includes unsafe chars." msgstr "用户名包含不安全字符。" -#: application/view/admin.py:72 +#: application/view/admin.py:87 application/view/login.py:211 msgid "Already exist the username." msgstr "此账号已经存在。" -#: application/view/admin.py:96 application/view/admin.py:117 -#: application/view/admin.py:141 +#: application/view/admin.py:93 application/view/admin.py:178 +#: application/view/admin.py:205 application/view/login.py:258 +msgid "The password includes non-ascii chars." +msgstr "密码包含非ASCII字符。" + +#: application/view/admin.py:120 application/view/admin.py:141 +#: application/view/admin.py:170 msgid "The username '{}' does not exist." msgstr "账号名 '{}' 不存在。" -#: application/view/admin.py:102 -msgid "The username is empty or you dont have right to delete it." -msgstr "账号名为空或您没有删除权限。" +#: application/view/admin.py:136 +msgid "The password will not be changed if the fields are empties." +msgstr "如果不填写密码,则密码不会被修改。" -#: application/view/admin.py:119 -msgid "Please input new password to confirm." -msgstr "请输入新密码确认。" +#: application/view/admin.py:137 application/view/admin.py:195 +msgid "Change account" +msgstr "修改用户账号" -#: application/view/admin.py:132 application/view/login.py:36 -msgid "Username is empty." -msgstr "账号名为空。" +#: application/view/admin.py:138 application/view/admin.py:196 +msgid "Change" +msgstr "修改" + +#: application/view/admin.py:193 +msgid "Change success." +msgstr "修改成功。" + +#: application/view/admin.py:210 +msgid "The old password is wrong." +msgstr "原密码错误。" -#: application/view/admin.py:159 +#: application/view/admin.py:217 msgid "Change password success." msgstr "修改密码成功。" @@ -1017,29 +1070,30 @@ msgstr "tumblr" msgid "Open in browser" msgstr "在浏览器打开" -#: application/view/adv.py:351 +#: application/view/adv.py:352 msgid "Authorization Error!
{}" msgstr "申请授权过程失败!
{}" -#: application/view/adv.py:374 +#: application/view/adv.py:375 msgid "Success authorized by Pocket!" msgstr "已经成功获得Pocket的授权!" -#: application/view/adv.py:380 +#: application/view/adv.py:381 msgid "" "Failed to request authorization of Pocket!
See details " "below:

{}" msgstr "申请Pocket授权失败!
错误信息参考如下:

{}" -#: application/view/adv.py:390 +#: application/view/adv.py:391 msgid "Request type [{}] unsupported" msgstr "不支持你请求的命令类型 [{}]" -#: application/view/adv.py:405 +#: application/view/adv.py:406 msgid "The Instapaper service encountered an error. Please try again later." msgstr "Instapaper服务器异常,请稍候再试。" -#: application/view/deliver.py:68 +#: application/view/deliver.py:68 application/view/login.py:154 +#: application/view/share.py:39 msgid "The username does not exist or the email is empty." msgstr "用户名不存在或email为空。" @@ -1056,175 +1110,227 @@ msgid "Cannot fetch data from {}, status: {}" msgstr "无法从 {} 获取数据,状态: {}" #: application/view/library.py:48 application/view/subscribe.py:192 -#: application/view/subscribe.py:301 application/view/subscribe.py:330 -#: application/view/subscribe.py:338 +#: application/view/subscribe.py:304 application/view/subscribe.py:333 +#: application/view/subscribe.py:341 msgid "The recipe does not exist." msgstr "此Recipe不存在。" -#: application/view/login.py:20 -msgid "Please input username and password." -msgstr "请输入正确的用户名和密码。" - -#: application/view/login.py:22 +#: application/view/login.py:25 msgid "Please use {}/{} to login at first time." msgstr "初次登录请使用用户名'{}'/密码'{}'。" #: application/view/login.py:38 +msgid "Username is empty." +msgstr "账号名为空。" + +#: application/view/login.py:40 msgid "The len of username reached the limit of 25 chars." msgstr "用户名超过25字符。" -#: application/view/login.py:70 application/view/login.py:72 +#: application/view/login.py:74 msgid "Forgot password?" msgstr "忘记密码?" -#: application/view/setting.py:19 +#: application/view/login.py:133 application/view/login.py:269 +msgid "The token is wrong or expired." +msgstr "Token码错误或已经逾期。" + +#: application/view/login.py:136 +msgid "Please input the correct username and email to reset password." +msgstr "请输入正确的用户名和Email来重置密码。" + +#: application/view/login.py:138 +msgid "The email of account '{name}' is {email}." +msgstr "账号 '{name}' 的Email为 {email}" + +#: application/view/login.py:159 +msgid "Reset password success, Please close this page and login again." +msgstr "修改密码成功,请关闭此页面重新登录。" + +#: application/view/login.py:162 +msgid "The email you input is not associated with this account." +msgstr "您输入的email不正确。" + +#: application/view/login.py:173 +msgid "The link to reset your password has been sent to your email." +msgstr "重置密码的邮件已经被发送至你的email。" + +#: application/view/login.py:174 +msgid "Please check your email inbox within 24 hours." +msgstr "请在24小时内检查你的email收件箱。" + +#: application/view/login.py:205 +msgid "The invitation code is invalid." +msgstr "邀请码无效。" + +#: application/view/login.py:213 +msgid "" +"Failed to create an account. Please contact the administrator for " +"assistance." +msgstr "创建账号失败,请联系管理员请求协助。" + +#: application/view/login.py:223 +msgid "Successfully created account." +msgstr "成功创建账号。" + +#: application/view/login.py:234 +msgid "Reset KindleEar password" +msgstr "重置KindleEar密码" + +#: application/view/login.py:235 +msgid "This is an automated email. Please do not reply to it." +msgstr "这个是自动发送的邮件,请勿直接回复。" + +#: application/view/login.py:236 +msgid "You can click the following link to reset your KindleEar password." +msgstr "你可以点击下面的链接来重置你的KindleEar密码。" + +#: application/view/setting.py:57 +msgid "Kindle E-mail is requied!" +msgstr "Kindle E-mail必须填写!" + +#: application/view/setting.py:59 +msgid "Title is requied!" +msgstr "书籍标题不能为空!" + +#: application/view/setting.py:124 msgid "Chinese" msgstr "中文" -#: application/view/setting.py:20 +#: application/view/setting.py:125 msgid "English" msgstr "英语" -#: application/view/setting.py:21 +#: application/view/setting.py:126 msgid "French" msgstr "法语" -#: application/view/setting.py:22 +#: application/view/setting.py:127 msgid "Spanish" msgstr "西班牙语" -#: application/view/setting.py:23 +#: application/view/setting.py:128 msgid "Portuguese" msgstr "葡萄牙语" -#: application/view/setting.py:24 +#: application/view/setting.py:129 msgid "German" msgstr "德语" -#: application/view/setting.py:25 +#: application/view/setting.py:130 msgid "Italian" msgstr "意大利语" -#: application/view/setting.py:26 +#: application/view/setting.py:131 msgid "Japanese" msgstr "日语" -#: application/view/setting.py:27 +#: application/view/setting.py:132 msgid "Russian" msgstr "俄语" -#: application/view/setting.py:28 +#: application/view/setting.py:133 msgid "Turkish" msgstr "土耳其语" -#: application/view/setting.py:29 +#: application/view/setting.py:134 msgid "Korean" msgstr "韩语" -#: application/view/setting.py:30 +#: application/view/setting.py:135 msgid "Arabic" msgstr "阿拉伯语" -#: application/view/setting.py:31 +#: application/view/setting.py:136 msgid "Czech" msgstr "捷克语" -#: application/view/setting.py:32 +#: application/view/setting.py:137 msgid "Dutch" msgstr "荷兰语" -#: application/view/setting.py:33 +#: application/view/setting.py:138 msgid "Greek" msgstr "希腊语" -#: application/view/setting.py:34 +#: application/view/setting.py:139 msgid "Hindi" msgstr "印地语" -#: application/view/setting.py:35 +#: application/view/setting.py:140 msgid "Malaysian" msgstr "马来西亚语" -#: application/view/setting.py:36 +#: application/view/setting.py:141 msgid "Bengali" msgstr "孟加拉语" -#: application/view/setting.py:37 +#: application/view/setting.py:142 msgid "Persian" msgstr "波斯语" -#: application/view/setting.py:38 +#: application/view/setting.py:143 msgid "Urdu" msgstr "乌尔都语" -#: application/view/setting.py:39 +#: application/view/setting.py:144 msgid "Swahili" msgstr "斯瓦希里语" -#: application/view/setting.py:40 +#: application/view/setting.py:145 msgid "Vietnamese" msgstr "越南语" -#: application/view/setting.py:41 +#: application/view/setting.py:146 msgid "Punjabi" msgstr "旁遮普语" -#: application/view/setting.py:42 +#: application/view/setting.py:147 msgid "Javanese" msgstr "爪哇语" -#: application/view/setting.py:43 +#: application/view/setting.py:148 msgid "Tagalog" msgstr "他加禄语" -#: application/view/setting.py:44 +#: application/view/setting.py:149 msgid "Hausa" msgstr "豪萨语" -#: application/view/setting.py:78 -msgid "Kindle E-mail is requied!" -msgstr "Kindle E-mail必须填写!" - -#: application/view/setting.py:80 -msgid "Title is requied!" -msgstr "书籍标题不能为空!" - -#: application/view/setting.py:82 application/view/setting.py:84 -#: application/view/setting.py:86 -msgid "Some parameters are missing or wrong." -msgstr "一些参数为空或错误。" - -#: application/view/setting.py:116 -msgid "Settings Saved!" -msgstr "恭喜,保存成功!" +#: application/view/share.py:50 application/view/subscribe.py:240 +msgid "Unknown command: {}" +msgstr "未知命令:{}" -#: application/view/share.py:103 -msgid "'{title}'

Saved to {act} [{email}] success." -msgstr "'{title}'

成功被保存至 {act} [{email}]。" +#: application/view/share.py:56 +msgid "There is no {} email yet." +msgstr "还没有设置 {} email." -#: application/view/share.py:125 -msgid "Failed save to Pocket.
" -msgstr "保存到 Pocket 失败。
" +#: application/view/share.py:96 application/view/share.py:121 +#: application/view/share.py:143 +msgid "Saved to your {} account." +msgstr "已经成功被保存到你的 {} 账户。" -#: application/view/share.py:127 -msgid "'{}'

Saved to your Pocket account." -msgstr " '{}'

已经成功被保存到你的Pocket账户。" +#: application/view/share.py:99 application/view/share.py:117 +#: application/view/share.py:146 +msgid "Failed save to {}." +msgstr "无法保存到 {}." -#: application/view/share.py:130 -msgid "See details below:

{}" -msgstr "下面是一些技术细节:

{}" +#: application/view/share.py:100 application/view/share.py:118 +#: application/view/share.py:147 +msgid "Reason :" +msgstr "原因:" -#: application/view/share.py:150 -msgid "'{}'

Saved to your Instapaper account." -msgstr "'{}'

已经成功被保存到你的Instapaper账户。" +#: application/view/share.py:109 +msgid "Unauthorized {} account!" +msgstr "尚未获得Pocket的授权!" -#: application/view/share.py:154 application/view/share.py:158 -msgid "Failed save to Instapaper
'{}'

Reason :" -msgstr "保存到 Instapaper 失败
'{}'

原因:" +#: application/view/share.py:122 +msgid "See details below:" +msgstr "下面是一些技术细节:" -#: application/view/share.py:158 -msgid "Unknown({})" -msgstr "未知({})" +#: application/view/share.py:145 +msgid "Unknown: {}" +msgstr "未知: {}" #: application/view/subscribe.py:55 msgid "Title or url is empty!" @@ -1246,7 +1352,7 @@ msgstr "标题或URL为空。" msgid "Failed to fetch the recipe." msgstr "抓取Recipe失败。" -#: application/view/subscribe.py:123 application/view/subscribe.py:262 +#: application/view/subscribe.py:123 application/view/subscribe.py:265 msgid "Failed to save the recipe. Error:" msgstr "保存Recipe失败。错误:" @@ -1254,33 +1360,33 @@ msgstr "保存Recipe失败。错误:" msgid "You can only delete the uploaded recipe." msgstr "您只能删除你自己上传的Recipe。" -#: application/view/subscribe.py:235 +#: application/view/subscribe.py:225 +msgid "The recipe have been subscribed, please unsubscribe it before delete." +msgstr "此Recipe已经被订阅,请先取消订阅然后再删除。" + +#: application/view/subscribe.py:238 msgid "This recipe has not been subscribed to yet." msgstr "此Recipe尚未被订阅。" -#: application/view/subscribe.py:237 -msgid "Unknown command: {}" -msgstr "未知命令:{}" - -#: application/view/subscribe.py:249 +#: application/view/subscribe.py:252 msgid "Can not read uploaded file, Error:" msgstr "无法读取上传的文件,错误:" -#: application/view/subscribe.py:257 +#: application/view/subscribe.py:260 msgid "" "Failed to decode the recipe. Please ensure that your recipe is saved in " "utf-8 encoding." msgstr "解码Recipe失败,请确保您的Recipe为utf-8编码。" -#: application/view/subscribe.py:277 -msgid "The recipe is already in the library" -msgstr "此Recipe已经在新闻源中" +#: application/view/subscribe.py:280 +msgid "The recipe is already in the library." +msgstr "此Recipe已经在新闻源中。" -#: application/view/subscribe.py:308 -msgid "The login information for this recipe has been cleared" -msgstr "此Recipe的网站登录信息已经被删除" +#: application/view/subscribe.py:311 +msgid "The login information for this recipe has been cleared." +msgstr "此Recipe的网站登录信息已经被删除。" -#: application/view/subscribe.py:312 -msgid "The login information for this recipe has been saved" -msgstr "此Recipe的网站登录信息已经保存" +#: application/view/subscribe.py:315 +msgid "The login information for this recipe has been saved." +msgstr "此Recipe的网站登录信息已经保存。" diff --git a/application/view/admin.py b/application/view/admin.py index a4bb6245..9f925303 100644 --- a/application/view/admin.py +++ b/application/view/admin.py @@ -3,6 +3,7 @@ #账号管理页面 import hashlib, datetime +from operator import attrgetter from flask import Blueprint, request, url_for, render_template, redirect, session, current_app as app from flask_babel import gettext as _ from ..base_handler import * @@ -19,145 +20,199 @@ def Admin(): #只有管理员才能管理其他用户 adminName = app.config['ADMIN_NAME'] - users = KeUser.get_all() if user.name == adminName else None - return render_template('admin.html', title='Admin', tab='admin', user=user, users=users, admin_name=adminName) + if user.name == adminName: + users = sorted(KeUser.get_all(), key=attrgetter('created_time')) + mailSrv = AppInfo.get_value(AppInfo.newUserMailService, 'admin') + signupType = AppInfo.get_value(AppInfo.signupType, 'oneTimeCode') + inviteCodes = AppInfo.get_value(AppInfo.inviteCodes, '') + return render_template('admin.html', title='Admin', tab='admin', users=users, adminName=adminName, + mailSrv=mailSrv, signupType=signupType, inviteCodes=inviteCodes, tips='') + else: + return render_template('change_password.html', tips='', tab='admin', user=user) @bpAdmin.post("/admin", endpoint='AdminPost') -@login_required(forAjax=True) +@login_required() def AdminPost(): + user = get_login_user() + #只有管理员才能管理其他用户 adminName = app.config['ADMIN_NAME'] - form = request.form + if user.name != adminName: + return redirect(url_for('bpAdmin.Admin')) + + mailSrv = request.form.get('sm_service') + signupType = request.form.get('signup_type') + inviteCodes = request.form.get('invite_codes', '') + AppInfo.set_value(AppInfo.newUserMailService, mailSrv) + AppInfo.set_value(AppInfo.signupType, signupType) + AppInfo.set_value(AppInfo.inviteCodes, inviteCodes) + users = sorted(KeUser.get_all(), key=attrgetter('created_time')) + return render_template('admin.html', title='Admin', tab='admin', users=users, adminName=adminName, + mailSrv=mailSrv, signupType=signupType, inviteCodes=inviteCodes, tips=_("Settings Saved!")) + +#管理员添加一个账号 +@bpAdmin.route("/account/add", endpoint='AdminAddAccount') +@login_required() +def AdminAddAccount(): + user = get_login_user() + if user.name != app.config['ADMIN_NAME']: + return redirect(url_for("bpLogin.Login")) + else: + return render_template('user_account.html', tips='', formTitle=_('Add account'), submitTitle=_('Add'), tab='admin') + +@bpAdmin.post("/account/add", endpoint='AdminAddAccountPost') +@login_required() +def AdminAddAccountPost(): user = get_login_user() - users = KeUser.get_all() if user.name == adminName else None - actType = form.get('actType') + if user.name != app.config['ADMIN_NAME']: + tips = _("You do not have sufficient privileges.") + return render_template('user_account.html', tips=tips, formTitle=_('Add account'), submitTitle=_('Add'), tab='admin') + + form = request.form + username = form.get('username') + password1 = form.get('password1') + password2 = form.get('password2') + email = form.get('email') + sm_service = form.get('sm_service') + expiration = str_to_int(form.get('expiration', '0')) - if actType == 'change': #修改当前登陆账号的密码 - oldPassword = form.get('op') - newP1 = form.get('p1') - newP2 = form.get('p2') + specialChars = ['<', '>', '&', '\\', '/', '%', '*', '.', '{', '}', ',', ';', '|', ' '] + tips = '' + if not all([username, password1, password2, email, sm_service]): + tips = _("Some parameters are missing or wrong.") + elif any([char in username for char in specialChars]): + tips = _("The username includes unsafe chars.") + elif password1 != password2: + tips = _("The two new passwords are dismatch.") + elif KeUser.get_or_none(KeUser.name == username): + tips = _("Already exist the username.") + else: + secret_key = new_secret_key() try: - pwd = hashlib.md5((oldPassword + user.secret_key).encode()).hexdigest() - newPwd = hashlib.md5((newP1 + user.secret_key).encode()).hexdigest() + pwd = hashlib.md5((password1 + secret_key).encode()).hexdigest() except: tips = _("The password includes non-ascii chars.") else: - if not all((oldPassword, newP1, newP2)): - tips = _("The username or password is empty.") - elif user.passwd != pwd: - tips = _("The old password is wrong.") - elif newP1 != newP2: - tips = _("The two new passwords are dismatch.") - else: - tips = 'ok' - user.passwd = newPwd - user.save() - return {'status': tips} - elif actType == 'add': #添加账户 - userName = form.get('new_username') - password1 = form.get('new_u_pwd1') - password2 = form.get('new_u_pwd2') - new_email = form.get('new_email', '') - sm_service = form.get('sm_service') #send mail service - expiration = str_to_int(form.get('new_u_expiration', '0')) - - specialChars = ['<', '>', '&', '\\', '/', '%', '*', '.', '{', '}', ',', ';', '|', ' '] - if user.name != adminName: #只有管理员能添加账号 - tips = _("You do not have sufficient privileges.") - elif not all((userName, password1, password2)): - tips = _("The username or password is empty.") - elif any([char in userName for char in specialChars]): - tips = _("The username includes unsafe chars.") - elif password1 != password2: - tips = _("The two new passwords are dismatch.") - elif KeUser.get_or_none(KeUser.name == userName): - tips = _("Already exist the username.") - else: - secret_key = new_secret_key() - try: - pwd = hashlib.md5((password1 + secret_key).encode()).hexdigest() - except: - tips = _("The password includes non-ascii chars.") - else: - send_mail_service = {'service': 'admin'} if sm_service == 'admin' else {} - au = KeUser(name=userName, passwd=pwd, kindle_email='', enable_send=False, - send_time=7, timezone=app.config['TIMEZONE'], book_type="epub", secret_key=secret_key, - expiration_days=expiration, share_links={'key': new_secret_key()}, - book_title='KindleEar', book_language='en', email=new_email, send_mail_service=send_mail_service) - if expiration: - au.expires = datetime.datetime.utcnow() + datetime.timedelta(days=expiration) - - au.save() - users = KeUser.get_all() if user.name == adminName else None - tips = 'ok' - return {'status': tips} - elif actType == 'delete': #删除账号,使用ajax请求的,返回一个字典 - name = form.get('name') - if name and (name != adminName) and (session.get('userName') in (adminName, name)): - u = KeUser.get_or_none(KeUser.name == name) - if not u: - return {'status': _("The username '{}' does not exist.").format(name)} - else: - u.erase_traces() #删除自己订阅的书,白名单,过滤器等,就是完全的清理 - u.delete_instance() - return {'status': 'ok'} - else: - return {'status': _("The username is empty or you dont have right to delete it.")} - else: #静悄悄的失败:) - return Admin() + send_mail_service = {'service': 'admin'} if sm_service == 'admin' else {} + user = KeUser(name=username, passwd=pwd, timezone=app.config['TIMEZONE'], secret_key=secret_key, + expiration_days=expiration, share_links={'key': new_secret_key()}, + email=email, send_mail_service=send_mail_service) + if expiration: + user.expires = datetime.datetime.utcnow() + datetime.timedelta(days=expiration) + user.save() + if tips: + return render_template('user_account.html', tips=tips, formTitle=_('Add account'), submitTitle=_('Add'), + user=None, tab='admin') + else: + return redirect(url_for('bpAdmin.Admin')) -#管理员修改其他账户的密码 -@bpAdmin.route("/mgrpwd/", endpoint='AdminManagePassword') -@login_required() -def AdminManagePassword(name): +#直接AJAX删除一个账号 +@bpAdmin.post("/account/delete", endpoint='AdminDeleteAccountAjax') +@login_required(forAjax=True) +def AdminDeleteAccountAjax(): user = get_login_user() - if user.name != app.config['ADMIN_NAME']: - return redirect(url_for("bpLogin.Login")) - + adminName = app.config['ADMIN_NAME'] + name = request.form.get('name') + if user.name != adminName or not name or name == adminName: + return {'status': _("You do not have sufficient privileges.")} + u = KeUser.get_or_none(KeUser.name == name) - expiration = 0 if not u: - tips = _("The username '{}' does not exist.").format(name) + return {'status': _("The username '{}' does not exist.").format(name)} else: - tips = _("Please input new password to confirm.") - expiration = u.expiration_days - - return render_template('adminmgrpwd.html', tips=tips, userName=name, expiration=expiration) - -@bpAdmin.post("/mgrpwd/", endpoint='AdminManagePasswordPost') + u.erase_traces() #删除账号订阅的书,白名单,过滤器等,就是完全的清理 + u.delete_instance() + return {'status': 'ok'} + +#修改密码,可能是修改自己的密码或管理员修改其他用户的密码 +@bpAdmin.route("/account/change/", endpoint='AdminAccountChange') @login_required() -def AdminManagePasswordPost(name): - form = request.form - fname = form.get('name') - p1 = form.get('p1') - p2 = form.get('p2') - expiration = str_to_int(form.get('ep', '0')) - tips = _("Username is empty.") +def AdminAccountChange(name): + user = get_login_user() + if user.name == name: + return render_template('change_password.html', tips='', tab='admin', user=user) + elif user.name == app.config['ADMIN_NAME']: + u = KeUser.get_or_none(KeUser.name == name) + if u: + tips = _('The password will not be changed if the fields are empties.') + return render_template('user_account.html', tips=tips, formTitle=_('Change account'), + submitTitle=_('Change'), user=u, tab='admin') + else: + return render_template('tipsback.html', title='error', urltoback=url_for('bpAdmin.Admin'), + tips=_("The username '{}' does not exist.").format(name)) + else: + return render_template('tipsback.html', title='privileges', urltoback=url_for('bpAdmin.Admin'), + tips=_('You do not have sufficient privileges.')) +@bpAdmin.post("/account/change/", endpoint='AdminAccountChangePost') +@login_required() +def AdminAccountChangePost(name): user = get_login_user() - if user.name != app.config['ADMIN_NAME']: - return redirect(url_for("bpLogin.Login")) + form = request.form + username = form.get('username') + orgpwd = form.get('orgpwd') + p1 = form.get('password1') + p2 = form.get('password2') + email = form.get('email') + u = None + if name != username: + return render_template('tipsback.html', title='error', urltoback=url_for('bpAdmin.Admin'), + tips=_('Some parameters are missing or wrong.')) + elif user.name == name: #修改自己的密码 + tips = ChangePassword(user, orgpwd, p1, p2, email) + return render_template('change_password.html', tips=tips, tab='admin', user=user) + elif user.name == app.config['ADMIN_NAME']: #管理员修改其他账号 + email = form.get('email', '') + sm_service = form.get('sm_service') + expiration = str_to_int(form.get('expiration', '0')) - if name and name != app.config['ADMIN_NAME']: - u = KeUser.get_or_none(KeUser.name == name) + u = KeUser.get_or_none(KeUser.name == username) if not u: - tips = _("The username '{}' does not exist.").format(name) - elif p1 != p2: + tips = _("The username '{}' does not exist.").format(username) + elif (p1 or p2) and (p1 != p2): tips = _("The two new passwords are dismatch.") else: - secret_key = u.secret_key or '' try: - pwd = hashlib.md5((p1 + secret_key).encode()).hexdigest() + if p1 or p2: + u.passwd = hashlib.md5((p1 + u.secret_key).encode()).hexdigest() except: tips = _("The password includes non-ascii chars.") else: - u.passwd = pwd u.expiration_days = expiration if expiration: u.expires = datetime.datetime.utcnow() + datetime.timedelta(days=expiration) else: u.expires = None + if sm_service == 'admin': + u.send_mail_service = {'service': 'admin'} + elif u.send_mail_service.get('service', 'admin') == 'admin': + send_mail_service = user.send_mail_service + send_mail_service['service'] = '' + u.send_mail_service = send_mail_service + u.email = email u.save() - strBackPage = '    Click here to go back' - tips = _("Change password success.") + strBackPage + tips = _("Change success.") - return render_template('adminmgrpwd.html', tips=tips, userName=name) + return render_template('user_account.html', tips=tips, formTitle=_('Change account'), + submitTitle=_('Change'), user=u, tab='admin') + +#修改一个账号的密码,返回执行结果字符串 +def ChangePassword(user, orgPwd, p1, p2, email): + secret_key = user.secret_key + try: + oldPwd = hashlib.md5((orgPwd + secret_key).encode()).hexdigest() + newPwd = hashlib.md5((p1 + secret_key).encode()).hexdigest() + except: + tips = _("The password includes non-ascii chars.") + else: + if not all((orgPwd, p1, p2, email)): + tips = _("The username or password is empty.") + elif user.passwd != oldPwd: + tips = _("The old password is wrong.") + elif p1 != p2: + tips = _("The two new passwords are dismatch.") + else: + user.passwd = newPwd + user.email = email + user.save() + tips = _("Change password success.") + return tips diff --git a/application/view/adv.py b/application/view/adv.py index b3432f9d..28add049 100644 --- a/application/view/adv.py +++ b/application/view/adv.py @@ -43,7 +43,7 @@ def AdvWhiteListPost(): if wlist.startswith('*@'): #输入*@xx.xx则修改为@xx.xx wlist = wlist[1:] if wlist: - WhiteList(mail=wlist, user=user.name).save() + WhiteList.get_or_create(mail=wlist, user=user.name) return redirect(url_for('bpAdv.AdvWhiteList')) #删除白名单项目 @@ -153,14 +153,14 @@ def AdvImportPost(): url = ('https:/' if url.startswith('/') else 'https://') + url if title and url: #查询是否有重复的 - dbItem = Recipe.get_or_none((Recipe.user == user.name) & (Recipe.url == url)) + dbItem = Recipe.get_or_none((Recipe.user == user.name) & (Recipe.title == title)) if dbItem: - dbItem.title = title + dbItem.url = url dbItem.isfulltext = isfulltext dbItem.save() else: - Recipe(title=title, url=url, user=user.name, isfulltext=isfulltext, type_='custom', - time=datetime.datetime.utcnow()).save() + Recipe.create(title=title, url=url, user=user.name, isfulltext=isfulltext, type_='custom', + time=datetime.datetime.utcnow()) return redirect(url_for("bpSubscribe.MySubscription")) else: @@ -217,6 +217,7 @@ def AdvExport(): #在本地选择一个图片上传做为自定义RSS书籍的封面 @bpAdv.route("/adv/cover") +@login_required() def AdvUploadCoverImage(tips=None): user = get_login_user() covers = {} @@ -319,7 +320,7 @@ def AdvDeleteCssAjaxPost(): ret = {'status': 'ok'} user = get_login_user() if request.form.get('action') == 'delete': - UserBlob.delete().where((UserBlob.name=='css') & (UserBlob.user == user.name)).execute() + UserBlob.delete().where((UserBlob.user == user.name) & (UserBlob.name=='css')).execute() return ret @@ -348,7 +349,8 @@ def AdvOAuth2(authType): request_token = pocket.get_request_token() url = pocket.get_authorize_url(request_token) except Exception as e: - return render_template('tipsback.html', title='Authorization Error', urltoback='/adv/archive', tips=_('Authorization Error!
{}').format(e)) + return render_template('tipsback.html', title='Authorization Error', urltoback=url_for('bpAdv.AdvArchive'), + tips=_('Authorization Error!
{}').format(e)) session['pocket_request_token'] = request_token return redirect(url) @@ -371,12 +373,12 @@ def AdvOAuth2Callback(authType): pocket['access_token'] = resp.get('access_token', '') user.share_links = shareLinks user.save() - return render_template('tipsback.html', title='Success authorized', urltoback='/adv/archive', tips=_('Success authorized by Pocket!')) + return render_template('tipsback.html', title='Success authorized', urltoback=url_for('bpAdv.AdvArchive'), tips=_('Success authorized by Pocket!')) except Exception as e: shareLinks[pocket] = {'enable': '', 'access_token': ''} user.share_links = shareLinks user.save() - return render_template('tipsback.html', title='Failed to authorize', urltoback='/adv/archive', + return render_template('tipsback.html', title='Failed to authorize', urltoback=url_for('bpAdv.AdvArchive'), tips=_('Failed to request authorization of Pocket!
See details below:

{}').format(e)) #通过AJAX验证密码等信息的函数 diff --git a/application/view/deliver.py b/application/view/deliver.py index 9fb0972e..5e78cc55 100644 --- a/application/view/deliver.py +++ b/application/view/deliver.py @@ -5,7 +5,6 @@ from collections import defaultdict from flask import Blueprint, render_template, request from flask_babel import gettext as _ -from ..back_end.task_queue_adpt import create_delivery_task from ..back_end.db_models import * from ..base_handler import * from ..utils import local_time, tz_now @@ -94,6 +93,7 @@ def SingleUserDelivery(userName: str, idList: list=None): #recipeId: Recipe Id, custom:xx, upload:xx, builtin:xx #separated: 是否单独推送 def queueOneBook(queueToPush: defaultdict, user: KeUser, recipeId: str, separated: bool): + from ..back_end.task_queue_adpt import create_delivery_task recipeId = recipeId.replace(':', '__') if separated: create_delivery_task({"userName": user.name, "recipeId": recipeId}) @@ -102,6 +102,7 @@ def queueOneBook(queueToPush: defaultdict, user: KeUser, recipeId: str, separate #启动推送队列中的书籍 def flushQueueToPush(queueToPush: defaultdict): + from ..back_end.task_queue_adpt import create_delivery_task for name in queueToPush: create_delivery_task({'userName': name, 'recipeId': ','.join(queueToPush[name])}) diff --git a/application/view/library.py b/application/view/library.py index c4466717..bc801b40 100644 --- a/application/view/library.py +++ b/application/view/library.py @@ -97,7 +97,7 @@ def SharedLibraryMgrPost(mgrType): keRss.append(item) else: ghStatus = srvErrStr(resp.status_code, 'github') - + ret['status'] = 'ok' if not (keStatus and ghStatus) else keStatus ret['data'] = keRss return ret diff --git a/application/view/library_offical.py b/application/view/library_offical.py index 00f0b3bb..9a7a8c07 100644 --- a/application/view/library_offical.py +++ b/application/view/library_offical.py @@ -6,7 +6,7 @@ from flask import Blueprint, render_template, request, Response from flask_babel import gettext as _ from ..base_handler import * -from ..utils import str_to_bool +from ..utils import str_to_bool, str_to_int from ..back_end.db_models import * #几个"官方"服务的地址 @@ -36,10 +36,8 @@ def SharedLibraryAppspot(): dataType = args.get('data_type') if dataType == LIBRARY_GETLASTTIME: #获取分享库的最近更新 - dbItem = AppInfo.get_or_none(AppInfo.name == 'lastSharedRssTime') - #转换为时间戳,秒数 - lastSharedRssTime = int(dbItem.time_value.timestamp()) if dbItem else 0 - return {'status': 'ok', 'data': lastSharedRssTime} + + return {'status': 'ok', 'data': str_to_int(AppInfo.get_value(AppInfo.lastSharedRssTime, '0')), 'tips': ''} else: #本来想在服务器端分页的,但是好像CPU/数据库存取资源比带宽资源更紧张,所以干脆一次性提供给客户端,由客户端分页和分类 #如果后续发现这样不理想,也可以考虑修改为服务器端分页 @@ -114,12 +112,7 @@ def SharedLibraryAppspotAjax(): #更新分类信息,用于缓存 if category: - cItem = SharedRssCategory.get_or_none(SharedRssCategory.name == category) - if cItem: - cItem.last_updated = now - else: - cItem = SharedRssCategory(name=category, last_updated=now) - cItem.save() + SharedRssCategory.replace(name=category, last_updated=now).execute() #没有其他订阅源使用此分类了 if prevCategory and not SharedRss.get_or_none(SharedRss.category == prevCategory): @@ -129,13 +122,8 @@ def SharedLibraryAppspotAjax(): #更新共享库的最新时间信息(仅用于kindleear.appspot.com"官方"共享服务器) def UpdateLastSharedRssTime(): - dbItem = AppInfo.get_or_none(AppInfo.name == 'lastSharedRssTime') - if dbItem: - dbItem.time_value = datetime.datetime.utcnow() - dbItem.save() - else: - AppInfo.create(name='lastSharedRssTime', time_value=datetime.datetime.utcnow()) - + AppInfo.set_value(AppInfo.lastSharedRssTime, str(datetime.datetime.utcnow().timestamp())) + #共享库的订阅源信息管理(仅用于kindleear.appspot.com"官方"共享服务器) @bpLibraryOffical.post(LIBRARY_MGR + "") def SharedLibraryMgrAppspotPost(mgrType): diff --git a/application/view/login.py b/application/view/login.py index 9fd43934..ea90fb96 100644 --- a/application/view/login.py +++ b/application/view/login.py @@ -2,7 +2,7 @@ # -*- coding:utf-8 -*- #登录页面 -import hashlib, datetime, time +import hashlib, datetime, time, json from urllib.parse import urljoin, urlencode from flask import Blueprint, url_for, render_template, redirect, session, current_app as app from flask_babel import gettext as _ @@ -13,14 +13,15 @@ bpLogin = Blueprint('bpLogin', __name__) +#账号名不允许的特殊字符 +specialChars = ['<', '>', '&', '\\', '/', '%', '*', '.', '{', '}', ',', ';', '|', ' '] + @bpLogin.route("/login") def Login(): # 第一次登陆时如果没有管理员帐号, # 则增加一个管理员帐号 ADMIN_NAME,密码 ADMIN_NAME,后续可以修改密码 tips = '' - if InitialAdminAccount(): - tips = (_("Please input username and password.")) - else: + if CreateAccountIfNotExist(app.config['ADMIN_NAME']): tips = (_("Please use {}/{} to login at first time.").format(app.config['ADMIN_NAME'], app.config['ADMIN_NAME'])) session['login'] = 0 @@ -30,9 +31,8 @@ def Login(): @bpLogin.post("/login") def LoginPost(): - name = request.form.get('u', '').strip() - passwd = request.form.get('p', '') - specialChars = ['<', '>', '&', '\\', '/', '%', '*', '.', '{', '}', ',', ';', '|'] + name = request.form.get('username', '').strip() + passwd = request.form.get('password', '') tips = '' if not name: tips = _("Username is empty.") @@ -44,7 +44,7 @@ def LoginPost(): if tips: return render_template('login.html', tips=tips) - InitialAdminAccount() #确认管理员账号是否存在 + CreateAccountIfNotExist(app.config['ADMIN_NAME']) #确认管理员账号是否存在 u = KeUser.get_or_none(KeUser.name == name) if u: @@ -78,22 +78,31 @@ def LoginPost(): session['role'] = '' return render_template('login.html', userName=name, tips=tips) -#判断管理员账号是否存在 -#如果管理员账号不存在,创建一个,并返回False,否则返回True -def InitialAdminAccount(): - adminName = app.config['ADMIN_NAME'] - u = KeUser.get_or_none(KeUser.name == adminName) - if u: - return True +#判断账号是否存在 +#如果账号不存在,创建一个,并返回True,否则返回False +def CreateAccountIfNotExist(name, password=None, email=None): + if KeUser.get_or_none(KeUser.name == name): + return False + password = password if password else name + email = email if email else app.config['SRC_EMAIL'] secretKey = new_secret_key() shareKey = new_secret_key() - password = hashlib.md5((adminName + secretKey).encode()).hexdigest() - KeUser.create(name=adminName, passwd=password, kindle_email='', enable_send=False, send_time=8, + try: + password = hashlib.md5((password + secretKey).encode()).hexdigest() + except Exception as e: + default_log.warning('CreateAccountIfNotExist() failed to hash password: {}'.format(str(e))) + return False + + send_mail_service = {} + if name != app.config['ADMIN_NAME'] and AppInfo.get_value(AppInfo.newUserMailService, 'admin') == 'admin': + send_mail_service = {'service': 'admin'} + + KeUser.create(name=name, passwd=password, kindle_email='', enable_send=False, send_time=6, timezone=app.config['TIMEZONE'], book_type="epub", device='kindle', expires=None, secret_key=secretKey, expiration_days=0, share_links={'key': shareKey}, book_title='KindleEar', book_language='en', - email=app.config['SRC_EMAIL']) - return False + email=email, send_mail_service=send_mail_service) + return True @bpLogin.route("/logout", methods=['GET', 'POST']) def Logout(): @@ -121,7 +130,7 @@ def ResetPasswordRoute(): return render_template('reset_password.html', tips='', userName=name, firstStep=False, token=token) else: - tips = _('The token is wrong or expired') + tips = _('The token is wrong or expired.') return render_template('tipsback.html', tips=tips, urltoback=url_for('bpLogin.ResetPasswordRoute')) tips = [_('Please input the correct username and email to reset password.')] @@ -150,7 +159,7 @@ def ResetPasswordPost(): tips = _('Reset password success, Please close this page and login again.') return render_template('reset_password.html', tips=tips, userName=name, firstStep=True) elif user.email != email: - tips = _("The email you input is not the account's email.") + tips = _("The email you input is not associated with this account.") return render_template('reset_password.html', tips=tips, userName=name, firstStep=True) else: token = new_secret_key(length=24) @@ -161,10 +170,61 @@ def ResetPasswordPost(): user.save() tips = send_resetpwd_email(user, token) if tips == 'ok': - tips = (_('The link to reset password is sent to your email.') + '
' + - _('Please check your email inputbox in 24 hours.')) + tips = (_('The link to reset your password has been sent to your email.') + '
' + + _('Please check your email inbox within 24 hours.')) return render_template('reset_password.html', tips=tips, userName=name, firstStep=True) +#注册表单 +@bpLogin.route("/signup") +def Signup(): + if app.config['ALLOW_SIGNUP']: + inviteNeed = AppInfo.get_value(AppInfo.signupType, 'oneTimeCode') != 'public' + return render_template('signup.html', tips='', inviteNeed=inviteNeed) + else: + tips = _("The website does not allow registration. You can ask the owner for an account.") + return render_template('tipsback.html', title='not allow', urltoback='/', tips=tips) + +#注册验证 +@bpLogin.post("/signup") +def SignupPost(): + if not app.config['ALLOW_SIGNUP']: + tips = _("The website does not allow registration. You can ask the owner for an account.") + return render_template('tipsback.html', title='not allow', urltoback='/', tips=tips) + + signupType = AppInfo.get_value(AppInfo.signupType, 'oneTimeCode') + inviteCodes = AppInfo.get_value(AppInfo.inviteCodes, '').splitlines() + form = request.form + name = form.get('username', '') + password1 = form.get('password1') + password2 = form.get('password2') + email = form.get('email', '') + code = form.get('invite_code') + if not all([name, password1, password2, email, len(name) < 25, '@' in email]): + tips = _("Some parameters are missing or wrong.") + elif (signupType != 'public') and (not code or (code not in inviteCodes)): + tips = _("The invitation code is invalid.") + elif password1 != password2: + tips = _("The two new passwords are dismatch.") + elif any([char in name for char in specialChars]): + tips = _("The username includes unsafe chars.") + elif KeUser.get_or_none(KeUser.name == name): + tips = _("Already exist the username.") + elif not CreateAccountIfNotExist(name, password1, email): + tips = _("Failed to create an account. Please contact the administrator for assistance.") + else: + #如果邀请码是一次性的,注册成功后从列表中去除 + if inviteCodes and signupType == 'oneTimeCode': + try: + inviteCodes.remove(code) + except: + pass + AppInfo.set_value(AppInfo.inviteCodes, '\n'.join(inviteCodes)) + + tips = _('Successfully created account.') + return render_template('tipsback.html', title='Success', urltoback=url_for('bpLogin.Login'), tips=tips) + + return render_template('signup.html', tips=tips, inviteNeed=(signupType != 'public')) + #发送重设密码邮件 #返回 'ok' 表示成功 def send_resetpwd_email(user, token): @@ -172,7 +232,7 @@ def send_resetpwd_email(user, token): return _("Some parameters are missing or wrong.") subject = _('Reset KindleEar password') - info = [_('This is auto-send email, please do not reply this email.'), + info = [_('This is an automated email. Please do not reply to it.'), _('You can click the following link to reset your KindleEar password.'), '
'] query = urlencode({'token': token, 'name': user.name}) @@ -206,4 +266,4 @@ def reset_pwd_final_step(user, token, new_p1, new_p2): else: return _("The two new passwords are dismatch.") else: - return _('The token is wrong or expired') + return _('The token is wrong or expired.') diff --git a/application/view/logs.py b/application/view/logs.py index 2cf76c4d..a7af2cb7 100644 --- a/application/view/logs.py +++ b/application/view/logs.py @@ -28,6 +28,9 @@ def Mylogs(): #每天自动运行的任务,清理过期log @bpLogs.route("/removelogs") +def RemoveLogsRoute(): + return RemoveLogs() + def RemoveLogs(): #停止过期用户的推送 now = datetime.datetime.utcnow() diff --git a/application/view/setting.py b/application/view/setting.py index 908367d0..c5571315 100644 --- a/application/view/setting.py +++ b/application/view/setting.py @@ -8,47 +8,20 @@ from ..base_handler import * from ..utils import str_to_bool, str_to_int, ke_encrypt from ..back_end.db_models import * +from ..back_end.send_mail_adpt import avaliable_sm_services from .subscribe import UpdateBookedCustomRss bpSetting = Blueprint('bpSetting', __name__) supported_languages = ['zh', 'tr_TR', 'en'] -#各种语言的语种代码和文字描述的对应关系 -def LangMap(): - return {"zh-cn": _("Chinese"), - "en-us": _("English"), - "fr-fr": _("French"), - "es-es": _("Spanish"), - "pt-br": _("Portuguese"), - "de-de": _("German"), - "it-it": _("Italian"), - "ja-jp": _("Japanese"), - "ru-ru": _("Russian"), - "tr-tr": _("Turkish"), - "ko-kr": _("Korean"), - "ar": _("Arabic"), - "cs": _("Czech"), - "nl": _("Dutch"), - "el": _("Greek"), - "hi": _("Hindi"), - "ms": _("Malaysian"), - "bn": _("Bengali"), - "fa": _("Persian"), - "ur": _("Urdu"), - "sw": _("Swahili"), - "vi": _("Vietnamese"), - "pa": _("Punjabi"), - "jv": _("Javanese"), - "tl": _("Tagalog"), - "ha": _("Hausa"),} - @bpSetting.route("/setting", endpoint='Setting') @login_required() def Setting(tips=None): user = get_login_user() - return render_template('setting.html', tab='set', user=user, tips=tips, mailSender=app.config['SRC_EMAIL'], - langMap=LangMap()) + sm_services = avaliable_sm_services() + return render_template('setting.html', tab='set', user=user, tips=tips, src_mail=app.config['SRC_EMAIL'], + langMap=LangMap(), sm_services=sm_services) @bpSetting.post("/setting", endpoint='SettingPost') @login_required() @@ -63,7 +36,7 @@ def SettingPost(): sm_srv_type = '' if user.name == os.getenv('ADMIN_NAME') or user.send_mail_service.get('service') != 'admin': sm_srv_need = True - sm_srv_type = form.get('sm_service', 'gae') + sm_srv_type = form.get('sm_service') sm_apikey = form.get('sm_apikey', '') sm_host = form.get('sm_host', '') sm_port = str_to_int(form.get('sm_port')) @@ -125,8 +98,9 @@ def SettingPost(): #根据自定义RSS的使能设置,将自定义RSS添加进订阅列表或从订阅列表移除 UpdateBookedCustomRss(user) - return render_template('setting.html', tab='set', user=user, tips=tips, mailSender=app.config['SRC_EMAIL'], - langMap=LangMap()) + sm_services = avaliable_sm_services() + return render_template('setting.html', tab='set', user=user, tips=tips, src_mail=app.config['SRC_EMAIL'], + langMap=LangMap(), sm_services=sm_services) #设置国际化语种 @bpSetting.route("/setlocale/") @@ -144,3 +118,32 @@ def get_locale(): except: #Working outside of request context langCode = 'en' return langCode if langCode else request.accept_languages.best_match(supported_languages) + +#各种语言的语种代码和文字描述的对应关系 +def LangMap(): + return {"zh-cn": _("Chinese"), + "en-us": _("English"), + "fr-fr": _("French"), + "es-es": _("Spanish"), + "pt-br": _("Portuguese"), + "de-de": _("German"), + "it-it": _("Italian"), + "ja-jp": _("Japanese"), + "ru-ru": _("Russian"), + "tr-tr": _("Turkish"), + "ko-kr": _("Korean"), + "ar": _("Arabic"), + "cs": _("Czech"), + "nl": _("Dutch"), + "el": _("Greek"), + "hi": _("Hindi"), + "ms": _("Malaysian"), + "bn": _("Bengali"), + "fa": _("Persian"), + "ur": _("Urdu"), + "sw": _("Swahili"), + "vi": _("Vietnamese"), + "pa": _("Punjabi"), + "jv": _("Javanese"), + "tl": _("Tagalog"), + "ha": _("Hausa"),} diff --git a/application/view/subscribe.py b/application/view/subscribe.py index 0af33c6a..b5bfaaa4 100644 --- a/application/view/subscribe.py +++ b/application/view/subscribe.py @@ -58,12 +58,12 @@ def MySubscriptionPost(): url = ('https:/' if url.startswith('/') else 'https://') + url #判断是否重复 - if url.lower() in [item.url.lower() for item in user.all_custom_rss()]: + if Recipe.get_or_none((Recipe.user == user.name) & (Recipe.title == title)): return redirect(url_for("bpSubscribe.MySubscription", tips=(_("Duplicated subscription!")))) - - Recipe(title=title, url=url, isfulltext=isfulltext, type_='custom', user=user.name, - time=datetime.datetime.utcnow()).save() - return redirect(url_for("bpSubscribe.MySubscription")) + else: + Recipe.create(title=title, url=url, isfulltext=isfulltext, type_='custom', user=user.name, + time=datetime.datetime.utcnow()) + return redirect(url_for("bpSubscribe.MySubscription")) #添加/删除自定义RSS订阅的AJAX处理函数 @bpSubscribe.post("/customrss/", endpoint='FeedsAjaxPost') @@ -90,11 +90,11 @@ def FeedsAjaxPost(actType): fromSharedLibrary = str_to_bool(form.get('fromsharedlibrary', '')) recipeId = form.get('recipeId', '') - respDict = {'status':'ok', 'title':title, 'url':url, 'isfulltext':isfulltext, 'recipeId': recipeId} + ret = {'status':'ok', 'title':title, 'url':url, 'isfulltext':isfulltext, 'recipeId': recipeId} if not title or not (url or recipeId): - respDict['status'] = _("The Title or Url is empty.") - return respDict + ret['status'] = _("The Title or Url is empty.") + return ret #如果url不存在,则可能是分享的recipe,需要连接服务器获取recipe代码 if not url: @@ -106,43 +106,42 @@ def FeedsAjaxPost(actType): resp = opener.open(buildKeUrl(path), {'recipeId': recipeId}) if resp.status_code != 200: - respDict['status'] = _("Failed to fetch the recipe.") - return respDict + ret['status'] = _("Failed to fetch the recipe.") + return ret if recipeId.startswith('http'): src = resp.text else: data = resp.json() if data.get('status') != 'ok': - respDict['status'] = data.get('status', '') - return respDict + ret['status'] = data.get('status', '') + return ret src = data.get('src', '') try: params = SaveRecipeIfCorrect(user, src) except Exception as e: return {'status': _("Failed to save the recipe. Error:") + str(e)} - respDict.update(params) + ret.update(params) else: #自定义RSS if not url.lower().startswith('http'): url = ('https:/' if url.startswith('/') else 'https://') + url - respDict['url'] = url + ret['url'] = url #判断是否重复 - if url.lower() in [item.url.lower() for item in user.all_custom_rss()]: - respDict['status'] = _("Duplicated subscription!") - return respDict - - rss = Recipe(title=title, url=url, isfulltext=isfulltext, type_='custom', user=user.name, - time=datetime.datetime.utcnow()) - rss.save() - respDict['id'] = rss.recipe_id - UpdateBookedCustomRss(user) + if Recipe.get_or_none((Recipe.user == user.name) & (Recipe.title == title)): + ret['status'] = _("Duplicated subscription!") + return ret + else: + rss = Recipe.create(title=title, url=url, isfulltext=isfulltext, type_='custom', user=user.name, + time=datetime.datetime.utcnow()) + ret['id'] = rss.recipe_id + UpdateBookedCustomRss(user) #如果是从共享库中订阅的,则通知共享服务器,提供订阅数量信息,以便排序 if fromSharedLibrary: SendNewSubscription(title, url, recipeId) - return respDict + return ret else: return {'status': 'Unknown command: {}'.format(actType)} @@ -207,9 +206,9 @@ def RecipeAjaxPost(actType): dbInst.separated = separated dbInst.save() else: - BookedRecipe(recipe_id=recipeId, separated=separated, user=user.name, title=title, + BookedRecipe.create(recipe_id=recipeId, separated=separated, user=user.name, title=title, description=desc, needs_subscription=needSubscription, - time=datetime.datetime.utcnow()).save() + time=datetime.datetime.utcnow()) respDict['title'] = title respDict['desc'] = desc @@ -219,10 +218,13 @@ def RecipeAjaxPost(actType): elif actType == 'delete': #删除已经上传的recipe if recipeType == 'builtin': return {'status': _('You can only delete the uploaded recipe.')} - - BookedRecipe.delete().where((BookedRecipe.user == user.name) & (BookedRecipe.recipe_id == recipeId)).execute() - recipe.delete_instance() - return {'status': 'ok', 'id': recipeId} + else: + bkRecipe = user.get_booked_recipe(recipeId) + if bkRecipe: + return {'status': _('The recipe have been subscribed, please unsubscribe it before delete.')} + else: + recipe.delete_instance() + return {'status': 'ok', 'id': recipeId} elif actType == 'schedule': #设置某个recipe的自定义推送时间 dbInst = BookedRecipe.get_or_none(BookedRecipe.recipe_id == recipeId) if dbInst: @@ -272,15 +274,14 @@ def SaveRecipeIfCorrect(user: KeUser, src: str): recipe = compile_recipe(src) #判断是否重复 - oldRecipe = Recipe.get_or_none(Recipe.title == recipe.title) + oldRecipe = Recipe.get_or_none((Recipe.user == user.name) & (Recipe.title == recipe.title)) if oldRecipe: raise Exception(_('The recipe is already in the library.')) params = {"title": recipe.title, "description": recipe.description, "type_": 'upload', "needs_subscription": recipe.needs_subscription, "src": src, "time": datetime.datetime.utcnow(), "user": user.name, "language": recipe.language} - dbInst = Recipe(**params) - dbInst.save() + dbInst = Recipe.create(**params) params.pop('src') params.pop('time') params.pop('type_') diff --git a/config.py b/config.py index f1e4f8d8..e9a8b7f2 100644 --- a/config.py +++ b/config.py @@ -26,12 +26,12 @@ #Email receiving service, "gae" | "" INBOUND_EMAIL_SERVICE = "gae" -#Select the type of task queue, "gae" | "celery" | "" -TASK_QUEUE_SERVICE = "" +#Select the type of task queue, "gae" | "celery" | "rq" | "" +TASK_QUEUE_SERVICE = "celery" -#If task queue service is celery -CELERY_BROKER_URL = "redis://127.0.0.1:6379/" -CELERY_RESULT_BACKEND = "redis://127.0.0.1:6379/" +#If task queue service is celery | rq +TASK_QUEUE_BROKER_URL = "redis://127.0.0.1:6379/" +TASK_QUEUE_RESULT_BACKEND = "redis://127.0.0.1:6379/" #If this option is empty, temporary files will be stored in memory #Setting this option can reduce memory consumption, supports both relative and absolute paths @@ -40,6 +40,9 @@ #If the depolyment plataform supports multi-threads, set this option will boost the download speed DOWNLOAD_THREAD_NUM = 1 +#If the website allow visitors to signup or not +ALLOW_SIGNUP = False + #------------------------------------------------------------------------------------ #Configurations below this line generally do not need to be modified #------------------------------------------------------------------------------------ diff --git a/docs/Chinese/1.intro.md b/docs/Chinese/1.intro.md index 63a6ccf7..606051fb 100644 --- a/docs/Chinese/1.intro.md +++ b/docs/Chinese/1.intro.md @@ -3,5 +3,6 @@ sort: 1 --- # Intro -KindleEar是开源免费网络应用,可以部署在大多数支持Python的托管平台,包括但不限于google appengine,Heroku,Pythonanywhere,主要功能是自动定期通过RSS收集网络文章然后制作成图文并茂的电子书推送至你的Kindle。 +KindleEar是一个开源免费网络应用,可以部署在大多数支持Python的托管平台,包括但不限于google cloud、Heroku、Pythonanywhere、VPS、ubuntu、树莓派等,主要功能是自动定期通过RSS收集网络文章然后制作成图文并茂的电子书推送至你的Kindle或其他电子阅读设备。 +Kindle修改和提取了著名电子书管理软件Calibre的epub/mobi生成模块,除了直接输入RSS链接地址即可推送外,还直接支持Calibre的Recipe格式(抓取各种网站信息的Python脚本),已经内置一千多个Recipe,涵盖多种语种,除此之外,您也可以编写自己的Recipe然后在管理页面上传到KindleEar。 diff --git a/docs/Chinese/2.deployment.md b/docs/Chinese/2.deployment.md index 16df5e81..91b1cf3a 100644 --- a/docs/Chinese/2.deployment.md +++ b/docs/Chinese/2.deployment.md @@ -3,5 +3,10 @@ sort: 2 --- # 部署步骤 -页面上下载KindleEar的最新版本,在页面的右下角有一个按钮"Download ZIP",点击即可下载一个包含全部源码的ZIP文档,然后解压到你喜欢的目录,比如C:\KindleEar(下面以这个为例)。 + +## google cloud +1. github页面上下载KindleEar的最新版本,在页面的右下角有一个按钮"Download ZIP",点击即可下载一个包含全部源码的ZIP文档,然后解压到你喜欢的目录,比如C:\KindleEa。 +2. 安装标准环境google cloud SDK/gloud CLI,并且执行 `gcloud init` + + diff --git a/docs/Chinese/3.faq.md b/docs/Chinese/3.faq.md new file mode 100644 index 00000000..122b4776 --- /dev/null +++ b/docs/Chinese/3.faq.md @@ -0,0 +1,71 @@ +# FAQ + + +## 何为全文RSS? +全文RSS是我给这一类RSS的称呼,也不知道正确的名字是什么,在Calibre里面叫 Embedded Content。 +这一类RSS在其XML文件中已经给出了文章的全文内容,只需要进行一次连接,即可获得很多篇文章的全文内容,而不需要像普通的RSS一样,每一篇文章都需要连接网络抓取其文章内容,大大节省了时间和其他资源消耗。 +如何确认自己要订阅的RSS是否是全文RSS?很简单,使用浏览器打开RSS对应的链接,查看是否已经有全部的文章内容,如果是,则为全文RSS。如果仅给出文章摘要,则不是。 + + +## 全文RSS能否按照摘要RSS处理?反之是否可以? +全文RSS当然可以按照摘要RSS处理,这样就忽略RSS链接中给出的文章内容,而直接到原链接中获取,只是多耗费一些时间,导致支持的RSS数量下降。如果是摘要RSS,则不能按全文RSS处理,否则会导致文章内容不全。 + + +## 如何给某个Recipe自定义推送时间? +除了在设置页面设置一个统一的推送日和时间外,每个Recipe都可以自定义一个自己唯一的推送日和推送时间,一旦设置,则统一的推送时间设置将对此Recipe无效。方法是在“我的订阅”的“已订阅”区段,点击某个Recipe后边的圆形按钮,使用弹出的“自定义推送时间”按钮进行设置,可以设置为仅某一天或几天推送,也可以设定一天推送多次。 +不过自定义推送时间仅针对内置和上传的Recipe,自定义RSS仅使用统一的推送日和推送时间,如果要设置自定义RSS的推送时间,则可以将其title和url写到一个Recipe里面,再上传即可开始设置。 + + +## 如何自定义封面? +KindleEar内置7个封面,默认随机选取,也可以配置为按周内日选取。你可以上传自己喜欢的封面来替换这些内置封面,入口在“高级设置”的“上传封面图像”菜单。 +如果要单独给某个Recipe设置封面,则需要在其Recipe源代码中添加一个 cover_url 属性,可以为本地文件(如果是相对目录,则相对于KindleEar应用目录)或网络图像,比如: +``` +cover_url = '/temp/mycover.jpg' +cover_url = 'application/images/mycover.jpg' +cover_url = 'https://www.google.com/mycover.jpg' +``` +额外的,如果要自定义报头,则添加一个 masthead_url 属性,格式和 cover_url 一致。 + + +## 忘记密码了怎么办? +KindleEar不保存密码原文,无法取回密码。在登录时密码验证错误时会有一个“忘记密码?”链接,点击这个链接就可以使用创建账号时登记的email邮箱来重置密码,管理员的重置密码email邮箱就是config.py里面的SRC_EMAIL。 + + +## xxx@appid.appspotmail.com 邮件地址怎么用? +如果您的应用是部署在Google cloud平台(GAE),KindleEar还额外赠送了一个邮件服务 xxx@appid.appspotmail.com ,在部署好KindleEar后,你自动拥有了无数个EMAIL邮箱地址,格式为:xxx@appid.appspotmail.com,xxx为任意合法字符串,appid为你的应用名。 + +1. 要使用此功能,先要添加白名单,如果为 '\*' 则允许所有邮件,否则格式为 'xx@xx.xx' 或 '@xx.xx' (不包括单引号)。 +2. 此邮箱将收到的邮件正文转换为邮件附件推送至你注册的Email邮箱。如果邮件中只有链接(多个链接则每行一个),则抓取链接的网页内容制作成电子书然后再推送。 +3. 如果在邮件主题最后添加了标识 !links,则不论邮件内容如何,KindleEar都只会提取邮件中的链接,然后抓取网页,制作成电子书发送至你的Kindle。这个功能最适合将网络连载网页直接发送至Kindle观看。 +4. 如果在邮件主题后添加了标识 !article,则忽略所有链接,直接将内容转换为电子书发送。 +5. 默认推送至管理员注册的邮箱,如果要推送至其他用户的邮箱,则使用格式:username__xxx@appid.appspotmail.com。(注意是双下划线) +6. 如果将电子书下载链接发送至 book@appid.appspotmail.com 或 username__book@appid.appspotmail.com 则KindleEar直接下载对应的电子书并转发至注册的邮箱(注意GAE对后缀名有限制,不能发送可能有安全隐患的文件后缀比如exe等,zip文件能发送,但是zip文件内不能包含可能有安全隐患的文件)。 +GAE可邮件发送的后缀名列表参见:[Mail Python API Overview](https://cloud.google.com/appengine/docs/python/mail/#Python_Sending_mail_with_attachments) +(book/file/download邮件地址保留为下载电子书使用) +7. 发送至 trigger@appid.appspotmail.com 或 username__trigger@appid.appspotmail.com,则触发一次手动投递。邮件标题为空或为all则完全等同于网页上的“现在投递”按钮。如果需要推送特定书籍,则在标题上写书籍名字,多个书籍名字使用逗号分隔。 +8. 发送至 debug@appid.appspotmail.com 的邮件则直接抓取邮件中的链接并直接发送HTML文件至管理员邮箱而不是Kindle邮箱。 + + +## 有的网站需要登录才能阅读文章的问题如何解决? +有些网站需要先注册账户然后登录后才能阅读和下载文章,对于此类网站,则可以在Recipe源代码中添加一个属性: +``` +needs_subscription = True +``` +然后订阅后即可以在对应Recipe的弹出菜单中选择“网站登录信息”,可以输入登录账号和密码。 +1. 需要执行javascript或要输入验证码的网站无法支持。 +2. 对于一些足够特殊和复杂的网站,可能你需要在书籍子类中重写 get_browser() 函数。 +3. 你输入的密码将加密保存,密钥为每个账号都不一样的8位随机字符串,有一定的安全性,而且我尽量照顾你的密码安全,你可以随时删除保存的密码信息,书籍退订后也马上删除密码,但因为密钥也保存在GAE上,所以不能保证很高的安全性,请自己明白并愿意承担其中的风险。 + + +## 订阅Recipe时“订阅”和“订阅(单独推送)”选项有什么区别? +“订阅”是合并推送,将所有按这个选项订阅的Recipe和自定义RSS都合并为一个文件推送,“订阅(单独推送)”是将这个Recipe单独创建一个文件推送,更适合一些生成文件比较大或有特殊推送时间的Recipe。 + + +## 投递日志状态显示 wrong SRC_EMAIL? +到 [GAE后台](https://console.cloud.google.com/appengine) 的Settings页面,看看 已经授权的Email列表里面有没有你的发送邮箱地址,如果没有就添加即可。 + + +## 我还有更多问题,到哪里去问? +如果你碰到更多问题,可以到 [https://github.com/cdhigh/KindleEar/issues](https://github.com/cdhigh/KindleEar/issues) 去提交一个issue,然后等待答复,不过在提交问题之前,还是建议先搜索一下别人先前提交的issues,说不定已经有人重复提过了相关问题呢? +如果没有别人提过类似问题,你在提交新问题的时候,建议附上 [GAE后台](https://console.cloud.google.com/appengine) 或你部署的平台的的Logs信息,以便定位问题,也可以得到更快的答复。 + diff --git a/docs/Chinese/README.md b/docs/Chinese/README.md index 296e64fb..13a7a317 100644 --- a/docs/Chinese/README.md +++ b/docs/Chinese/README.md @@ -1,10 +1,16 @@ --- -sort: 1 +sort: 2 --- # 中文文档 -- [简介&部署](https://cdhigh.github.io/Chinese/1.intro.html) +- [简介](https://cdhigh.github.io/Chinese/1.intro.html) + +- [部署方法](https://cdhigh.github.io/Chinese/2.deployment.html) + - [Google cloud](https://cdhigh.github.io/Chinese/2.deployment.html#gae) + - [Pythonanywhere](https://cdhigh.github.io/Chinese/2.deployment.html#pythonanywhere) + - [Ubuntu](https://cdhigh.github.io/Chinese/2.deployment.html#ubuntu) + +- [FAQ](https://cdhigh.github.io/Chinese/3.faq.html) -- [快速入门](https://cdhigh.github.io/Chinese/2.deployment.html) diff --git a/docs/English/3.faq.md b/docs/English/3.faq.md new file mode 100644 index 00000000..b8bc6db1 --- /dev/null +++ b/docs/English/3.faq.md @@ -0,0 +1,65 @@ +# FAQ + + +## What is full-text RSS? +Full-text RSS is the term I use for this type of RSS, and I don't know what the correct name is. In Calibre, it's called Embedded Content. This type of RSS already provides the full content of the article in its XML file. You only need to make one connection to get the full content of many articles, instead of having to connect to the network for each article like with regular RSS, saving a lot of time and other resource consumption. +How to confirm whether the RSS you want to subscribe to is full-text RSS? It's simple. Use a browser to open the corresponding link of the RSS and see if all the article content is already there. If it is, then it's full-text RSS. If only article summaries are provided, then it's not. + + +## Can full-text RSS be treated as summary RSS, and vice versa? +Of course, full-text RSS can be treated as summary RSS, which ignores the article content provided in the RSS link and directly fetches it from the original link, but it takes a little longer, resulting in a decrease in the number of supported RSS feeds. If it's summary RSS, it cannot be treated as full-text RSS, otherwise it will result in incomplete article content. + + +## How to customize the delivery time for a Recipe? +In addition to setting a unified delivery day and time on the settings page, each Recipe can customize its own unique delivery day and time. Once set, the unified time setting will be ignored for this Recipe. The method is to click on the circular button next to a Recipe in the "Subscribed" section of "My Feeds," then use the "Customize delivery time" button that pops up to set it. You can set it to push only on certain days or multiple times a day. +However, custom push time is only applicable to built-in and uploaded Recipes. Custom RSS feeds use only the unified time set. If you want to set the push time for a custom RSS feed, you can write its title and URL into a Recipe, and then upload it to start setting. + + +## How to customize the cover? +KindleEar has 7 built-in covers, which are randomly selected by default or can be configured to be selected by day of the week. You can upload your own favorite covers to replace these built-in covers. The entry is in the "Cover Image" under "Advanced" section. +If you want to set a cover for a specific Recipe, you need to add a cover_url attribute in its Recipe source code. It can be a local file (if it's a relative directory, it's relative to the KindleEar application directory) or a web image, for example: +``` +cover_url = '/temp/mycover.jpg' +cover_url = 'application/images/mycover.jpg' +cover_url = 'https://www.google.com/mycover.jpg' +``` +Additionally, if you want to customize the masthead, add a masthead_url attribute, which has the same format as cover_url. + + +## What if I forget my password? +KindleEar does not store passwords in plain text and cannot retrieve them. If login fails due to password verification, a "Forgot Password?" link is provided. Click on this link to reset your password using the email address registered when creating your account. The reset password email address for the administrator is SRC_EMAIL in config.py. + + +## How to use the xxx@appid.appspotmail.com email address? +If your application is deployed on the Google Cloud Platform (GAE), KindleEar also provides an additional email service xxx@appid.appspotmail.com. After deploying KindleEar, you automatically have countless EMAIL email addresses in the format: xxx@appid.appspotmail.com, where xxx is any legal string and appid is your application name. +1. To use this feature, you need to add whitelist items firstly. If it's '\*', it allows all emails. Otherwise, the format is 'xx@xx.xx' or '@xx.xx' (without single quotes). +2. This service will send the contents of received emails as email attachments to your registered kindle email address. If there are only links in the email (multiple links are one per line), KindleEar will fetch the web page content and create an e-book, then send it. +3. If the subject of the email is followed by the identifier !links, KindleEar will only extract the links from the email regardless of its content, then fetch the web pages and send them to your Kindle. This feature is best for directly sending serialized web pages to Kindle for viewing. +4. If the subject of the email is followed by the identifier !article, all links will be ignored, and the content will be directly converted into an e-book and sent. +5. By default, it's sent to the email registered by the administrator. If you want to send it to another user's email, use the format: username__xxx@appid.appspotmail.com. (Note the double underscore) +6. If you send the download link of an e-book to book@appid.appspotmail.com or username__book@appid.appspotmail.com, KindleEar will directly download the corresponding e-book archive and forward it to the registered email address. (Note: GAE has restrictions on file extensions that can be emailed and cannot send file extensions that may have security risks such as exe, zip files can be sent, but zip files cannot contain files with potential security risks.) GAE's list of email-able file extensions: [Mail Python API Overview](https://cloud.google.com/appengine/docs/python/mail/#Python_Sending_mail_with_attachments) (book/file/download email addresses reserved for downloading e-books) +7. Sending to trigger@appid.appspotmail.com or username__trigger@appid.appspotmail.com triggers a manual delivery. If the subject is empty or all, it's exactly the same as the "Deliver Now" button on the webpage. If you need to push specific books, write the book name in the subject, separated by commas. +8. Emails sent to debug@appid.appspotmail.com directly extract the links from the email and send HTML files directly to the administrator's email (SRC_EMAIL) instead of the Kindle email. + + +## What if some websites require login to read articles? +Some websites require registering an account and logging in to read and download articles. For such websites, you can add an attribute in the Recipe source code: +``` +needs_subscription = True +``` +Then, after subscribing, you can select "Website Login Information" from the corresponding Recipe's popup menu to enter your login account and password. +1. Websites that require executing JavaScript or entering a captcha are not supported. +2. For some sufficiently special and complex websites, you may need to override the get_browser() function in the book subclass. +3. The password you enter is encrypted and saved, with a unique 8-character random string key for each account, which has a certain level of security. I try my best to take care of your password security. You can delete the saved password information at any time, and the password is also deleted immediately after unsubscribing from the book. However, because the key is also saved in database, the security cannot be guaranteed to be very high. Please understand and be willing to bear the risks involved. + + +## What's the difference between "Subscribe" and "Subscribe (Deliver Separately)"? +"Subscribe" is for combined delivery, which combines all Recipes and custom RSS feeds subscribed to with this option into one file for delivery. "Subscribe (Deliver Separately)" creates a separate file for this Recipe for delivery, which is more suitable for Recipes that generate large file or have special delivery times. + + +## Why does the delivery log status show wrong SRC_EMAIL? +Go to the Settings page of [GAE backend](https://console.cloud.google.com/appengine) and check if your sending email address is in the list of authorized emails. If not, add it. + + +## I have more questions, where can I ask? +If you have more questions, you can submit an issue at [https://github.com/cdhigh/KindleEar/issues](https://github.com/cdhigh/KindleEar/issues) and wait for a reply. Before submitting a question, it's recommended to search for previously submitted issues first. Maybe someone has already submitted a similar issue? If no one has submitted a similar issue, when you submit a new one, it's recommended to attach the Logs information of [GAE backend](https://console.cloud.google.com/appengine) or the platform you deployed to for problem location, which can also get you a faster reply. diff --git a/docs/English/README.md b/docs/English/README.md index 25c9d7ab..63dfe096 100644 --- a/docs/English/README.md +++ b/docs/English/README.md @@ -3,7 +3,8 @@ sort: 1 --- # English docs -- [Intro&Deployment](https://cdhigh.github.io/Chinese/1.intro.html) +- [Intro](https://cdhigh.github.io/English/1.intro.html) -- [Usage](https://cdhigh.github.io/Chinese/2.deployment.html) +- [Deployment](https://cdhigh.github.io/English/2.deployment.html) +- [FAQ](https://cdhigh.github.io/English/3.faq.html) diff --git a/docs/Gemfile b/docs/Gemfile index ccd8f63f..b55e7479 100644 --- a/docs/Gemfile +++ b/docs/Gemfile @@ -1,4 +1,4 @@ -source "https://gems.ruby-china.com" +source "https://rubygems.org" gem "jekyll-rtd-theme" gem "github-pages", group: :jekyll_plugins \ No newline at end of file diff --git a/docs/_config.yml b/docs/_config.yml index e0c012df..5011c04a 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -12,4 +12,10 @@ exclude: - CNAME - Gemfile - Gemfile.lock + +plugins: + - jemoji + - jekyll-avatar + - jekyll-mentions + \ No newline at end of file diff --git a/main.py b/main.py index 55f1fff2..dbd96c22 100644 --- a/main.py +++ b/main.py @@ -34,8 +34,8 @@ def set_env(): os.environ['DATABASE_PASSWORD'] = DATABASE_PASSWORD os.environ['TASK_QUEUE_SERVICE'] = TASK_QUEUE_SERVICE - os.environ['CELERY_BROKER_URL'] = CELERY_BROKER_URL - os.environ['CELERY_RESULT_BACKEND'] = CELERY_RESULT_BACKEND + os.environ['TASK_QUEUE_BROKER_URL'] = TASK_QUEUE_BROKER_URL + os.environ['TASK_QUEUE_RESULT_BACKEND'] = TASK_QUEUE_RESULT_BACKEND os.environ['KE_DOMAIN'] = 'http://127.0.0.1:5000/' #KE_DOMAIN os.environ['SRC_EMAIL'] = SRC_EMAIL os.environ['ADMIN_NAME'] = ADMIN_NAME @@ -49,7 +49,6 @@ def set_env(): from application.back_end.db_models import create_database_tables create_database_tables() - def main(): if len(sys.argv) <= 1: #os.environ['DATASTORE_DATASET'] = app.config['APP_ID'] diff --git a/messages.pot b/messages.pot index 04b49297..a3bbda31 100644 --- a/messages.pot +++ b/messages.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-02-14 21:49-0300\n" +"POT-Creation-Date: 2024-02-17 14:29-0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,141 +17,149 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Generated-By: Babel 2.14.0\n" -#: application/templates/admin.html:3 application/templates/base.html:148 -#: application/templates/base.html:150 +#: application/templates/admin.html:3 application/templates/base.html:138 msgid "Admin" msgstr "" -#: application/templates/admin.html:14 -msgid "Change Password" +#: application/templates/admin.html:19 +msgid "Signup settings" msgstr "" #: application/templates/admin.html:19 -msgid "Old password" +msgid "Save" +msgstr "" + +#: application/templates/admin.html:21 +#: application/templates/user_account.html:31 +msgid "Email service" msgstr "" #: application/templates/admin.html:23 -#: application/templates/adminmgrpwd.html:19 -#: application/templates/adminmgrpwd.html:20 -msgid "New password" +#: application/templates/user_account.html:34 +msgid "Same as admin" msgstr "" -#: application/templates/admin.html:27 application/templates/admin.html:52 -#: application/templates/adminmgrpwd.html:23 -#: application/templates/adminmgrpwd.html:24 -msgid "Confirm password" +#: application/templates/admin.html:24 +#: application/templates/user_account.html:35 +msgid "Independent" +msgstr "" + +#: application/templates/admin.html:28 +msgid "Signup type" +msgstr "" + +#: application/templates/admin.html:30 +msgid "Public" msgstr "" #: application/templates/admin.html:31 -#: application/templates/adminmgrpwd.html:39 -msgid "Confirm Change" +msgid "One time code" msgstr "" -#: application/templates/admin.html:39 -msgid "Add Account" +#: application/templates/admin.html:32 +msgid "Permanent code" msgstr "" -#: application/templates/admin.html:44 application/templates/admin.html:86 -#: application/templates/adminmgrpwd.html:15 -#: application/templates/adminmgrpwd.html:16 -#: application/templates/delaccount.html:16 application/templates/home.html:15 -#: application/templates/login.html:29 application/templates/login.html:30 -#: application/templates/logs.html:85 application/templates/setting.html:205 -msgid "Username" +#: application/templates/admin.html:36 +msgid "Invitation codes" msgstr "" -#: application/templates/admin.html:48 -#: application/templates/adv_archive.html:55 application/templates/base.html:47 -#: application/templates/home.html:16 application/templates/login.html:33 -#: application/templates/login.html:34 application/templates/setting.html:209 -msgid "Password" +#: application/templates/admin.html:43 +msgid "Accounts" msgstr "" -#: application/templates/admin.html:56 application/templates/admin.html:88 -#: application/templates/adminmgrpwd.html:27 -msgid "Expiration" +#: application/templates/admin.html:43 +#: application/templates/adv_whitelist.html:29 application/templates/my.html:32 +#: application/view/admin.py:60 application/view/admin.py:68 +#: application/view/admin.py:103 +msgid "Add" msgstr "" -#: application/templates/admin.html:58 application/templates/admin.html:99 -#: application/templates/adminmgrpwd.html:29 -msgid "Never expire" +#: application/templates/admin.html:54 application/templates/home.html:15 +#: application/templates/login.html:21 application/templates/logs.html:61 +#: application/templates/reset_password.html:19 +#: application/templates/reset_password.html:20 +#: application/templates/setting.html:211 application/templates/signup.html:21 +#: application/templates/user_account.html:15 +msgid "Username" msgstr "" -#: application/templates/admin.html:59 application/templates/admin.html:101 -#: application/templates/adminmgrpwd.html:30 -#: application/templates/setting.html:151 -msgid "7 Days" +#: application/templates/admin.html:55 +msgid "AutoSend" msgstr "" -#: application/templates/admin.html:60 application/templates/admin.html:103 -#: application/templates/adminmgrpwd.html:31 -msgid "1 Month" +#: application/templates/admin.html:56 +#: application/templates/change_password.html:27 +#: application/templates/reset_password.html:26 +#: application/templates/signup.html:33 +#: application/templates/user_account.html:27 +msgid "Email" msgstr "" -#: application/templates/admin.html:61 application/templates/admin.html:105 -#: application/templates/adminmgrpwd.html:32 -msgid "3 Months" +#: application/templates/admin.html:57 +#: application/templates/user_account.html:39 +msgid "Expiration" msgstr "" -#: application/templates/admin.html:62 application/templates/admin.html:107 -#: application/templates/adminmgrpwd.html:33 -msgid "6 Months" +#: application/templates/admin.html:58 +msgid "Operation" msgstr "" -#: application/templates/admin.html:63 application/templates/admin.html:109 -#: application/templates/adminmgrpwd.html:34 -msgid "1 Year" +#: application/templates/admin.html:65 +msgid "Yes" msgstr "" -#: application/templates/admin.html:64 application/templates/admin.html:111 -#: application/templates/adminmgrpwd.html:35 -msgid "2 Years" +#: application/templates/admin.html:65 +msgid "No" msgstr "" #: application/templates/admin.html:68 -#: application/templates/adv_whitelist.html:29 application/templates/my.html:32 -msgid "Add" +#: application/templates/user_account.html:42 +msgid "Never expire" msgstr "" -#: application/templates/admin.html:74 -msgid "Accounts" +#: application/templates/admin.html:70 application/templates/setting.html:150 +#: application/templates/user_account.html:43 +msgid "7 Days" msgstr "" -#: application/templates/admin.html:85 -msgid "No." +#: application/templates/admin.html:72 +#: application/templates/user_account.html:44 +msgid "1 Month" msgstr "" -#: application/templates/admin.html:87 -msgid "AutoSend" +#: application/templates/admin.html:74 +#: application/templates/user_account.html:45 +msgid "3 Months" msgstr "" -#: application/templates/admin.html:89 -msgid "Operation" +#: application/templates/admin.html:76 +#: application/templates/user_account.html:46 +msgid "6 Months" msgstr "" -#: application/templates/admin.html:97 -msgid "Yes" +#: application/templates/admin.html:78 +#: application/templates/user_account.html:47 +msgid "1 Year" msgstr "" -#: application/templates/admin.html:97 -msgid "No" +#: application/templates/admin.html:80 +#: application/templates/user_account.html:48 +msgid "2 Years" msgstr "" -#: application/templates/admin.html:117 application/templates/admin.html:120 -msgid "Change" +#: application/templates/admin.html:85 +#: application/templates/change_password.html:3 +msgid "Change password" msgstr "" -#: application/templates/admin.html:118 application/templates/admin.html:121 +#: application/templates/admin.html:89 #: application/templates/adv_uploadcss.html:31 #: application/templates/adv_whitelist.html:22 -#: application/templates/base.html:22 application/templates/delaccount.html:20 +#: application/templates/base.html:22 msgid "Delete" msgstr "" -#: application/templates/adminmgrpwd.html:3 -msgid "Change password" -msgstr "" - #: application/templates/adv_archive.html:3 #: application/templates/adv_archive.html:13 #: application/templates/adv_base.html:59 @@ -175,12 +183,19 @@ msgstr "" msgid "Email or Username" msgstr "" +#: application/templates/adv_archive.html:55 application/templates/base.html:47 +#: application/templates/home.html:16 application/templates/login.html:25 +#: application/templates/setting.html:215 application/templates/signup.html:25 +#: application/templates/user_account.html:19 +msgid "Password" +msgstr "" + #: application/templates/adv_archive.html:58 application/templates/base.html:57 msgid "Verify" msgstr "" #: application/templates/adv_archive.html:93 -#: application/templates/setting.html:223 +#: application/templates/setting.html:230 msgid "Save settings" msgstr "" @@ -235,7 +250,7 @@ msgid "Deliver" msgstr "" #: application/templates/adv_import.html:3 -#: application/templates/adv_import.html:18 +#: application/templates/adv_import.html:19 msgid "Import" msgstr "" @@ -244,10 +259,10 @@ msgid "Import custom rss from an OPML file." msgstr "" #: application/templates/adv_import.html:15 -msgid "Import as fulltext RSS by default" +msgid "Import as fulltext rss by default" msgstr "" -#: application/templates/adv_import.html:19 +#: application/templates/adv_import.html:20 msgid "Download" msgstr "" @@ -307,17 +322,14 @@ msgid "Please input mail address" msgstr "" #: application/templates/autoback.html:3 -#: application/templates/tipsandback.html:3 msgid "Auto back" msgstr "" #: application/templates/autoback.html:28 -#: application/templates/tipsandback.html:13 msgid "Auto back to previous page after 5 seconds" msgstr "" #: application/templates/autoback.html:29 -#: application/templates/tipsandback.html:14 #: application/templates/tipsback.html:15 msgid "Click to back" msgstr "" @@ -362,7 +374,7 @@ msgstr "" msgid "Category" msgstr "" -#: application/templates/base.html:33 application/templates/setting.html:130 +#: application/templates/base.html:33 application/templates/setting.html:129 msgid "Language" msgstr "" @@ -409,7 +421,7 @@ msgid "The recipe is already subscribed." msgstr "" #: application/templates/base.html:45 -msgid "Subscription info" +msgid "Website login lnformation" msgstr "" #: application/templates/base.html:46 @@ -427,7 +439,7 @@ msgid "" msgstr "" #: application/templates/base.html:50 -msgid "Cannot set the subscription infomation, Error:" +msgid "Cannot set the website login information, Error:" msgstr "" #: application/templates/base.html:51 @@ -461,8 +473,8 @@ msgstr "" msgid "Verified" msgstr "" -#: application/templates/base.html:59 application/view/login.py:67 -#: application/view/share.py:154 +#: application/templates/base.html:59 application/view/login.py:73 +#: application/view/share.py:145 msgid "The username does not exist or password is wrong." msgstr "" @@ -499,7 +511,7 @@ msgid "[Uncategoried]" msgstr "" #: application/templates/base.html:68 -msgid "No links found in the chosen language." +msgid "There are no links found." msgstr "" #: application/templates/base.html:69 @@ -514,35 +526,35 @@ msgstr "" msgid "Customize delivery time" msgstr "" -#: application/templates/base.html:72 application/templates/setting.html:50 +#: application/templates/base.html:72 application/templates/setting.html:49 msgid "Delivery days" msgstr "" -#: application/templates/base.html:73 application/templates/setting.html:52 +#: application/templates/base.html:73 application/templates/setting.html:51 msgid "Mon" msgstr "" -#: application/templates/base.html:74 application/templates/setting.html:54 +#: application/templates/base.html:74 application/templates/setting.html:53 msgid "Tue" msgstr "" -#: application/templates/base.html:75 application/templates/setting.html:56 +#: application/templates/base.html:75 application/templates/setting.html:55 msgid "Wed" msgstr "" -#: application/templates/base.html:76 application/templates/setting.html:58 +#: application/templates/base.html:76 application/templates/setting.html:57 msgid "Thu" msgstr "" -#: application/templates/base.html:77 application/templates/setting.html:60 +#: application/templates/base.html:77 application/templates/setting.html:59 msgid "Fri" msgstr "" -#: application/templates/base.html:78 application/templates/setting.html:62 +#: application/templates/base.html:78 application/templates/setting.html:61 msgid "Sat" msgstr "" -#: application/templates/base.html:79 application/templates/setting.html:64 +#: application/templates/base.html:79 application/templates/setting.html:63 msgid "Sun" msgstr "" @@ -558,13 +570,14 @@ msgstr "" msgid "The account have been deleted." msgstr "" -#: application/templates/base.html:83 application/view/admin.py:46 -#: application/view/admin.py:66 +#: application/templates/base.html:83 application/view/admin.py:208 +#: application/view/share.py:135 msgid "The username or password is empty." msgstr "" -#: application/templates/base.html:84 application/view/admin.py:50 -#: application/view/admin.py:70 application/view/admin.py:143 +#: application/templates/base.html:84 application/view/admin.py:85 +#: application/view/admin.py:172 application/view/admin.py:212 +#: application/view/login.py:207 application/view/login.py:267 msgid "The two new passwords are dismatch." msgstr "" @@ -576,7 +589,7 @@ msgstr "" msgid "Account added successfully." msgstr "" -#: application/templates/base.html:87 application/view/login.py:106 +#: application/templates/base.html:87 application/view/login.py:117 msgid "login required" msgstr "" @@ -595,36 +608,61 @@ msgid "Logout" msgstr "" #: application/templates/base.html:113 application/templates/home.html:17 -#: application/templates/login.html:3 application/templates/login.html:37 +#: application/templates/login.html:3 application/templates/login.html:19 +#: application/templates/login.html:29 msgid "Login" msgstr "" -#: application/templates/base.html:133 application/templates/base.html:135 -#: application/templates/home.html:11 application/templates/my.html:3 +#: application/templates/base.html:115 application/templates/signup.html:3 +#: application/templates/signup.html:19 application/templates/signup.html:43 +msgid "Signup" +msgstr "" + +#: application/templates/base.html:135 application/templates/home.html:11 +#: application/templates/my.html:3 msgid "Feeds" msgstr "" -#: application/templates/base.html:138 application/templates/base.html:140 -#: application/templates/setting.html:3 +#: application/templates/base.html:136 application/templates/setting.html:3 msgid "Settings" msgstr "" -#: application/templates/base.html:143 application/templates/base.html:145 -#: application/templates/logs.html:3 +#: application/templates/base.html:137 application/templates/logs.html:3 msgid "Logs" msgstr "" -#: application/templates/base.html:153 application/templates/base.html:155 +#: application/templates/base.html:139 msgid "Advanced" msgstr "" -#: application/templates/base.html:158 application/templates/base.html:160 -#: application/templates/library.html:3 +#: application/templates/base.html:140 application/templates/library.html:3 msgid "Shared" msgstr "" -#: application/templates/delaccount.html:3 -msgid "Delete account" +#: application/templates/change_password.html:13 +msgid "Change Password" +msgstr "" + +#: application/templates/change_password.html:15 +msgid "Old password" +msgstr "" + +#: application/templates/change_password.html:19 +#: application/templates/reset_password.html:31 +#: application/templates/reset_password.html:32 +msgid "New password" +msgstr "" + +#: application/templates/change_password.html:23 +#: application/templates/reset_password.html:35 +#: application/templates/reset_password.html:36 +#: application/templates/signup.html:29 +#: application/templates/user_account.html:23 +msgid "Confirm password" +msgstr "" + +#: application/templates/change_password.html:32 +msgid "Confirm Change" msgstr "" #: application/templates/home.html:3 @@ -659,55 +697,55 @@ msgid "" "share the service with friends." msgstr "" -#: application/templates/library.html:48 application/templates/my.html:62 +#: application/templates/library.html:50 application/templates/my.html:62 msgid "Search" msgstr "" -#: application/templates/login.html:40 +#: application/templates/login.html:34 application/view/login.py:184 +#: application/view/login.py:191 msgid "" -"The website doesn't allow registration. You can ask the owner for an " +"The website does not allow registration. You can ask the owner for an " "account." msgstr "" -#: application/templates/logs.html:34 +#: application/templates/logs.html:10 msgid "Only display last 10 logs" msgstr "" -#: application/templates/logs.html:45 application/templates/logs.html:86 +#: application/templates/logs.html:21 application/templates/logs.html:62 msgid "Time" msgstr "" -#: application/templates/logs.html:46 application/templates/logs.html:87 -#: application/templates/my.html:17 application/templates/setting.html:99 -#: application/templates/setting.html:100 +#: application/templates/logs.html:22 application/templates/logs.html:63 +#: application/templates/my.html:17 application/templates/setting.html:98 +#: application/templates/setting.html:99 application/templates/setting.html:100 #: application/templates/setting.html:101 -#: application/templates/setting.html:102 -#: application/templates/setting.html:126 +#: application/templates/setting.html:125 msgid "Title" msgstr "" -#: application/templates/logs.html:47 application/templates/logs.html:88 +#: application/templates/logs.html:23 application/templates/logs.html:64 msgid "Size" msgstr "" -#: application/templates/logs.html:48 application/templates/logs.html:89 +#: application/templates/logs.html:24 application/templates/logs.html:65 msgid "To" msgstr "" -#: application/templates/logs.html:49 application/templates/logs.html:90 +#: application/templates/logs.html:25 application/templates/logs.html:66 msgid "Status" msgstr "" -#: application/templates/logs.html:69 +#: application/templates/logs.html:45 msgid "There is no log" msgstr "" -#: application/templates/logs.html:73 +#: application/templates/logs.html:49 msgid "Logs of other users" msgstr "" -#: application/templates/my.html:12 -msgid "Custom Rss" +#: application/templates/my.html:12 application/templates/setting.html:123 +msgid "Custom RSS" msgstr "" #: application/templates/my.html:23 @@ -742,220 +780,235 @@ msgstr "" msgid "Drag and drop this link to your bookmarks" msgstr "" -#: application/templates/setting.html:14 -msgid "Your account will pause after {0}, please log in again before it expires." +#: application/templates/reset_password.html:3 +#: application/templates/reset_password.html:41 +msgid "Reset password" msgstr "" -#: application/templates/setting.html:15 -msgid "Delete Account" +#: application/templates/setting.html:14 +msgid "Your account will pause after {0}, please log in again before it expires." msgstr "" -#: application/templates/setting.html:24 -msgid "Base Setting" +#: application/templates/setting.html:23 +msgid "Base" msgstr "" -#: application/templates/setting.html:26 +#: application/templates/setting.html:25 msgid "Auto Delivery" msgstr "" -#: application/templates/setting.html:28 +#: application/templates/setting.html:27 msgid "Recipes and custom RSS" msgstr "" -#: application/templates/setting.html:29 +#: application/templates/setting.html:28 msgid "Recipes only" msgstr "" -#: application/templates/setting.html:30 +#: application/templates/setting.html:29 msgid "Disable all" msgstr "" -#: application/templates/setting.html:34 +#: application/templates/setting.html:33 msgid "Kindle E-mail" msgstr "" -#: application/templates/setting.html:35 +#: application/templates/setting.html:34 msgid "Seperated by comma" msgstr "" -#: application/templates/setting.html:38 +#: application/templates/setting.html:37 msgid "Time zone" msgstr "" -#: application/templates/setting.html:67 +#: application/templates/setting.html:66 msgid "Delivery time" msgstr "" -#: application/templates/setting.html:75 +#: application/templates/setting.html:74 msgid "Book type" msgstr "" -#: application/templates/setting.html:82 +#: application/templates/setting.html:81 msgid "Device type" msgstr "" -#: application/templates/setting.html:92 +#: application/templates/setting.html:91 msgid "Others" msgstr "" -#: application/templates/setting.html:96 +#: application/templates/setting.html:95 msgid "Title format" msgstr "" -#: application/templates/setting.html:98 +#: application/templates/setting.html:97 msgid "Title Only" msgstr "" -#: application/templates/setting.html:106 -msgid "Book mode" -msgstr "" - -#: application/templates/setting.html:108 -msgid "Periodical" -msgstr "" - -#: application/templates/setting.html:109 -msgid "Comic" -msgstr "" - -#: application/templates/setting.html:113 +#: application/templates/setting.html:112 msgid "Remove hyperlinks" msgstr "" -#: application/templates/setting.html:115 +#: application/templates/setting.html:114 msgid "Do not remove hyperlinks" msgstr "" -#: application/templates/setting.html:116 +#: application/templates/setting.html:115 msgid "Remove image links" msgstr "" -#: application/templates/setting.html:117 +#: application/templates/setting.html:116 msgid "Remove text links" msgstr "" -#: application/templates/setting.html:118 +#: application/templates/setting.html:117 msgid "Remove all hyperlinks" msgstr "" -#: application/templates/setting.html:124 -msgid "Custom Rss Setting" -msgstr "" - -#: application/templates/setting.html:142 +#: application/templates/setting.html:141 msgid "Oldest article" msgstr "" -#: application/templates/setting.html:144 +#: application/templates/setting.html:143 msgid "No limit" msgstr "" -#: application/templates/setting.html:145 +#: application/templates/setting.html:144 msgid "1 Day" msgstr "" -#: application/templates/setting.html:146 +#: application/templates/setting.html:145 msgid "2 Days" msgstr "" -#: application/templates/setting.html:147 +#: application/templates/setting.html:146 msgid "3 Days" msgstr "" -#: application/templates/setting.html:148 +#: application/templates/setting.html:147 msgid "4 Days" msgstr "" -#: application/templates/setting.html:149 +#: application/templates/setting.html:148 msgid "5 Days" msgstr "" -#: application/templates/setting.html:150 +#: application/templates/setting.html:149 msgid "6 Days" msgstr "" -#: application/templates/setting.html:155 +#: application/templates/setting.html:154 msgid "Time format" msgstr "" -#: application/templates/setting.html:167 +#: application/templates/setting.html:166 msgid "Author format" msgstr "" #: application/templates/setting.html:182 -msgid "Send mail service" +msgid "Send Mail Service" msgstr "" #: application/templates/setting.html:184 msgid "Service" msgstr "" -#: application/templates/setting.html:193 +#: application/templates/setting.html:199 msgid "ApiKey" msgstr "" -#: application/templates/setting.html:197 +#: application/templates/setting.html:203 msgid "Host" msgstr "" -#: application/templates/setting.html:201 +#: application/templates/setting.html:207 msgid "Port" msgstr "" -#: application/templates/setting.html:213 +#: application/templates/setting.html:219 msgid "Save path" msgstr "" -#: application/templates/setting.html:219 +#: application/templates/setting.html:226 #, python-format msgid "" "Important: Please activate your kindle firstly, then goto %(personal)s " "Page and add %(sender)s to 'Approved Personal Document E-mail List'." msgstr "" -#: application/templates/setting.html:219 +#: application/templates/setting.html:226 msgid "Personal Document Settings" msgstr "" -#: application/view/admin.py:43 application/view/admin.py:78 -#: application/view/admin.py:149 -msgid "The password includes non-ascii chars." +#: application/templates/signup.html:38 +msgid "Invitation code" msgstr "" -#: application/view/admin.py:48 -msgid "The old password is wrong." +#: application/templates/user_account.html:3 +msgid "User account" msgstr "" -#: application/view/admin.py:64 +#: application/view/admin.py:50 application/view/setting.py:96 +msgid "Settings Saved!" +msgstr "" + +#: application/view/admin.py:60 application/view/admin.py:68 +#: application/view/admin.py:103 +msgid "Add account" +msgstr "" + +#: application/view/admin.py:67 application/view/admin.py:116 +#: application/view/admin.py:144 msgid "You do not have sufficient privileges." msgstr "" -#: application/view/admin.py:68 application/view/login.py:40 +#: application/view/admin.py:81 application/view/admin.py:159 +#: application/view/login.py:203 application/view/login.py:232 +#: application/view/setting.py:61 application/view/setting.py:63 +#: application/view/setting.py:65 application/view/share.py:35 +msgid "Some parameters are missing or wrong." +msgstr "" + +#: application/view/admin.py:83 application/view/login.py:42 +#: application/view/login.py:209 msgid "The username includes unsafe chars." msgstr "" -#: application/view/admin.py:72 +#: application/view/admin.py:87 application/view/login.py:211 msgid "Already exist the username." msgstr "" -#: application/view/admin.py:96 application/view/admin.py:117 -#: application/view/admin.py:141 +#: application/view/admin.py:93 application/view/admin.py:178 +#: application/view/admin.py:205 application/view/login.py:258 +msgid "The password includes non-ascii chars." +msgstr "" + +#: application/view/admin.py:120 application/view/admin.py:141 +#: application/view/admin.py:170 msgid "The username '{}' does not exist." msgstr "" -#: application/view/admin.py:102 -msgid "The username is empty or you dont have right to delete it." +#: application/view/admin.py:136 +msgid "The password will not be changed if the fields are empties." msgstr "" -#: application/view/admin.py:119 -msgid "Please input new password to confirm." +#: application/view/admin.py:137 application/view/admin.py:195 +msgid "Change account" msgstr "" -#: application/view/admin.py:132 application/view/login.py:36 -msgid "Username is empty." +#: application/view/admin.py:138 application/view/admin.py:196 +msgid "Change" +msgstr "" + +#: application/view/admin.py:193 +msgid "Change success." +msgstr "" + +#: application/view/admin.py:210 +msgid "The old password is wrong." msgstr "" -#: application/view/admin.py:159 +#: application/view/admin.py:217 msgid "Change password success." msgstr "" @@ -1014,29 +1067,30 @@ msgstr "" msgid "Open in browser" msgstr "" -#: application/view/adv.py:351 +#: application/view/adv.py:352 msgid "Authorization Error!
{}" msgstr "" -#: application/view/adv.py:374 +#: application/view/adv.py:375 msgid "Success authorized by Pocket!" msgstr "" -#: application/view/adv.py:380 +#: application/view/adv.py:381 msgid "" "Failed to request authorization of Pocket!
See details " "below:

{}" msgstr "" -#: application/view/adv.py:390 +#: application/view/adv.py:391 msgid "Request type [{}] unsupported" msgstr "" -#: application/view/adv.py:405 +#: application/view/adv.py:406 msgid "The Instapaper service encountered an error. Please try again later." msgstr "" -#: application/view/deliver.py:68 +#: application/view/deliver.py:68 application/view/login.py:154 +#: application/view/share.py:39 msgid "The username does not exist or the email is empty." msgstr "" @@ -1053,174 +1107,226 @@ msgid "Cannot fetch data from {}, status: {}" msgstr "" #: application/view/library.py:48 application/view/subscribe.py:192 -#: application/view/subscribe.py:301 application/view/subscribe.py:330 -#: application/view/subscribe.py:338 +#: application/view/subscribe.py:304 application/view/subscribe.py:333 +#: application/view/subscribe.py:341 msgid "The recipe does not exist." msgstr "" -#: application/view/login.py:20 -msgid "Please input username and password." -msgstr "" - -#: application/view/login.py:22 +#: application/view/login.py:25 msgid "Please use {}/{} to login at first time." msgstr "" #: application/view/login.py:38 +msgid "Username is empty." +msgstr "" + +#: application/view/login.py:40 msgid "The len of username reached the limit of 25 chars." msgstr "" -#: application/view/login.py:70 application/view/login.py:72 +#: application/view/login.py:74 msgid "Forgot password?" msgstr "" -#: application/view/setting.py:19 +#: application/view/login.py:133 application/view/login.py:269 +msgid "The token is wrong or expired." +msgstr "" + +#: application/view/login.py:136 +msgid "Please input the correct username and email to reset password." +msgstr "" + +#: application/view/login.py:138 +msgid "The email of account '{name}' is {email}." +msgstr "" + +#: application/view/login.py:159 +msgid "Reset password success, Please close this page and login again." +msgstr "" + +#: application/view/login.py:162 +msgid "The email you input is not associated with this account." +msgstr "" + +#: application/view/login.py:173 +msgid "The link to reset your password has been sent to your email." +msgstr "" + +#: application/view/login.py:174 +msgid "Please check your email inbox within 24 hours." +msgstr "" + +#: application/view/login.py:205 +msgid "The invitation code is invalid." +msgstr "" + +#: application/view/login.py:213 +msgid "" +"Failed to create an account. Please contact the administrator for " +"assistance." +msgstr "" + +#: application/view/login.py:223 +msgid "Successfully created account." +msgstr "" + +#: application/view/login.py:234 +msgid "Reset KindleEar password" +msgstr "" + +#: application/view/login.py:235 +msgid "This is an automated email. Please do not reply to it." +msgstr "" + +#: application/view/login.py:236 +msgid "You can click the following link to reset your KindleEar password." +msgstr "" + +#: application/view/setting.py:57 +msgid "Kindle E-mail is requied!" +msgstr "" + +#: application/view/setting.py:59 +msgid "Title is requied!" +msgstr "" + +#: application/view/setting.py:124 msgid "Chinese" msgstr "" -#: application/view/setting.py:20 +#: application/view/setting.py:125 msgid "English" msgstr "" -#: application/view/setting.py:21 +#: application/view/setting.py:126 msgid "French" msgstr "" -#: application/view/setting.py:22 +#: application/view/setting.py:127 msgid "Spanish" msgstr "" -#: application/view/setting.py:23 +#: application/view/setting.py:128 msgid "Portuguese" msgstr "" -#: application/view/setting.py:24 +#: application/view/setting.py:129 msgid "German" msgstr "" -#: application/view/setting.py:25 +#: application/view/setting.py:130 msgid "Italian" msgstr "" -#: application/view/setting.py:26 +#: application/view/setting.py:131 msgid "Japanese" msgstr "" -#: application/view/setting.py:27 +#: application/view/setting.py:132 msgid "Russian" msgstr "" -#: application/view/setting.py:28 +#: application/view/setting.py:133 msgid "Turkish" msgstr "" -#: application/view/setting.py:29 +#: application/view/setting.py:134 msgid "Korean" msgstr "" -#: application/view/setting.py:30 +#: application/view/setting.py:135 msgid "Arabic" msgstr "" -#: application/view/setting.py:31 +#: application/view/setting.py:136 msgid "Czech" msgstr "" -#: application/view/setting.py:32 +#: application/view/setting.py:137 msgid "Dutch" msgstr "" -#: application/view/setting.py:33 +#: application/view/setting.py:138 msgid "Greek" msgstr "" -#: application/view/setting.py:34 +#: application/view/setting.py:139 msgid "Hindi" msgstr "" -#: application/view/setting.py:35 +#: application/view/setting.py:140 msgid "Malaysian" msgstr "" -#: application/view/setting.py:36 +#: application/view/setting.py:141 msgid "Bengali" msgstr "" -#: application/view/setting.py:37 +#: application/view/setting.py:142 msgid "Persian" msgstr "" -#: application/view/setting.py:38 +#: application/view/setting.py:143 msgid "Urdu" msgstr "" -#: application/view/setting.py:39 +#: application/view/setting.py:144 msgid "Swahili" msgstr "" -#: application/view/setting.py:40 +#: application/view/setting.py:145 msgid "Vietnamese" msgstr "" -#: application/view/setting.py:41 +#: application/view/setting.py:146 msgid "Punjabi" msgstr "" -#: application/view/setting.py:42 +#: application/view/setting.py:147 msgid "Javanese" msgstr "" -#: application/view/setting.py:43 +#: application/view/setting.py:148 msgid "Tagalog" msgstr "" -#: application/view/setting.py:44 +#: application/view/setting.py:149 msgid "Hausa" msgstr "" -#: application/view/setting.py:78 -msgid "Kindle E-mail is requied!" -msgstr "" - -#: application/view/setting.py:80 -msgid "Title is requied!" -msgstr "" - -#: application/view/setting.py:82 application/view/setting.py:84 -#: application/view/setting.py:86 -msgid "Some parameters are missing or wrong." -msgstr "" - -#: application/view/setting.py:116 -msgid "Settings Saved!" +#: application/view/share.py:50 application/view/subscribe.py:240 +msgid "Unknown command: {}" msgstr "" -#: application/view/share.py:103 -msgid "'{title}'

Saved to {act} [{email}] success." +#: application/view/share.py:56 +msgid "There is no {} email yet." msgstr "" -#: application/view/share.py:125 -msgid "Failed save to Pocket.
" +#: application/view/share.py:96 application/view/share.py:121 +#: application/view/share.py:143 +msgid "Saved to your {} account." msgstr "" -#: application/view/share.py:127 -msgid "'{}'

Saved to your Pocket account." +#: application/view/share.py:99 application/view/share.py:117 +#: application/view/share.py:146 +msgid "Failed save to {}." msgstr "" -#: application/view/share.py:130 -msgid "See details below:

{}" +#: application/view/share.py:100 application/view/share.py:118 +#: application/view/share.py:147 +msgid "Reason :" msgstr "" -#: application/view/share.py:150 -msgid "'{}'

Saved to your Instapaper account." +#: application/view/share.py:109 +msgid "Unauthorized {} account!" msgstr "" -#: application/view/share.py:154 application/view/share.py:158 -msgid "Failed save to Instapaper
'{}'

Reason :" +#: application/view/share.py:122 +msgid "See details below:" msgstr "" -#: application/view/share.py:158 -msgid "Unknown({})" +#: application/view/share.py:145 +msgid "Unknown: {}" msgstr "" #: application/view/subscribe.py:55 @@ -1243,7 +1349,7 @@ msgstr "" msgid "Failed to fetch the recipe." msgstr "" -#: application/view/subscribe.py:123 application/view/subscribe.py:262 +#: application/view/subscribe.py:123 application/view/subscribe.py:265 msgid "Failed to save the recipe. Error:" msgstr "" @@ -1251,33 +1357,33 @@ msgstr "" msgid "You can only delete the uploaded recipe." msgstr "" -#: application/view/subscribe.py:235 -msgid "This recipe has not been subscribed to yet." +#: application/view/subscribe.py:225 +msgid "The recipe have been subscribed, please unsubscribe it before delete." msgstr "" -#: application/view/subscribe.py:237 -msgid "Unknown command: {}" +#: application/view/subscribe.py:238 +msgid "This recipe has not been subscribed to yet." msgstr "" -#: application/view/subscribe.py:249 +#: application/view/subscribe.py:252 msgid "Can not read uploaded file, Error:" msgstr "" -#: application/view/subscribe.py:257 +#: application/view/subscribe.py:260 msgid "" "Failed to decode the recipe. Please ensure that your recipe is saved in " "utf-8 encoding." msgstr "" -#: application/view/subscribe.py:277 -msgid "The recipe is already in the library" +#: application/view/subscribe.py:280 +msgid "The recipe is already in the library." msgstr "" -#: application/view/subscribe.py:308 -msgid "The login information for this recipe has been cleared" +#: application/view/subscribe.py:311 +msgid "The login information for this recipe has been cleared." msgstr "" -#: application/view/subscribe.py:312 -msgid "The login information for this recipe has been saved" +#: application/view/subscribe.py:315 +msgid "The login information for this recipe has been saved." msgstr "" diff --git a/requirements.txt b/requirements.txt index c7ccd0b3..c6ce5123 100644 --- a/requirements.txt +++ b/requirements.txt @@ -30,5 +30,6 @@ google-cloud-tasks==2.15.0 celery==5.3.6 redis==5.0.1 eventlet==0.35.1 +#flask-rq2==18.3 appengine-python-standard==1.1.6 diff --git a/tests/runtests.py b/tests/runtests.py index 7da02075..12e799b0 100644 --- a/tests/runtests.py +++ b/tests/runtests.py @@ -8,7 +8,7 @@ sys.path.insert(0, os.path.join(appDir, 'application', 'lib')) log = logging.getLogger() -log.setLevel(logging.DEBUG) +log.setLevel(logging.INFO) builtins.__dict__['default_log'] = log builtins.__dict__['appDir'] = appDir @@ -31,8 +31,8 @@ def set_env(): os.environ['DATABASE_PASSWORD'] = '' os.environ['TASK_QUEUE_SERVICE'] = TASK_QUEUE_SERVICE - os.environ['CELERY_BROKER_URL'] = CELERY_BROKER_URL - os.environ['CELERY_RESULT_BACKEND'] = CELERY_RESULT_BACKEND + os.environ['TASK_QUEUE_BROKER_URL'] = TASK_QUEUE_BROKER_URL + os.environ['TASK_QUEUE_RESULT_BACKEND'] = TASK_QUEUE_RESULT_BACKEND os.environ['KE_DOMAIN'] = KE_DOMAIN os.environ['SRC_EMAIL'] = SRC_EMAIL os.environ['ADMIN_NAME'] = ADMIN_NAME @@ -41,11 +41,11 @@ def set_env(): if 1: TEST_MODULES = ['test_login', 'test_setting', 'test_admin', 'test_subscribe', 'test_adv', - 'test_share', 'test_logs'] + 'test_logs'] #'test_share', if INBOUND_EMAIL_SERVICE == 'gae': TEST_MODULES.append('test_inbound_email') else: - TEST_MODULES = ['test_library_official'] + TEST_MODULES = ['test_login'] def runtests(suite, verbosity=1, failfast=False): runner = unittest.TextTestRunner(verbosity=verbosity, failfast=failfast) @@ -73,7 +73,7 @@ def reload_module(module_name): return module def main(): - verbosity = 4 #Verbosity of output, 0 | 1 | 4 + verbosity = 1 #Verbosity of output, 0 | 1 | 4 failfast = 0 #Exit on first failure/error report = '' # '' | 'html' | 'console' diff --git a/tests/test_admin.py b/tests/test_admin.py index d8c94d5c..ac14334c 100644 --- a/tests/test_admin.py +++ b/tests/test_admin.py @@ -8,102 +8,125 @@ class AdminTestCase(BaseTestCase): def test_admin_page(self): resp = self.client.get('/admin') self.assertEqual(resp.status_code, 200) - data = resp.text - self.assertTrue(('Change Password' in data) and ('Add Account' in data)) + text = resp.text + self.assertTrue(('Accounts' in text) and ('Add' in text)) - def test_change_admin_password_fail(self): - resp = self.client.post('/admin', data={'actType': 'change', 'op': None, 'p1': '1', 'p2': '1'}) + def test_add_account_and_delete(self): + resp = self.client.get('/account/add') self.assertEqual(resp.status_code, 200) - self.assertEqual(resp.json, {'status': 'The password includes non-ascii chars.'}) + self.assertIn('Add account', resp.text) - resp = self.client.post('/admin', data={'actType': 'change', 'op': 'admin', 'p1': 'admin', 'p2': ''}) + data = {'username': '', 'password1': '1', 'password2': '2', 'email': '1@1.com', + 'sm_service': 'admin', 'expiration': 0} + resp = self.client.post('/account/add', data=data) self.assertEqual(resp.status_code, 200) - self.assertEqual(resp.json, {'status': 'The username or password is empty.'}) - - resp = self.client.post('/admin', data={'actType': 'change', 'op': 'ADMIN', 'p1': 'admin', 'p2': 'admin'}) + self.assertIn('Some parameters are missing or wrong.', resp.text) + + data['username'] = '/1>' + resp = self.client.post('/account/add', data=data) self.assertEqual(resp.status_code, 200) - self.assertEqual(resp.json, {'status': 'The old password is wrong.'}) + self.assertIn('The username includes unsafe chars.', resp.text) - resp = self.client.post('/admin', data={'actType': 'change', 'op': 'admin', 'p1': '1', 'p2': '2'}) + resp = self.client.post('/account/add', data=data) self.assertEqual(resp.status_code, 200) - self.assertEqual(resp.json, {'status': 'The two new passwords are dismatch.'}) + self.assertIn('The two new passwords are dismatch.', resp.text) - def test_change_admin_password_success(self): - resp = self.client.post('/admin', data={'actType': 'change', 'op': 'admin', 'p1': '1', 'p2': '1'}) + data['username'] = 'admin' + data['password2'] = '1' + resp = self.client.post('/account/add', data=data) self.assertEqual(resp.status_code, 200) - self.assertEqual(resp.json, {'status': 'ok'}) - - resp = self.client.post('/admin', data={'actType': 'change', 'op': '1', 'p1': 'admin', 'p2': 'admin'}) - self.assertEqual(resp.status_code, 200) - self.assertEqual(resp.json, {'status': 'ok'}) + self.assertIn('Already exist the username.', resp.text) - def test_add_account_and_delete(self): - resp = self.client.post('/admin', data={'actType': 'add', 'new_username': '', 'new_u_pwd1': '1', - 'new_u_pwd2': '1', 'new_u_expiration': 0}) - self.assertEqual(resp.status_code, 200) - self.assertEqual(resp.json, {'status': 'The username or password is empty.'}) - - resp = self.client.post('/admin', data={'actType': 'add', 'new_username': '/1>', 'new_u_pwd1': 'admin', - 'new_u_pwd2': 'admin', 'new_u_expiration': 0}) + data['username'] = '1' + resp = self.client.post('/account/add', data=data) + self.assertEqual(resp.status_code, 302) + user = KeUser.get_or_none(KeUser.name == '1') + self.assertEqual(user.email, '1@1.com') + + resp = self.client.post('/account/delete', data={'name': 'admin'}) self.assertEqual(resp.status_code, 200) - self.assertEqual(resp.json, {'status': 'The username includes unsafe chars.'}) + self.assertEqual(resp.json, {'status': 'You do not have sufficient privileges.'}) - resp = self.client.post('/admin', data={'actType': 'add', 'new_username': '1', 'new_u_pwd1': '2', - 'new_u_pwd2': '3', 'new_u_expiration': 0}) + resp = self.client.post('/account/delete', data={'name': '2'}) self.assertEqual(resp.status_code, 200) - self.assertEqual(resp.json, {'status': 'The two new passwords are dismatch.'}) + self.assertEqual(resp.json, {'status': "The username '2' does not exist."}) - resp = self.client.post('/admin', data={'actType': 'add', 'new_username': '1', 'new_u_pwd1': '2', - 'new_u_pwd2': '2', 'new_u_expiration': 10}) + resp = self.client.post('/account/delete', data={'name': '1'}) self.assertEqual(resp.status_code, 200) self.assertEqual(resp.json, {'status': 'ok'}) + + def test_change_admin_password(self): + resp = self.client.get('/account/change/admin') + self.assertEqual(resp.status_code, 200) + self.assertIn("Change Password", resp.text) - resp = self.client.post('/admin', data={'actType': 'add', 'new_username': '1', 'new_u_pwd1': '3', - 'new_u_pwd2': '3', 'new_u_expiration': 0}) + data = {'username': '', 'orgpwd': '', 'password1': '1', 'password2': '2', 'email': '1@1'} + resp = self.client.post('/account/change/admin', data=data) self.assertEqual(resp.status_code, 200) - self.assertEqual(resp.json, {'status': 'Already exist the username.'}) + self.assertIn("The username or password is empty.", resp.text) - resp = self.client.post('/admin', data={'actType': 'delete', 'name': '1'}) + data['username'] = 'admin' + data['orgpwd'] = 'admin1' + resp = self.client.post('/account/change/admin', data=data) self.assertEqual(resp.status_code, 200) - self.assertEqual(resp.json, {'status': "ok"}) + self.assertIn("The old password is wrong.", resp.text) - def test_del_account_fail(self): - resp = self.client.post('/admin', data={'actType': 'delete', 'name': ''}) + data['orgpwd'] = 'admin' + resp = self.client.post('/account/change/admin', data=data) self.assertEqual(resp.status_code, 200) - self.assertEqual(resp.json, {'status': 'The username is empty or you dont have right to delete it.'}) + self.assertIn("The two new passwords are dismatch.", resp.text) - resp = self.client.post('/admin', data={'actType': 'delete', 'name': 'notexist'}) + data['password2'] = '1' + resp = self.client.post('/account/change/admin', data=data) self.assertEqual(resp.status_code, 200) - self.assertEqual(resp.json, {'status': "The username 'notexist' does not exist."}) + self.assertIn("Change password success.", resp.text) + + data['orgpwd'] = '1' + data['password1'] = 'admin' + data['password2'] = 'admin' + resp = self.client.post('/account/change/admin', data=data) + self.assertEqual(resp.status_code, 200) + self.assertIn("Change password success.", resp.text) - def test_change_user_password(self): - resp = self.client.post('/admin', data={'actType': 'add', 'new_username': '1', 'new_u_pwd1': '2', - 'new_u_pwd2': '2', 'new_u_expiration': 0}) - self.assertEqual(resp.status_code, 200) - self.assertEqual(resp.json, {'status': 'ok'}) + data = {'username': '2', 'password1': '2', 'password2': '2', 'email': '1@1.com', + 'sm_service': 'admin', 'expiration': 12} + resp = self.client.post('/account/add', data=data) + self.assertEqual(resp.status_code, 302) - resp = self.client.get('/mgrpwd/notexist') + resp = self.client.get('/account/change/2') self.assertEqual(resp.status_code, 200) - self.assertIn("The username 'notexist' does not exist.", resp.text) + self.assertIn("The password will not be changed if the fields are empties.", resp.text) - resp = self.client.get('/mgrpwd/1') + resp = self.client.get('/account/change/4') self.assertEqual(resp.status_code, 200) - self.assertIn("Please input new password to confirm.", resp.text) + self.assertIn("The username '4' does not exist.", resp.text) + + data['password1'] = '1' + data['password2'] = '1' + resp = self.client.post('/account/change/2', data=data) + self.assertEqual(resp.status_code, 200) + self.assertIn("Change success.", resp.text) - resp = self.client.post('/mgrpwd/10', data={'p1': '1', 'p2': '1', 'ep': '200'}) + data['username'] = '3' + resp = self.client.post('/account/change/3', data=data) self.assertEqual(resp.status_code, 200) - self.assertIn("The username '10' does not exist.", resp.text) + self.assertIn("The username '3' does not exist.", resp.text) - resp = self.client.post('/mgrpwd/1', data={'p1': '1', 'p2': '12', 'ep': '200'}) + data['username'] = '2' + data['password1'] = '1' + resp = self.client.post('/account/change/2', data=data) self.assertEqual(resp.status_code, 200) self.assertIn("The two new passwords are dismatch.", resp.text) - - resp = self.client.post('/mgrpwd/1', data={'p1': '12', 'p2': '12', 'ep': '200'}) + + data['password1'] = '2' + data['password2'] = '2' + data['expiration'] = 100 + resp = self.client.post('/account/change/2', data=data) self.assertEqual(resp.status_code, 200) - self.assertIn("Change password success.", resp.text) + self.assertIn("Change success.", resp.text) - resp = self.client.post('/mgrpwd/1', data={'p1': '12', 'p2': '12', 'ep': '0'}) + data['sm_service'] = 'independent' + resp = self.client.post('/account/change/2', data=data) self.assertEqual(resp.status_code, 200) - self.assertIn("Change password success.", resp.text) - + self.assertIn("Change success.", resp.text) diff --git a/tests/test_base.py b/tests/test_base.py index 00d54f11..1a15b244 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -25,9 +25,9 @@ def setUp(self): self.client = app.test_client() self.runner = app.test_cli_runner() if self.login_required: - self.client.post('/login', data={'u': self.login_required, 'p': self.login_required}) + self.client.post('/login', data={'username': self.login_required, 'password': self.login_required}) self.temp_files = [] - + def tearDown(self): if self.login_required: self.client.post('/logout') diff --git a/tests/test_library_official.py b/tests/test_library_official.py index 35f970bf..2336a501 100644 --- a/tests/test_library_official.py +++ b/tests/test_library_official.py @@ -14,8 +14,8 @@ def test_shared(self): self.assertEqual(resp.status_code, 200) self.assertEqual(resp.json, {'status': 'ok', 'data': 0}) - now = datetime.datetime.utcnow() - AppInfo.create(name='lastSharedRssTime', time_value=now) + now = str(datetime.datetime.utcnow().timestamp()) + AppInfo.create(name='lastSharedRssTime', value=now) resp = self.client.get('/kindleearappspotlibrary', query_string=query) self.assertEqual(resp.status_code, 200) self.assertEqual(resp.json, {'status': 'ok', 'data': int(now.timestamp())}) diff --git a/tests/test_login.py b/tests/test_login.py index 9850ade2..c2e9c69c 100644 --- a/tests/test_login.py +++ b/tests/test_login.py @@ -11,24 +11,24 @@ def test_login_page(self): ('Please input username and password.' in data)) def test_login_wrong_parms(self): - resp = self.client.post('/login', data={'u': '', 'p': 'password'}) + resp = self.client.post('/login', data={'username': '', 'password': 'password'}) self.assertEqual(resp.status_code, 200) self.assertIn('Username is empty.', resp.text) - resp = self.client.post('/login', data={'u': '1111111111111111111111111111111111111111111111111', 'p': 'password'}) + resp = self.client.post('/login', data={'username': '1111111111111111111111111111111111111111111111111', 'password': 'password'}) self.assertEqual(resp.status_code, 200) self.assertIn('The len of username reached the limit of 25 chars.', resp.text) - resp = self.client.post('/login', data={'u': '', 'p': 'password'}) + resp = self.client.post('/login', data={'username': '', 'password': 'password'}) self.assertEqual(resp.status_code, 200) self.assertIn('The username includes unsafe chars.', resp.text) - resp = self.client.post('/login', data={'u': 'admin', 'p': 'password'}) + resp = self.client.post('/login', data={'username': 'admin', 'password': 'password'}) self.assertEqual(resp.status_code, 200) self.assertIn('The username does not exist or password is wrong.', resp.text) def test_login_success(self): - resp = self.client.post('/login', data={'u': 'admin', 'p': 'admin'}) + resp = self.client.post('/login', data={'username': 'admin', 'password': 'admin'}) self.assertEqual(resp.status_code, 302) resp = self.client.post('/logout') diff --git a/tests/test_logs.py b/tests/test_logs.py index 5d8d5415..486e33b6 100644 --- a/tests/test_logs.py +++ b/tests/test_logs.py @@ -9,7 +9,8 @@ class LogsTestCase(BaseTestCase): def test_logs(self): resp = self.client.get('/logs') self.assertEqual(resp.status_code, 200) - self.assertIn('There is no log', resp.text) + text = resp.text + self.assertTrue(('There is no log' in text) or ('Only display last 10 logs' in text)) data = {'user': 'admin', 'to': 'test@test.com', 'size': 1024, 'time_str': '2024-01-01', 'datetime': datetime.datetime.utcnow(), 'book': 'test', 'status': 'ok'} @@ -23,7 +24,7 @@ def test_logs(self): data['user'] = 'other' DeliverLog.create(**data) - KeUser.create(name='other', passwd='pwd') + KeUser.create(name='other', passwd='pwd', email='1@2') resp = self.client.get('/logs') self.assertEqual(resp.status_code, 200) self.assertIn('Logs of other users', resp.text) @@ -34,7 +35,7 @@ def test_remove_logs(self): self.assertEqual(resp.status_code, 200) self.assertIn('lines delivery log removed.', resp.text) - KeUser.create(name='other', passwd='pwd', enable_send=True, expiration_days=7, + KeUser.create(name='other', passwd='pwd', email='1@1', enable_send=True, expiration_days=7, expires=datetime.datetime.utcnow() - datetime.timedelta(days=30)) resp = self.client.get('/removelogs') self.assertEqual(resp.status_code, 200) diff --git a/tests/test_setting.py b/tests/test_setting.py index 47abfa21..bfb2ccd5 100644 --- a/tests/test_setting.py +++ b/tests/test_setting.py @@ -11,7 +11,7 @@ def test_setting_page(self): resp = self.client.get('/setting') self.assertEqual(resp.status_code, 200) data = resp.text - self.assertTrue(('Base Setting' in data) and ('Oldest article' in data)) + self.assertTrue(('Base' in data) and ('Oldest article' in data)) def test_set_post(self): data = {'kindle_email': '', 'rss_title': '', 'sm_service': 'sendgrid', 'sm_apikey': '', 'sm_host': '', diff --git a/tests/test_subscribe.py b/tests/test_subscribe.py index cabe980a..529788cc 100644 --- a/tests/test_subscribe.py +++ b/tests/test_subscribe.py @@ -12,7 +12,7 @@ def test_my_page(self): resp = self.client.get('/my') self.assertEqual(resp.status_code, 200) data = resp.text - self.assertTrue(('Custom Rss' in data) and ('Subscribed' in data)) + self.assertTrue(('Custom RSS' in data) and ('Subscribed' in data)) def test_web_custom_rss(self): resp = self.client.post('/my', data={'rss_title': 'bbc', 'url': '', 'fulltext': False})

{{u.expiration_days}} Days - {% if u.name == admin_name -%} - {{_("Change")}} - {{_("Delete")}} + + {% if u.name == adminName -%} + {% else -%} - {{_("Change")}} - {{_("Delete")}} + {% endif -%}