浏览代码

Merge branch 'develop' into encrypt-otp-secrets

version-14
Sagar Vora 2 年前
committed by GitHub
父节点
当前提交
d6f3e2935f
找不到此签名对应的密钥 GPG 密钥 ID: 4AEE18F83AFDEB23
共有 63 个文件被更改,包括 552 次插入529 次删除
  1. +1
    -1
      .editorconfig
  2. +1
    -1
      .github/helper/roulette.py
  3. +11
    -9
      .github/workflows/patch-mariadb-tests.yml
  4. +12
    -2
      frappe/core/doctype/comment/comment.json
  5. +2
    -1
      frappe/core/doctype/communication/mixins.py
  6. +9
    -3
      frappe/core/doctype/doctype/doctype.js
  7. +3
    -4
      frappe/core/doctype/doctype/doctype.py
  8. +0
    -8
      frappe/core/doctype/feedback/feedback.js
  9. +0
    -73
      frappe/core/doctype/feedback/feedback.json
  10. +0
    -9
      frappe/core/doctype/feedback/feedback.py
  11. +0
    -42
      frappe/core/doctype/feedback/test_feedback.py
  12. +1
    -1
      frappe/core/doctype/sms_settings/sms_settings.py
  13. +1
    -1
      frappe/core/doctype/system_settings/system_settings.py
  14. +3
    -3
      frappe/core/doctype/user/user.py
  15. +5
    -6
      frappe/core/doctype/version/version_view.html
  16. +2
    -4
      frappe/hooks.py
  17. +3
    -3
      frappe/integrations/doctype/dropbox_settings/dropbox_settings.py
  18. +1
    -1
      frappe/integrations/doctype/google_drive/google_drive.py
  19. +3
    -1
      frappe/integrations/doctype/oauth_provider_settings/oauth_provider_settings.py
  20. +2
    -2
      frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py
  21. +19
    -2
      frappe/model/naming.py
  22. +1
    -0
      frappe/patches.txt
  23. +1
    -1
      frappe/patches/v11_0/set_dropbox_file_backup.py
  24. +30
    -0
      frappe/patches/v14_0/setup_likes_from_feedback.py
  25. +2
    -2
      frappe/public/js/frappe/form/controls/datepicker_i18n.js
  26. +2
    -1
      frappe/public/js/frappe/form/dashboard.js
  27. +1
    -1
      frappe/public/js/frappe/form/formatters.js
  28. +0
    -15
      frappe/public/js/frappe/list/base_list.js
  29. +1
    -4
      frappe/public/js/frappe/list/list_view.js
  30. +1
    -1
      frappe/public/js/frappe/utils/diffview.js
  31. +38
    -2
      frappe/public/js/frappe/utils/utils.js
  32. +4
    -3
      frappe/public/js/frappe/views/reports/query_report.js
  33. +2
    -1
      frappe/public/js/frappe/views/reports/report_utils.js
  34. +2
    -1
      frappe/public/js/frappe/views/reports/report_view.js
  35. +5
    -1
      frappe/public/scss/common/css_variables.scss
  36. +5
    -1
      frappe/public/scss/desk/dark.scss
  37. +8
    -12
      frappe/public/scss/desk/global.scss
  38. +1
    -1
      frappe/templates/includes/comments/comment.html
  39. +3
    -4
      frappe/templates/includes/comments/comments.html
  40. +8
    -7
      frappe/templates/includes/comments/comments.py
  41. +0
    -0
      frappe/templates/includes/feedback/__init__.py
  42. +0
    -55
      frappe/templates/includes/feedback/feedback.py
  43. +0
    -0
      frappe/templates/includes/likes/__init__.py
  44. +11
    -13
      frappe/templates/includes/likes/likes.html
  45. +77
    -0
      frappe/templates/includes/likes/likes.py
  46. +1
    -1
      frappe/templates/pages/integrations/razorpay_checkout.py
  47. +27
    -0
      frappe/tests/test_naming.py
  48. +138
    -153
      frappe/translations/ru.csv
  49. +9
    -13
      frappe/twofactor.py
  50. +1
    -1
      frappe/utils/data.py
  51. +1
    -0
      frappe/utils/safe_exec.py
  52. +9
    -9
      frappe/website/doctype/blog_post/blog_post.json
  53. +25
    -27
      frappe/website/doctype/blog_post/blog_post.py
  54. +16
    -3
      frappe/website/doctype/blog_post/templates/blog_post.html
  55. +23
    -0
      frappe/website/doctype/blog_post/test_blog_post.py
  56. +9
    -9
      frappe/website/doctype/blog_settings/blog_settings.json
  57. +2
    -2
      frappe/website/doctype/blog_settings/blog_settings.py
  58. +1
    -1
      frappe/website/doctype/web_page_view/web_page_view.py
  59. +1
    -1
      frappe/website/doctype/website_settings/google_indexing.py
  60. +1
    -1
      frappe/www/contact.py
  61. +1
    -1
      package.json
  62. +1
    -0
      pyproject.toml
  63. +4
    -4
      yarn.lock

+ 1
- 1
.editorconfig 查看文件

@@ -9,6 +9,6 @@ trim_trailing_whitespace = true
charset = utf-8 charset = utf-8


# python, js indentation settings # python, js indentation settings
[{*.py,*.js,*.vue}]
[{*.py,*.js,*.vue,*.css,*.scss,*.html}]
indent_style = tab indent_style = tab
indent_size = 4 indent_size = 4

+ 1
- 1
.github/helper/roulette.py 查看文件

@@ -46,7 +46,7 @@ def is_ci(file):
return ".github" in file return ".github" in file


def is_frontend_code(file): def is_frontend_code(file):
return file.lower().endswith((".css", ".scss", ".less", ".sass", ".styl", ".js", ".ts", ".vue"))
return file.lower().endswith((".css", ".scss", ".less", ".sass", ".styl", ".js", ".ts", ".vue", ".html"))


def is_docs(file): def is_docs(file):
regex = re.compile(r'\.(md|png|jpg|jpeg|csv|svg)$|^.github|LICENSE') regex = re.compile(r'\.(md|png|jpg|jpeg|csv|svg)$|^.github|LICENSE')


+ 11
- 9
.github/workflows/patch-mariadb-tests.yml 查看文件

@@ -30,26 +30,28 @@ jobs:
- name: Clone - name: Clone
uses: actions/checkout@v3 uses: actions/checkout@v3


- name: Check if build should be run
id: check-build
run: |
python "${GITHUB_WORKSPACE}/.github/helper/roulette.py"
env:
TYPE: "server"
PR_NUMBER: ${{ github.event.number }}
REPO_NAME: ${{ github.repository }}

- name: Setup Python - name: Setup Python
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: "gabrielfalcao/pyenv-action@v10" uses: "gabrielfalcao/pyenv-action@v10"
with: with:
versions: 3.10:latest, 3.7:latest versions: 3.10:latest, 3.7:latest


- name: Setup Node - name: Setup Node
if: ${{ steps.check-build.outputs.build == 'strawberry' }}
uses: actions/setup-node@v3 uses: actions/setup-node@v3
with: with:
node-version: 14 node-version: 14
check-latest: true check-latest: true


- name: Check if build should be run
id: check-build
run: |
python "${GITHUB_WORKSPACE}/.github/helper/roulette.py"
env:
TYPE: "server"
PR_NUMBER: ${{ github.event.number }}
REPO_NAME: ${{ github.repository }}

- name: Add to Hosts - name: Add to Hosts
if: ${{ steps.check-build.outputs.build == 'strawberry' }} if: ${{ steps.check-build.outputs.build == 'strawberry' }}
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts


+ 12
- 2
frappe/core/doctype/comment/comment.json 查看文件

@@ -1,4 +1,5 @@
{ {
"actions": [],
"creation": "2019-02-07 10:10:46.845678", "creation": "2019-02-07 10:10:46.845678",
"doctype": "DocType", "doctype": "DocType",
"editable_grid": 1, "editable_grid": 1,
@@ -17,7 +18,8 @@
"link_name", "link_name",
"reference_owner", "reference_owner",
"section_break_10", "section_break_10",
"content"
"content",
"ip_address"
], ],
"fields": [ "fields": [
{ {
@@ -102,9 +104,16 @@
"ignore_xss_filter": 1, "ignore_xss_filter": 1,
"in_list_view": 1, "in_list_view": 1,
"label": "Content" "label": "Content"
},
{
"fieldname": "ip_address",
"fieldtype": "Data",
"hidden": 1,
"label": "IP Address"
} }
], ],
"modified": "2019-09-02 21:00:10.784787",
"links": [],
"modified": "2022-07-12 17:35:31.774137",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Core", "module": "Core",
"name": "Comment", "name": "Comment",
@@ -138,6 +147,7 @@
"quick_entry": 1, "quick_entry": 1,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [],
"title_field": "comment_type", "title_field": "comment_type",
"track_changes": 1 "track_changes": 1
} }

+ 2
- 1
frappe/core/doctype/communication/mixins.py 查看文件

@@ -73,7 +73,8 @@ class CommunicationEmailMixin:
if include_sender: if include_sender:
cc.append(self.sender_mailid) cc.append(self.sender_mailid)
if is_inbound_mail_communcation: if is_inbound_mail_communcation:
cc.append(self.get_owner())
if (doc_owner := self.get_owner()) not in frappe.STANDARD_USERS:
cc.append(doc_owner)
cc = set(cc) - {self.sender_mailid} cc = set(cc) - {self.sender_mailid}
cc.update(self.get_assignees()) cc.update(self.get_assignees())




+ 9
- 3
frappe/core/doctype/doctype/doctype.js 查看文件

@@ -46,9 +46,7 @@ frappe.ui.form.on('DocType', {
} }


if(frm.is_new()) { if(frm.is_new()) {
if (!(frm.doc.permissions && frm.doc.permissions.length)) {
frm.add_child('permissions', {role: 'System Manager'});
}
frm.events.set_default_permission(frm);
} else { } else {
frm.toggle_enable("engine", 0); frm.toggle_enable("engine", 0);
} }
@@ -65,6 +63,14 @@ frappe.ui.form.on('DocType', {
if (frm.doc.istable && frm.is_new()) { if (frm.doc.istable && frm.is_new()) {
frm.set_value('autoname', 'autoincrement'); frm.set_value('autoname', 'autoincrement');
frm.set_value('allow_rename', 0); frm.set_value('allow_rename', 0);
} else if (!frm.doc.istable && !frm.is_new()) {
frm.events.set_default_permission(frm);
}
},

set_default_permission: (frm) => {
if (!(frm.doc.permissions && frm.doc.permissions.length)) {
frm.add_child('permissions', {role: 'System Manager'});
} }
}, },
}); });


+ 3
- 4
frappe/core/doctype/doctype/doctype.py 查看文件

@@ -181,10 +181,6 @@ class DocType(Document):
) )
) )


def after_insert(self):
# clear user cache so that on the next reload this doctype is included in boot
clear_user_cache(frappe.session.user)

def set_defaults_for_single_and_table(self): def set_defaults_for_single_and_table(self):
if self.issingle: if self.issingle:
self.allow_import = 0 self.allow_import = 0
@@ -412,6 +408,9 @@ class DocType(Document):
delete_notification_count_for(doctype=self.name) delete_notification_count_for(doctype=self.name)
frappe.clear_cache(doctype=self.name) frappe.clear_cache(doctype=self.name)


# clear user cache so that on the next reload this doctype is included in boot
clear_user_cache(frappe.session.user)

if not frappe.flags.in_install and hasattr(self, "before_update"): if not frappe.flags.in_install and hasattr(self, "before_update"):
self.sync_global_search() self.sync_global_search()




+ 0
- 8
frappe/core/doctype/feedback/feedback.js 查看文件

@@ -1,8 +0,0 @@
// Copyright (c) 2021, Frappe Technologies and contributors
// For license information, please see license.txt

frappe.ui.form.on('Feedback', {
// refresh: function(frm) {

// }
});

+ 0
- 73
frappe/core/doctype/feedback/feedback.json 查看文件

@@ -1,73 +0,0 @@
{
"actions": [],
"creation": "2021-06-03 19:02:55.328423",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"reference_doctype",
"reference_name",
"column_break_3",
"like",
"ip_address"
],
"fields": [
{
"fieldname": "column_break_3",
"fieldtype": "Column Break"
},
{
"fieldname": "reference_doctype",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Reference Document Type",
"options": "\nBlog Post"
},
{
"fieldname": "reference_name",
"fieldtype": "Dynamic Link",
"in_list_view": 1,
"label": "Reference Name",
"options": "reference_doctype",
"reqd": 1
},
{
"fieldname": "ip_address",
"fieldtype": "Data",
"hidden": 1,
"label": "IP Address",
"read_only": 1
},
{
"default": "0",
"fieldname": "like",
"fieldtype": "Check",
"label": "Like"
}
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2021-11-10 20:53:21.255593",
"modified_by": "Administrator",
"module": "Core",
"name": "Feedback",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "reference_name",
"track_changes": 1
}

+ 0
- 9
frappe/core/doctype/feedback/feedback.py 查看文件

@@ -1,9 +0,0 @@
# Copyright (c) 2021, Frappe Technologies and contributors
# License: MIT. See LICENSE

# import frappe
from frappe.model.document import Document


class Feedback(Document):
pass

+ 0
- 42
frappe/core/doctype/feedback/test_feedback.py 查看文件

@@ -1,42 +0,0 @@
# Copyright (c) 2021, Frappe Technologies and Contributors
# License: MIT. See LICENSE

import unittest

import frappe


class TestFeedback(unittest.TestCase):
def tearDown(self):
frappe.form_dict.reference_doctype = None
frappe.form_dict.reference_name = None
frappe.form_dict.like = None
frappe.local.request_ip = None

def test_feedback_creation_updation(self):
from frappe.website.doctype.blog_post.test_blog_post import make_test_blog

test_blog = make_test_blog()

frappe.db.delete("Feedback", {"reference_doctype": "Blog Post"})

from frappe.templates.includes.feedback.feedback import give_feedback

frappe.form_dict.reference_doctype = "Blog Post"
frappe.form_dict.reference_name = test_blog.name
frappe.form_dict.like = True
frappe.local.request_ip = "127.0.0.1"

feedback = give_feedback()

self.assertEqual(feedback.like, True)

frappe.form_dict.like = False

updated_feedback = give_feedback()

self.assertEqual(updated_feedback.like, False)

frappe.db.delete("Feedback", {"reference_doctype": "Blog Post"})

test_blog.delete()

+ 1
- 1
frappe/core/doctype/sms_settings/sms_settings.py 查看文件

@@ -63,7 +63,7 @@ def send_sms(receiver_list, msg, sender_name="", success_msg=True):
"success_msg": success_msg, "success_msg": success_msg,
} }


if frappe.db.get_value("SMS Settings", None, "sms_gateway_url"):
if frappe.db.get_single_value("SMS Settings", "sms_gateway_url"):
send_via_gateway(arg) send_via_gateway(arg)
else: else:
msgprint(_("Please Update SMS Settings")) msgprint(_("Please Update SMS Settings"))


+ 1
- 1
frappe/core/doctype/system_settings/system_settings.py 查看文件

@@ -28,7 +28,7 @@ class SystemSettings(Document):


if self.enable_two_factor_auth: if self.enable_two_factor_auth:
if self.two_factor_method == "SMS": if self.two_factor_method == "SMS":
if not frappe.db.get_value("SMS Settings", None, "sms_gateway_url"):
if not frappe.db.get_single_value("SMS Settings", "sms_gateway_url"):
frappe.throw( frappe.throw(
_("Please setup SMS before setting it as an authentication method, via SMS Settings") _("Please setup SMS before setting it as an authentication method, via SMS Settings")
) )


+ 3
- 3
frappe/core/doctype/user/user.py 查看文件

@@ -611,10 +611,10 @@ class User(Document):
""" """


login_with_mobile = cint( login_with_mobile = cint(
frappe.db.get_value("System Settings", "System Settings", "allow_login_using_mobile_number")
frappe.db.get_single_value("System Settings", "allow_login_using_mobile_number")
) )
login_with_username = cint( login_with_username = cint(
frappe.db.get_value("System Settings", "System Settings", "allow_login_using_user_name")
frappe.db.get_single_value("System Settings", "allow_login_using_user_name")
) )


or_filters = [{"name": user_name}] or_filters = [{"name": user_name}]
@@ -861,7 +861,7 @@ def sign_up(email, full_name, redirect_to):
user.insert() user.insert()


# set default signup role as per Portal Settings # set default signup role as per Portal Settings
default_role = frappe.db.get_value("Portal Settings", None, "default_role")
default_role = frappe.db.get_single_value("Portal Settings", "default_role")
if default_role: if default_role:
user.add_roles(default_role) user.add_roles(default_role)




+ 5
- 6
frappe/core/doctype/version/version_view.html 查看文件

@@ -18,8 +18,8 @@
{% for item in data.changed %} {% for item in data.changed %}
<tr> <tr>
<td>{{ frappe.meta.get_label(doc.ref_doctype, item[0]) }}</td> <td>{{ frappe.meta.get_label(doc.ref_doctype, item[0]) }}</td>
<td class="danger">{{ item[1] }}</td>
<td class="success">{{ item[2] }}</td>
<td class="diff-remove">{{ item[1] }}</td>
<td class="diff-add">{{ item[2] }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@@ -43,8 +43,7 @@
{% for item in values %} {% for item in values %}
<tr> <tr>
<td>{{ frappe.meta.get_label(doc.ref_doctype, item[0]) }}</td> <td>{{ frappe.meta.get_label(doc.ref_doctype, item[0]) }}</td>
<td class="{{
key==="added" ? __("success") : __("danger") }}">
<td class="{{ key==="added" ? "diff-add" : "diff-remove" }}">
{% var item_keys = Object.keys(item[1]).sort(); %} {% var item_keys = Object.keys(item[1]).sort(); %}
<table class="table table-bordered"> <table class="table table-bordered">
<tbody> <tbody>
@@ -86,8 +85,8 @@
<td>{{ frappe.meta.get_label(doc.ref_doctype, table_info[0]) }}</td> <td>{{ frappe.meta.get_label(doc.ref_doctype, table_info[0]) }}</td>
<td>{{ table_info[1] }}</td> <td>{{ table_info[1] }}</td>
<td>{{ item[0] }}</td> <td>{{ item[0] }}</td>
<td class="danger">{{ item[1] }}</td>
<td class="success">{{ item[2] }}</td>
<td class="diff-remove">{{ item[1] }}</td>
<td class="diff-add">{{ item[2] }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
{% endfor %} {% endfor %}


+ 2
- 4
frappe/hooks.py 查看文件

@@ -180,12 +180,10 @@ doc_events = {
"on_update": "frappe.integrations.doctype.google_contacts.google_contacts.update_contacts_to_google_contacts", "on_update": "frappe.integrations.doctype.google_contacts.google_contacts.update_contacts_to_google_contacts",
}, },
"DocType": { "DocType": {
"after_insert": "frappe.cache_manager.build_domain_restriced_doctype_cache",
"after_save": "frappe.cache_manager.build_domain_restriced_doctype_cache",
"on_update": "frappe.cache_manager.build_domain_restriced_doctype_cache",
}, },
"Page": { "Page": {
"after_insert": "frappe.cache_manager.build_domain_restriced_page_cache",
"after_save": "frappe.cache_manager.build_domain_restriced_page_cache",
"on_update": "frappe.cache_manager.build_domain_restriced_page_cache",
}, },
} }




+ 3
- 3
frappe/integrations/doctype/dropbox_settings/dropbox_settings.py 查看文件

@@ -62,21 +62,21 @@ def take_backups_weekly():




def take_backups_if(freq): def take_backups_if(freq):
if frappe.db.get_value("Dropbox Settings", None, "backup_frequency") == freq:
if frappe.db.get_single_value("Dropbox Settings", "backup_frequency") == freq:
take_backup_to_dropbox() take_backup_to_dropbox()




def take_backup_to_dropbox(retry_count=0, upload_db_backup=True): def take_backup_to_dropbox(retry_count=0, upload_db_backup=True):
did_not_upload, error_log = [], [] did_not_upload, error_log = [], []
try: try:
if cint(frappe.db.get_value("Dropbox Settings", None, "enabled")):
if cint(frappe.db.get_single_value("Dropbox Settings", "enabled")):
validate_file_size() validate_file_size()


did_not_upload, error_log = backup_to_dropbox(upload_db_backup) did_not_upload, error_log = backup_to_dropbox(upload_db_backup)
if did_not_upload: if did_not_upload:
raise Exception raise Exception


if cint(frappe.db.get_value("Dropbox Settings", None, "send_email_for_successful_backup")):
if cint(frappe.db.get_single_value("Dropbox Settings", "send_email_for_successful_backup")):
send_email(True, "Dropbox", "Dropbox Settings", "send_notifications_to") send_email(True, "Dropbox", "Dropbox Settings", "send_notifications_to")
except JobTimeoutException: except JobTimeoutException:
if retry_count < 2: if retry_count < 2:


+ 1
- 1
frappe/integrations/doctype/google_drive/google_drive.py 查看文件

@@ -48,7 +48,7 @@ def authorize_access(reauthorize=False, code=None):
""" """


oauth_code = ( oauth_code = (
frappe.db.get_value("Google Drive", "Google Drive", "authorization_code") if not code else code
frappe.db.get_single_value("Google Drive", "authorization_code") if not code else code
) )
oauth_obj = GoogleOAuth("drive") oauth_obj = GoogleOAuth("drive")




+ 3
- 1
frappe/integrations/doctype/oauth_provider_settings/oauth_provider_settings.py 查看文件

@@ -14,7 +14,9 @@ def get_oauth_settings():
"""Returns oauth settings""" """Returns oauth settings"""
out = frappe._dict( out = frappe._dict(
{ {
"skip_authorization": frappe.db.get_value("OAuth Provider Settings", None, "skip_authorization")
"skip_authorization": frappe.db.get_single_value(
"OAuth Provider Settings", "skip_authorization"
)
} }
) )




+ 2
- 2
frappe/integrations/doctype/s3_backup_settings/s3_backup_settings.py 查看文件

@@ -76,8 +76,8 @@ def take_backups_monthly():




def take_backups_if(freq): def take_backups_if(freq):
if cint(frappe.db.get_value("S3 Backup Settings", None, "enabled")):
if frappe.db.get_value("S3 Backup Settings", None, "frequency") == freq:
if cint(frappe.db.get_single_value("S3 Backup Settings", "enabled")):
if frappe.db.get_single_value("S3 Backup Settings", "frequency") == freq:
take_backups_s3() take_backups_s3()






+ 19
- 2
frappe/model/naming.py 查看文件

@@ -1,6 +1,7 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE # License: MIT. See LICENSE


import datetime
import re import re
from typing import TYPE_CHECKING, Callable, Optional from typing import TYPE_CHECKING, Callable, Optional


@@ -23,6 +24,17 @@ NAMING_SERIES_PATTERN = re.compile(r"^[\w\- \/.#{}]+$", re.UNICODE)
BRACED_PARAMS_PATTERN = re.compile(r"(\{[\w | #]+\})") BRACED_PARAMS_PATTERN = re.compile(r"(\{[\w | #]+\})")




# Types that can be using in naming series fields
NAMING_SERIES_PART_TYPES = (
int,
str,
datetime.datetime,
datetime.date,
datetime.time,
datetime.timedelta,
)


class InvalidNamingSeriesError(frappe.ValidationError): class InvalidNamingSeriesError(frappe.ValidationError):
pass pass


@@ -298,6 +310,9 @@ def parse_naming_series(
series_set = False series_set = False
today = now_datetime() today = now_datetime()
for e in parts: for e in parts:
if not e:
continue

part = "" part = ""
if e.startswith("#"): if e.startswith("#"):
if not series_set: if not series_set:
@@ -320,14 +335,16 @@ def parse_naming_series(
part = frappe.defaults.get_user_default("fiscal_year") part = frappe.defaults.get_user_default("fiscal_year")
elif e.startswith("{") and doc: elif e.startswith("{") and doc:
e = e.replace("{", "").replace("}", "") e = e.replace("{", "").replace("}", "")
part = (cstr(doc.get(e)) or "").strip()
part = doc.get(e)
elif doc and doc.get(e): elif doc and doc.get(e):
part = (cstr(doc.get(e)) or "").strip()
part = doc.get(e)
else: else:
part = e part = e


if isinstance(part, str): if isinstance(part, str):
name += part name += part
elif isinstance(part, NAMING_SERIES_PART_TYPES):
name += cstr(part).strip()


return name return name




+ 1
- 0
frappe/patches.txt 查看文件

@@ -193,6 +193,7 @@ frappe.patches.v14_0.reset_creation_datetime
frappe.patches.v14_0.remove_is_first_startup frappe.patches.v14_0.remove_is_first_startup
frappe.patches.v14_0.clear_long_pending_stale_logs frappe.patches.v14_0.clear_long_pending_stale_logs
frappe.patches.v14_0.log_settings_migration frappe.patches.v14_0.log_settings_migration
frappe.patches.v14_0.setup_likes_from_feedback


[post_model_sync] [post_model_sync]
frappe.patches.v14_0.drop_data_import_legacy frappe.patches.v14_0.drop_data_import_legacy


+ 1
- 1
frappe/patches/v11_0/set_dropbox_file_backup.py 查看文件

@@ -4,6 +4,6 @@ from frappe.utils import cint


def execute(): def execute():
frappe.reload_doctype("Dropbox Settings") frappe.reload_doctype("Dropbox Settings")
check_dropbox_enabled = cint(frappe.db.get_value("Dropbox Settings", None, "enabled"))
check_dropbox_enabled = cint(frappe.db.get_single_value("Dropbox Settings", "enabled"))
if check_dropbox_enabled == 1: if check_dropbox_enabled == 1:
frappe.db.set_value("Dropbox Settings", None, "file_backup", 1) frappe.db.set_value("Dropbox Settings", None, "file_backup", 1)

+ 30
- 0
frappe/patches/v14_0/setup_likes_from_feedback.py 查看文件

@@ -0,0 +1,30 @@
import frappe


def execute():
frappe.reload_doctype("Comment")

if frappe.db.count("Feedback") > 20000:
frappe.db.auto_commit_on_many_writes = True

for feedback in frappe.get_all("Feedback", fields=["*"]):
if feedback.like:
new_comment = frappe.new_doc("Comment")
new_comment.comment_type = "Like"
new_comment.comment_email = feedback.owner
new_comment.content = "Liked by: " + feedback.owner
new_comment.reference_doctype = feedback.reference_doctype
new_comment.reference_name = feedback.reference_name
new_comment.creation = feedback.creation
new_comment.modified = feedback.modified
new_comment.owner = feedback.owner
new_comment.modified_by = feedback.modified_by
new_comment.ip_address = feedback.ip_address
new_comment.db_insert()

if frappe.db.auto_commit_on_many_writes:
frappe.db.auto_commit_on_many_writes = False

# clean up
frappe.db.delete("Feedback")
frappe.db.commit()

+ 2
- 2
frappe/public/js/frappe/form/controls/datepicker_i18n.js 查看文件

@@ -22,10 +22,10 @@ import "air-datepicker/dist/js/i18n/datepicker.zh.js";
months: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'اكتوبر', 'نوفمبر', 'ديسمبر'], months: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'اكتوبر', 'نوفمبر', 'ديسمبر'],
monthsShort: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'اكتوبر', 'نوفمبر', 'ديسمبر'], monthsShort: ['يناير', 'فبراير', 'مارس', 'أبريل', 'مايو', 'يونيو', 'يوليو', 'أغسطس', 'سبتمبر', 'اكتوبر', 'نوفمبر', 'ديسمبر'],
today: 'اليوم', today: 'اليوم',
clear: 'Clear',
clear: 'حذف',
dateFormat: 'dd/mm/yyyy', dateFormat: 'dd/mm/yyyy',
timeFormat: 'hh:ii aa', timeFormat: 'hh:ii aa',
firstDay: 0
firstDay: 6
}; };
})(jQuery); })(jQuery);




+ 2
- 1
frappe/public/js/frappe/form/dashboard.js 查看文件

@@ -554,7 +554,8 @@ frappe.ui.form.Dashboard = class FormDashboard {
colors: ['green'], colors: ['green'],
truncateLegends: 1, truncateLegends: 1,
axisOptions: { axisOptions: {
shortenYAxisNumbers: 1
shortenYAxisNumbers: 1,
numberFormatter: frappe.utils.format_chart_axis_number,
} }
}); });
this.show(); this.show();


+ 1
- 1
frappe/public/js/frappe/form/formatters.js 查看文件

@@ -26,7 +26,7 @@ frappe.form.formatters = {
if (df) { if (df) {
const std_df = frappe.meta.docfield_map[df.parent] && frappe.meta.docfield_map[df.parent][df.fieldname]; const std_df = frappe.meta.docfield_map[df.parent] && frappe.meta.docfield_map[df.parent][df.fieldname];
if (std_df && std_df.formatter && typeof std_df.formatter==='function') { if (std_df && std_df.formatter && typeof std_df.formatter==='function') {
value = std_df.formatter(value);
value = std_df.formatter(value, df);
} }
} }
return value; return value;


+ 0
- 15
frappe/public/js/frappe/list/base_list.js 查看文件

@@ -764,10 +764,6 @@ class FilterArea {


const doctype_fields = this.list_view.meta.fields; const doctype_fields = this.list_view.meta.fields;
const title_field = this.list_view.meta.title_field; const title_field = this.list_view.meta.title_field;
const has_existing_filters = (
this.list_view.filters
&& this.list_view.filters.length > 0
);


fields = fields.concat( fields = fields.concat(
doctype_fields doctype_fields
@@ -803,23 +799,12 @@ class FilterArea {
} }
} }


let default_value;

if (fieldtype === "Link" && !has_existing_filters) {
default_value = frappe.defaults.get_user_default(options);
}

if (["__default", "__global"].includes(default_value)) {
default_value = null;
}

return { return {
fieldtype: fieldtype, fieldtype: fieldtype,
label: __(df.label), label: __(df.label),
options: options, options: options,
fieldname: df.fieldname, fieldname: df.fieldname,
condition: condition, condition: condition,
default: default_value,
onchange: () => this.refresh_list_view(), onchange: () => this.refresh_list_view(),
ignore_link_validation: fieldtype === "Dynamic Link", ignore_link_validation: fieldtype === "Dynamic Link",
is_filter: 1, is_filter: 1,


+ 1
- 4
frappe/public/js/frappe/list/list_view.js 查看文件

@@ -87,10 +87,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
this.menu_items = this.menu_items.concat(this.get_menu_items()); this.menu_items = this.menu_items.concat(this.get_menu_items());


// set filters from view_user_settings or list_settings // set filters from view_user_settings or list_settings
if (
this.view_user_settings.filters &&
this.view_user_settings.filters.length
) {
if (Array.isArray(this.view_user_settings.filters)) {
// Priority 1: view_user_settings // Priority 1: view_user_settings
const saved_filters = this.view_user_settings.filters; const saved_filters = this.view_user_settings.filters;
this.filters = this.validate_filters(saved_filters); this.filters = this.validate_filters(saved_filters);


+ 1
- 1
frappe/public/js/frappe/utils/diffview.js 查看文件

@@ -89,7 +89,7 @@ frappe.ui.DiffView = class DiffView {
} else if (line.startsWith("-")) { } else if (line.startsWith("-")) {
line_class = "delete"; line_class = "delete";
} }
html += `<div class=${line_class}>${line}</div>`;
html += `<div class="${line_class} text-wrap">${line}</div>`;
}); });
return `<div class='diffview'>${html}</div>`; return `<div class='diffview'>${html}</div>`;
} }


+ 38
- 2
frappe/public/js/frappe/utils/utils.js 查看文件

@@ -1145,7 +1145,12 @@ Object.assign(frappe.utils, {
{ {
divisor: 1.0e+5, divisor: 1.0e+5,
symbol: 'Lakh' symbol: 'Lakh'
}],
},
{
divisor: 1.0e+3,
symbol: 'K',
}
],
'': '':
[{ [{
divisor: 1.0e+12, divisor: 1.0e+12,
@@ -1205,7 +1210,8 @@ Object.assign(frappe.utils, {
axisOptions: { axisOptions: {
xIsSeries: 1, xIsSeries: 1,
shortenYAxisNumbers: 1, shortenYAxisNumbers: 1,
xAxisMode: 'tick'
xAxisMode: 'tick',
numberFormatter: frappe.utils.format_chart_axis_number,
} }
}; };


@@ -1220,6 +1226,11 @@ Object.assign(frappe.utils, {
return new frappe.Chart(wrapper, chart_args); return new frappe.Chart(wrapper, chart_args);
}, },


format_chart_axis_number(label, country) {
const default_country = frappe.sys_defaults.country;
return frappe.utils.shorten_number(label, country || default_country, 3);
},

generate_route(item) { generate_route(item) {
const type = item.type.toLowerCase(); const type = item.type.toLowerCase();
if (type === "doctype") { if (type === "doctype") {
@@ -1537,4 +1548,29 @@ Object.assign(frappe.utils, {
is_current_user(user) { is_current_user(user) {
return user === frappe.session.user; return user === frappe.session.user;
}, },

debug: {
watch_property(obj, prop, callback=console.trace) {
if (!frappe.boot.developer_mode) {
return;
}
console.warn("Adding property watcher, make sure to remove it after debugging.");

// Adapted from https://stackoverflow.com/a/11658693
// Reused under CC-BY-SA 4.0
// changes: variable names are changed for consistency with our codebase
const private_prop = "$_" + prop + "_$";
obj[private_prop] = obj[prop];

Object.defineProperty(obj, prop, {
get: function() {
return obj[private_prop];
},
set: function(value) {
callback();
obj[private_prop] = value;
},
});
},
}
}); });

+ 4
- 3
frappe/public/js/frappe/views/reports/query_report.js 查看文件

@@ -623,6 +623,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {


if (data.prepared_report) { if (data.prepared_report) {
this.prepared_report = true; this.prepared_report = true;
this.prepared_report_document = data.doc
// If query_string contains prepared_report_name then set filters // If query_string contains prepared_report_name then set filters
// to match the mentioned prepared report doc and disable editing // to match the mentioned prepared report doc and disable editing
if (query_params.prepared_report_name) { if (query_params.prepared_report_name) {
@@ -943,10 +944,10 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
}; };
} }
options.axisOptions = { options.axisOptions = {
shortenYAxisNumbers: 1
shortenYAxisNumbers: 1,
numberFormatter: frappe.utils.format_chart_axis_number,
}; };
options.height = 280; options.height = 280;

return options; return options;
} }


@@ -1800,7 +1801,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
} }


toggle_nothing_to_show(flag) { toggle_nothing_to_show(flag) {
let message = this.prepared_report
let message = (this.prepared_report && !this.prepared_report_document)
? __('This is a background report. Please set the appropriate filters and then generate a new one.') ? __('This is a background report. Please set the appropriate filters and then generate a new one.')
: this.get_no_result_message(); : this.get_no_result_message();




+ 2
- 1
frappe/public/js/frappe/views/reports/report_utils.js 查看文件

@@ -30,7 +30,8 @@ frappe.report_utils = {
colors: colors, colors: colors,
axisOptions: { axisOptions: {
shortenYAxisNumbers: 1, shortenYAxisNumbers: 1,
xAxisMode: 'tick'
xAxisMode: 'tick',
numberFormatter: frappe.utils.format_chart_axis_number,
} }
}; };




+ 2
- 1
frappe/public/js/frappe/views/reports/report_view.js 查看文件

@@ -529,7 +529,8 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
truncateLegends: 1, truncateLegends: 1,
colors: ['#70E078', 'light-blue', 'orange', 'red'], colors: ['#70E078', 'light-blue', 'orange', 'red'],
axisOptions: { axisOptions: {
shortenYAxisNumbers: 1
shortenYAxisNumbers: 1,
numberFormatter: frappe.utils.format_chart_axis_number,
}, },
tooltipOptions: { tooltipOptions: {
formatTooltipY: value => frappe.format(value, get_df(this.chart_args.y_axes[0]), { always_show_decimals: true, inline: true }, get_doc(value.doc)) formatTooltipY: value => frappe.format(value, get_df(this.chart_args.y_axes[0]), { always_show_decimals: true, inline: true }, get_doc(value.doc))


+ 5
- 1
frappe/public/scss/common/css_variables.scss 查看文件

@@ -134,7 +134,7 @@ $input-height: 28px !default;


--shadow-xs: rgba(0, 0, 0, 0.05) 0px 0.5px 0px 0px, rgba(0, 0, 0, 0.08) 0px 0px 0px 1px, rgba(0, 0, 0, 0.05) 0px 2px 4px 0px; --shadow-xs: rgba(0, 0, 0, 0.05) 0px 0.5px 0px 0px, rgba(0, 0, 0, 0.08) 0px 0px 0px 1px, rgba(0, 0, 0, 0.05) 0px 2px 4px 0px;
--shadow-sm: 0px 1px 2px rgba(25, 39, 52, 0.05), 0px 0px 4px rgba(25, 39, 52, 0.1); --shadow-sm: 0px 1px 2px rgba(25, 39, 52, 0.05), 0px 0px 4px rgba(25, 39, 52, 0.1);
--shadow-base: 0px 4px 8px rgba(25, 39, 52, 0.06), 0px 0px 4px rgba(25, 39, 52, 0.12);
--shadow-base: 0px 4px 8px rgba(25, 39, 52, 0.06), 0px 0px 4px rgba(25, 39, 52, 0.12);
--shadow-md: 0px 8px 14px rgba(25, 39, 52, 0.08), 0px 2px 6px rgba(25, 39, 52, 0.04); --shadow-md: 0px 8px 14px rgba(25, 39, 52, 0.08), 0px 2px 6px rgba(25, 39, 52, 0.04);
--shadow-lg: 0px 18px 22px rgba(25, 39, 52, 0.1), 0px 1px 10px rgba(0, 0, 0, 0.06), 0px 0.5px 5px rgba(25, 39, 52, 0.04); --shadow-lg: 0px 18px 22px rgba(25, 39, 52, 0.1), 0px 1px 10px rgba(0, 0, 0, 0.06), 0px 0.5px 5px rgba(25, 39, 52, 0.04);


@@ -262,6 +262,10 @@ $input-height: 28px !default;
--checkbox-focus-shadow: 0 0 0 2px var(--gray-300); --checkbox-focus-shadow: 0 0 0 2px var(--gray-300);
--checkbox-gradient: linear-gradient(180deg, #4AC3F8 -124.51%, var(--primary) 100%); --checkbox-gradient: linear-gradient(180deg, #4AC3F8 -124.51%, var(--primary) 100%);


// "diff" colors
--diff-added: var(--green-100);
--diff-removed: var(--red-100);

--right-arrow-svg: url("data: image/svg+xml;utf8, <svg width='6' height='8' viewBox='0 0 6 8' fill='none' xmlns='http://www.w3.org/2000/svg'><path d='M1.25 7.5L4.75 4L1.25 0.5' stroke='%231F272E' stroke-linecap='round' stroke-linejoin='round'/></svg>"); --right-arrow-svg: url("data: image/svg+xml;utf8, <svg width='6' height='8' viewBox='0 0 6 8' fill='none' xmlns='http://www.w3.org/2000/svg'><path d='M1.25 7.5L4.75 4L1.25 0.5' stroke='%231F272E' stroke-linecap='round' stroke-linejoin='round'/></svg>");
--left-arrow-svg: url("data: image/svg+xml;utf8, <svg width='6' height='8' viewBox='0 0 6 8' fill='none' xmlns='http://www.w3.org/2000/svg'><path d='M7.5 9.5L4 6l3.5-3.5' stroke='%231F272E' stroke-linecap='round' stroke-linejoin='round'></path></svg>"); --left-arrow-svg: url("data: image/svg+xml;utf8, <svg width='6' height='8' viewBox='0 0 6 8' fill='none' xmlns='http://www.w3.org/2000/svg'><path d='M7.5 9.5L4 6l3.5-3.5' stroke='%231F272E' stroke-linecap='round' stroke-linejoin='round'></path></svg>");
} }

+ 5
- 1
frappe/public/scss/desk/dark.scss 查看文件

@@ -91,7 +91,11 @@


--highlight-shadow: 1px 1px 10px var(--blue-900), 0px 0px 4px var(--blue-500); --highlight-shadow: 1px 1px 10px var(--blue-900), 0px 0px 4px var(--blue-500);


--shadow-base: 0px 4px 8px rgba(114, 176, 233, 0.06), 0px 0px 4px rgba(112, 172, 228, 0.12);
--shadow-base: 0px 4px 8px rgba(114, 176, 233, 0.06), 0px 0px 4px rgba(112, 172, 228, 0.12);

// "diff" colors
--diff-added: var(--green-800);
--diff-removed: var(--red-800);


// input // input
--input-disabled-bg: none; --input-disabled-bg: none;


+ 8
- 12
frappe/public/scss/desk/global.scss 查看文件

@@ -579,22 +579,18 @@ details > summary:focus {
color: var(--text-color); color: var(--text-color);
} }


.diffview .insert {
background-color: var(--green-100);
.diffview .insert,
.diff-add {
background-color: var(--diff-added);
} }


.diffview .delete {
background-color: var(--red-100);
.diffview .delete,
.diff-remove {
background-color: var(--diff-removed);
} }


[data-theme="dark"] {
.diffview .insert {
background-color: var(--green-800);
}
.diffview .delete {
background-color: var(--red-800);
}
.chart-wrapper {
padding: 1em;
} }


// REDESIGN TODO: Handling of broken images? // REDESIGN TODO: Handling of broken images?


+ 1
- 1
frappe/templates/includes/comments/comment.html 查看文件

@@ -13,6 +13,6 @@
{{ frappe.utils.pretty_date(comment.creation) }} {{ frappe.utils.pretty_date(comment.creation) }}
</span> </span>
</div> </div>
<div class="content">{{ frappe.utils.strip_html(comment.content) | markdown }}</div>
<div id="{{ comment.name }}" class="content">{{ frappe.utils.strip_html(comment.content) | markdown }}</div>
</div> </div>
</div> </div>

+ 3
- 4
frappe/templates/includes/comments/comments.html 查看文件

@@ -57,7 +57,7 @@
<script> <script>
frappe.ready(function() { frappe.ready(function() {
let guest_allowed = parseInt("{{ guest_allowed or 0}}"); let guest_allowed = parseInt("{{ guest_allowed or 0}}");
let comment_count = "{{ comment_text }}";
let comment_count = "{{ comment_count }}";
let full_name = "" let full_name = ""
let user_id = ""; let user_id = "";


@@ -88,9 +88,9 @@
} }


let $comment_count = $(` let $comment_count = $(`
<div class="feedback-item">
<div class="feedback-item comments">
<span class="comment-icon">${frappe.utils.icon('small-message', 'md')}</span> <span class="comment-icon">${frappe.utils.icon('small-message', 'md')}</span>
<span class="comment-count"></span>
<span class="comment-count">${comment_count}</span>
</div> </div>
`); `);


@@ -127,7 +127,6 @@
} }


$('.blog-feedback').append($comment_count); $('.blog-feedback').append($comment_count);
$('.comment-count').text(comment_count);
$("#comment-form textarea").val(""); $("#comment-form textarea").val("");


update_timeline_line_length('bottom'); update_timeline_line_length('bottom');


+ 8
- 7
frappe/templates/includes/comments/comments.py 查看文件

@@ -3,9 +3,8 @@
import re import re


import frappe import frappe
from frappe import _
from frappe import _, scrub
from frappe.rate_limiter import rate_limit from frappe.rate_limiter import rate_limit
from frappe.utils import add_to_date, now
from frappe.utils.html_utils import clean_html from frappe.utils.html_utils import clean_html
from frappe.website.doctype.blog_settings.blog_settings import get_comment_limit from frappe.website.doctype.blog_settings.blog_settings import get_comment_limit
from frappe.website.utils import clear_cache from frappe.website.utils import clear_cache
@@ -42,11 +41,13 @@ def add_comment(comment, comment_email, comment_by, reference_doctype, reference
if route: if route:
clear_cache(route) clear_cache(route)


content = (
comment.content
+ "<p><a href='{}/app/Form/Comment/{}' style='font-size: 80%'>{}</a></p>".format(
frappe.utils.get_request_site_address(), comment.name, _("View Comment")
)
if doc.get("route"):
url = f"{frappe.utils.get_request_site_address()}/{doc.route}#{comment.name}"
else:
url = f"{frappe.utils.get_request_site_address()}/app/{scrub(doc.doctype)}/{doc.name}#comment-{comment.name}"

content = comment.content + "<p><a href='{}' style='font-size: 80%'>{}</a></p>".format(
url, _("View Comment")
) )


if doc.doctype == "Blog Post" and not doc.enable_email_notification: if doc.doctype == "Blog Post" and not doc.enable_email_notification:


+ 0
- 0
frappe/templates/includes/feedback/__init__.py 查看文件


+ 0
- 55
frappe/templates/includes/feedback/feedback.py 查看文件

@@ -1,55 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE

import frappe
from frappe import _
from frappe.rate_limiter import rate_limit
from frappe.website.doctype.blog_settings.blog_settings import get_feedback_limit


@frappe.whitelist(allow_guest=True)
@rate_limit(key="reference_name", limit=get_feedback_limit, seconds=60 * 60)
def give_feedback(reference_doctype, reference_name, like):
like = frappe.parse_json(like)
ref_doc = frappe.get_doc(reference_doctype, reference_name)
if ref_doc.disable_feedback == 1:
return

filters = {
"owner": frappe.session.user,
"reference_doctype": reference_doctype,
"reference_name": reference_name,
}
d = frappe.get_all("Feedback", filters=filters, limit=1)
if d:
doc = frappe.get_doc("Feedback", d[0].name)
else:
doc = doc = frappe.new_doc("Feedback")
doc.reference_doctype = reference_doctype
doc.reference_name = reference_name
doc.ip_address = frappe.local.request_ip
doc.like = like
doc.save(ignore_permissions=True)

subject = _("Feedback on {0}: {1}").format(reference_doctype, reference_name)
ref_doc.enable_email_notification and send_mail(doc, subject)
return doc


def send_mail(feedback, subject):
doc = frappe.get_doc(feedback.reference_doctype, feedback.reference_name)
if feedback.like:
message = "<p>Hey, </p><p>You have received a ❤️ heart on your blog post <b>{}</b></p>".format(
feedback.reference_name
)
else:
return

# notify creator
frappe.sendmail(
recipients=frappe.db.get_value("User", doc.owner, "email") or doc.owner,
subject=subject,
message=message,
reference_doctype=doc.doctype,
reference_name=doc.name,
)

frappe/core/doctype/feedback/__init__.py → frappe/templates/includes/likes/__init__.py 查看文件


frappe/templates/includes/feedback/feedback.html → frappe/templates/includes/likes/likes.html 查看文件

@@ -1,20 +1,13 @@
<div class="feedback-item mr-3">
<div id="likes" class="feedback-item likes mr-3">
<span class="like-icon"></span> <span class="like-icon"></span>
<span class="like-count"></span> <span class="like-count"></span>
</div> </div>


<script type="text/javascript"> <script type="text/javascript">
frappe.ready(() => { frappe.ready(() => {
let like = parseInt("{{ user_feedback.like or 0 }}");
let like = parseInt("{{ like or 0 }}");
let like_count = parseInt("{{ like_count or 0 }}"); let like_count = parseInt("{{ like_count or 0 }}");


let update_like = function() {
like = !like;
like ? like_count++ : like_count--;
toggle_like_icon(like);
$('.like-count').text(like_count);
}

let toggle_like_icon = function(active) { let toggle_like_icon = function(active) {
active ? $('.like-icon').addClass('gray') : $('.like-icon').removeClass('gray'); active ? $('.like-icon').addClass('gray') : $('.like-icon').removeClass('gray');
} }
@@ -26,16 +19,21 @@


$('.like-icon').click(() => { $('.like-icon').click(() => {
update_like(); update_like();
update_feedback();
}) })


let update_feedback = function() {
let update_like = function() {
like = !like;
like ? like_count++ : like_count--;
toggle_like_icon(like);
$('.like-count').text(like_count);

return frappe.call({ return frappe.call({
method: "frappe.templates.includes.feedback.feedback.give_feedback",
method: "frappe.templates.includes.likes.likes.like",
args: { args: {
reference_doctype: "{{ reference_doctype or doctype }}", reference_doctype: "{{ reference_doctype or doctype }}",
reference_name: "{{ reference_name or name }}", reference_name: "{{ reference_name or name }}",
like
like,
route: "{{ pathname }}",
} }
}); });
} }

+ 77
- 0
frappe/templates/includes/likes/likes.py 查看文件

@@ -0,0 +1,77 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: MIT. See LICENSE

import frappe
from frappe import _
from frappe.rate_limiter import rate_limit
from frappe.website.doctype.blog_settings.blog_settings import get_like_limit
from frappe.website.utils import clear_cache


@frappe.whitelist(allow_guest=True)
@rate_limit(key="reference_name", limit=get_like_limit, seconds=60 * 60)
def like(reference_doctype, reference_name, like, route=""):
like = frappe.parse_json(like)
ref_doc = frappe.get_doc(reference_doctype, reference_name)
if ref_doc.disable_likes == 1:
return

if like:
liked = add_like(reference_doctype, reference_name)
else:
liked = delete_like(reference_doctype, reference_name)

# since likes are embedded in the page, clear the web cache
if route:
clear_cache(route)

if like and ref_doc.enable_email_notification:
subject = _("Like on {0}: {1}").format(reference_doctype, reference_name)
content = _("You have received a ❤️ like on your blog post")
message = f"<p>{content} <b>{reference_name}</b></p>"
message = message + "<p><a href='{}/{}#likes' style='font-size: 80%'>{}</a></p>".format(
frappe.utils.get_request_site_address(), ref_doc.route, _("View Blog Post")
)

# notify creator
frappe.sendmail(
recipients=frappe.db.get_value("User", ref_doc.owner, "email") or ref_doc.owner,
subject=subject,
message=message,
reference_doctype=ref_doc.doctype,
reference_name=ref_doc.name,
)

return liked


def add_like(reference_doctype, reference_name):
user = frappe.session.user

like = frappe.new_doc("Comment")
like.comment_type = "Like"
like.comment_email = user
like.reference_doctype = reference_doctype
like.reference_name = reference_name
like.content = "Liked by: " + user
if user == "Guest":
like.ip_address = frappe.local.request_ip
like.save(ignore_permissions=True)
return True


def delete_like(reference_doctype, reference_name):
user = frappe.session.user

filters = {
"comment_type": "Like",
"comment_email": user,
"reference_doctype": reference_doctype,
"reference_name": reference_name,
}

if user == "Guest":
filters["ip_address"] = frappe.local.request_ip

frappe.db.delete("Comment", filters)
return False

+ 1
- 1
frappe/templates/pages/integrations/razorpay_checkout.py 查看文件

@@ -51,7 +51,7 @@ def get_context(context):




def get_api_key(): def get_api_key():
api_key = frappe.db.get_value("Razorpay Settings", None, "api_key")
api_key = frappe.db.get_single_value("Razorpay Settings", "api_key")
if cint(frappe.form_dict.get("use_sandbox")): if cint(frappe.form_dict.get("use_sandbox")):
api_key = frappe.conf.sandbox_api_key api_key = frappe.conf.sandbox_api_key




+ 27
- 0
frappe/tests/test_naming.py 查看文件

@@ -9,6 +9,7 @@ from frappe.model.naming import (
append_number_if_name_exists, append_number_if_name_exists,
determine_consecutive_week_number, determine_consecutive_week_number,
getseries, getseries,
parse_naming_series,
revert_series_if_last, revert_series_if_last,
) )
from frappe.tests.utils import FrappeTestCase from frappe.tests.utils import FrappeTestCase
@@ -342,6 +343,32 @@ class TestNaming(FrappeTestCase):
name.startswith("KOOH-on_update"), f"incorrect name generated {name}, missing field value" name.startswith("KOOH-on_update"), f"incorrect name generated {name}, missing field value"
) )


def test_naming_with_empty_part(self):
# check naming with empty part (duplicate dots)

webhook = frappe.new_doc("Webhook")
webhook.webhook_docevent = "on_update"

series = "KOOH-..{webhook_docevent}.-.####"

name = parse_naming_series(series, doc=webhook)
self.assertTrue(
name.startswith("KOOH-on_update"), f"incorrect name generated {name}, missing field value"
)

def test_naming_with_unsupported_part(self):
# check naming with empty part (duplicate dots)

webhook = frappe.new_doc("Webhook")
webhook.webhook_docevent = {"dict": "not supported"}

series = "KOOH-..{webhook_docevent}.-.####"

name = parse_naming_series(series, doc=webhook)
self.assertTrue(
name.startswith("KOOH-"), f"incorrect name generated {name}, missing field value"
)



def make_invalid_todo(): def make_invalid_todo():
frappe.get_doc({"doctype": "ToDo", "description": "Test"}).insert(set_name="ToDo") frappe.get_doc({"doctype": "ToDo", "description": "Test"}).insert(set_name="ToDo")

+ 138
- 153
frappe/translations/ru.csv 查看文件

@@ -107,7 +107,7 @@ Hourly,Почасовой,
Hub Sync ID,Идентификатор синхронизации концентратора, Hub Sync ID,Идентификатор синхронизации концентратора,
IP Address,IP адрес, IP Address,IP адрес,
Image,Изображение, Image,Изображение,
Image View,Просмотр изображения,
Image View,Просмотр изображений,
Import Data,Импорт данных, Import Data,Импорт данных,
Import Log,Лог импорта, Import Log,Лог импорта,
Inactive,Неактивный, Inactive,Неактивный,
@@ -246,7 +246,7 @@ Start Import,Начать импорт,
State,Состояние, State,Состояние,
Stopped,Приостановлено, Stopped,Приостановлено,
Subject,Тема, Subject,Тема,
Submit,Провести,
Submit,Подписать,
Successful,Успешно, Successful,Успешно,
Summary,Резюме, Summary,Резюме,
Sunday,Воскресенье, Sunday,Воскресенье,
@@ -445,7 +445,7 @@ Append To can be one of {0},Добавить к может быть одним
Append To is mandatory for incoming mails,Добавить к является обязательным для входящих сообщений, Append To is mandatory for incoming mails,Добавить к является обязательным для входящих сообщений,
"Append as communication against this DocType (must have fields, ""Status"", ""Subject"")","Добавить как коммуникацию для этого DocType (должен иметь поля, ""Статус"", ""Тема"")", "Append as communication against this DocType (must have fields, ""Status"", ""Subject"")","Добавить как коммуникацию для этого DocType (должен иметь поля, ""Статус"", ""Тема"")",
Applicable Document Types,Применимые типы документов, Applicable Document Types,Применимые типы документов,
Apply,Подать заявление,
Apply,Применить,
Apply Strict User Permissions,Применение строгих пользовательских разрешений, Apply Strict User Permissions,Применение строгих пользовательских разрешений,
Apply To All Document Types,Применить ко всем типам документов, Apply To All Document Types,Применить ко всем типам документов,
Apply this rule if the User is the Owner,"Применить это правило, если пользователь является владелец", Apply this rule if the User is the Owner,"Применить это правило, если пользователь является владелец",
@@ -679,8 +679,8 @@ Client Information,Информация о клиенте,
Client Script,Скрипт клиента, Client Script,Скрипт клиента,
Client URLs,URL-адреса клиентов, Client URLs,URL-адреса клиентов,
Client side script extensions in Javascript,Расширения клиентский сценарий в Javascript, Client side script extensions in Javascript,Расширения клиентский сценарий в Javascript,
Collapsible,Складной,
Collapsible Depends On,Складные Зависит от,
Collapsible,Сворачиваемый,
Collapsible Depends On,Сворачиваемый - зависит от,
Column,Колонка, Column,Колонка,
Column <b>{0}</b> already exist.,Столбец <b>{0}</b> уже существует., Column <b>{0}</b> already exist.,Столбец <b>{0}</b> уже существует.,
Column Break,Разрыв столбца, Column Break,Разрыв столбца,
@@ -706,7 +706,8 @@ Compiled Successfully,Успешно скомпилировано,
Complete By,Завершить до, Complete By,Завершить до,
Complete Registration,Полная регистрация, Complete Registration,Полная регистрация,
Complete Setup,Завершение установки, Complete Setup,Завершение установки,
Completed By,Завершено,
Completed By,Завершил(а),
Completed On,Завершено,
Compose Email,Написать письмо, Compose Email,Написать письмо,
Condition Detail,Детализация условий, Condition Detail,Детализация условий,
Conditions,Условия, Conditions,Условия,
@@ -755,8 +756,6 @@ Created Custom Field {0} in {1},Дата создания настраиваем
Created On,Дата создания, Created On,Дата создания,
Criticism,Критика, Criticism,Критика,
Criticize,Критиковать, Criticize,Критиковать,
Ctrl + Down,Ctrl + Down,
Ctrl + Up,Ctrl + Up,
Ctrl+Enter to add comment,"Ctrl+Enter, чтобы добавить комментарий", Ctrl+Enter to add comment,"Ctrl+Enter, чтобы добавить комментарий",
Currency Name,Название валюты, Currency Name,Название валюты,
Currency Precision,Точность валюты, Currency Precision,Точность валюты,
@@ -879,7 +878,7 @@ Disable SMTP server authentication,Отключить аутентификаци
Disable Sidebar Stats,Отключить статистику боковой панели, Disable Sidebar Stats,Отключить статистику боковой панели,
Disable Signup,Отключение Регистрация, Disable Signup,Отключение Регистрация,
Disable Standard Email Footer,Отключить стандартный нижний колонтитул электронной почты, Disable Standard Email Footer,Отключить стандартный нижний колонтитул электронной почты,
Discard,Отбросить,
Discard,Отменить,
Display,Показать, Display,Показать,
Display Depends On,Показание зависит от, Display Depends On,Показание зависит от,
Do not allow user to change after set the first time,Не позволяйте пользователю изменять после установить в первый раз, Do not allow user to change after set the first time,Не позволяйте пользователю изменять после установить в первый раз,
@@ -1120,11 +1119,11 @@ First Transaction,Первая сделка,
First data column must be blank.,Первая колонка данных должна быть пустой., First data column must be blank.,Первая колонка данных должна быть пустой.,
First set the name and save the record.,Сначала задайте имя и сохраните запись., First set the name and save the record.,Сначала задайте имя и сохраните запись.,
Flag,Флаг, Flag,Флаг,
Float,Сплавы,
Float Precision,Float Precision,
Fold,Сложить,
Fold can not be at the end of the form,Fold не может быть в конце виде,
Fold must come before a Section Break,Сложите должны прийти до перерыва раздел,
Float,Дробное,
Float Precision,Плавающая точность,
Fold,Сворачиваемое,
Fold can not be at the end of the form,Сворачиваемое поле не может быть в конце формы,
Fold must come before a Section Break,Сворачиваемое должно идти до разрыва раздел,
Folder,Папка, Folder,Папка,
Folder name should not include '/' (slash),Имя папки не должно включать «/» (косая черта), Folder name should not include '/' (slash),Имя папки не должно включать «/» (косая черта),
Folder {0} is not empty,Папка {0} не пуста, Folder {0} is not empty,Папка {0} не пуста,
@@ -1240,15 +1239,12 @@ Home Settings,Домашние настройки,
Home/Test Folder 1,Главная/Тестовая Папка 1, Home/Test Folder 1,Главная/Тестовая Папка 1,
Home/Test Folder 1/Test Folder 3,Главная/Тестовая Папка 1/Тестовая Папка 3, Home/Test Folder 1/Test Folder 3,Главная/Тестовая Папка 1/Тестовая Папка 3,
Home/Test Folder 2,Главная/Тестовая Папка 2, Home/Test Folder 2,Главная/Тестовая Папка 2,
Host,Host,
Hostname,Hostname,
"How should this currency be formatted? If not set, will use system defaults","Как следует отображать числа в этой валюте? Если не указано, то будут использоваться системные значения", "How should this currency be formatted? If not set, will use system defaults","Как следует отображать числа в этой валюте? Если не указано, то будут использоваться системные значения",
I found these: ,Я нашел следующее:,
I found these: ,Я нашел это: ,
ID,ID, ID,ID,
ID (name) of the entity whose property is to be set,"ID (имя) лица, имущество которого должно быть установлено", ID (name) of the entity whose property is to be set,"ID (имя) лица, имущество которого должно быть установлено",
Icon will appear on the button,Иконка появится на кнопке, Icon will appear on the button,Иконка появится на кнопке,
Identity Details,Сведения о личности, Identity Details,Сведения о личности,
Idx,Idx,
"If Apply Strict User Permission is checked and User Permission is defined for a DocType for a User, then all the documents where value of the link is blank, will not be shown to that User","Если флажок Apply Strict User Permission установлен, а для пользователя DocType для пользователя задано разрешение пользователя, тогда все документы, где значение ссылки пустым, не будут показаны этому пользователю", "If Apply Strict User Permission is checked and User Permission is defined for a DocType for a User, then all the documents where value of the link is blank, will not be shown to that User","Если флажок Apply Strict User Permission установлен, а для пользователя DocType для пользователя задано разрешение пользователя, тогда все документы, где значение ссылки пустым, не будут показаны этому пользователю",
If Checked workflow status will not override status in list view,"Если установлен флажок, статус процесса не будет отменять статус в журнале", If Checked workflow status will not override status in list view,"Если установлен флажок, статус процесса не будет отменять статус в журнале",
If Owner,Если владелец, If Owner,Если владелец,
@@ -1303,7 +1299,7 @@ In Filter,В фильтрe,
In Global Search,В глобальном поиске, In Global Search,В глобальном поиске,
In Grid View,В табличном виде, In Grid View,В табличном виде,
In Hours,В час, In Hours,В час,
In List View,В виде списка,
In List View,Отображать в списке,
In Preview,В предварительном просмотре, In Preview,В предварительном просмотре,
In Reply To,В ответ на, In Reply To,В ответ на,
In Standard Filter,В стандартный фильтр, In Standard Filter,В стандартный фильтр,
@@ -1322,7 +1318,7 @@ Index,Индекс,
Indicator,Индикатор, Indicator,Индикатор,
Info,Информация, Info,Информация,
Info:,Информация:, Info:,Информация:,
Initial Sync Count,Первоначальная синхронизация Count,
Initial Sync Count,Первоначальная синхронизация,
InnoDB,InnoDB, InnoDB,InnoDB,
Insert Above,Вставить сверху, Insert Above,Вставить сверху,
Insert After,Вставить после, Insert After,Вставить после,
@@ -1333,7 +1329,7 @@ Insert Column Before {0},Вставить столбец до {0},
Insert Style,Вставьте стиль, Insert Style,Вставьте стиль,
Insert new records,Вставить новые записи, Insert new records,Вставить новые записи,
Instructions Emailed,Инструкции отправлены по электронной почте, Instructions Emailed,Инструкции отправлены по электронной почте,
Insufficient Permission for {0},Недостаточное разрешение для {0},
Insufficient Permission for {0},Недостаточно прав для {0},
Int,Интервал, Int,Интервал,
Integration Request,Интеграция заявки, Integration Request,Интеграция заявки,
Integration Request Service,Интеграция заявки на обслуживание, Integration Request Service,Интеграция заявки на обслуживание,
@@ -1374,33 +1370,30 @@ Invalid recipient address,Неверный адрес получателя,
Invalid {0} condition,Недопустимое условие {0}, Invalid {0} condition,Недопустимое условие {0},
Inverse,Обратный, Inverse,Обратный,
Is,Является, Is,Является,
Is Attachments Folder,Является папкой вложений,
Is Child Table,Является дочерней таблицей,
Is Attachments Folder,Это папка для вложений,
Is Child Table,Это дочерняя таблицей,
Is Custom Field,Это нестандартное поле, Is Custom Field,Это нестандартное поле,
Is First Startup,Первый запуск, Is First Startup,Первый запуск,
Is Folder,Папка,
Is Folder,Это папка,
Is Global,Является глобальным, Is Global,Является глобальным,
Is Globally Pinned,Глобально закреплено, Is Globally Pinned,Глобально закреплено,
Is Home Folder,Является корневой папкой, Is Home Folder,Является корневой папкой,
Is Mandatory Field,Является обязательным полем, Is Mandatory Field,Является обязательным полем,
Is Pinned,Прикреплено, Is Pinned,Прикреплено,
Is Primary Contact,Основной контакт,
Is Primary Contact,Это основной контакт,
Is Private,Является приватным, Is Private,Является приватным,
Is Published Field,Есть Опубликовано поле,
Is Published Field must be a valid fieldname,Опубликовано Поле должно быть действительным имя_полем,
Is Published Field,Это опубликованое поле,
Is Published Field must be a valid fieldname,Опубликованое роле должно быть допустимым именем поля,
Is Single,Единственный, Is Single,Единственный,
Is Spam,Спам,
Is Standard,Стандартный отчёт,
Is Spam,Это спам,
Is Standard,Это стандартный отчёт,
Is Submittable,Подлежит исполнению, Is Submittable,Подлежит исполнению,
Is Table,Является таблицей, Is Table,Является таблицей,
Is Your Company Address,Является адресом вашей компании, Is Your Company Address,Является адресом вашей компании,
It is risky to delete this file: {0}. Please contact your System Manager.,"Рискованно удалять этот файл: {0}. Пожалуйста, обратитесь к менеджеру системы.", It is risky to delete this file: {0}. Please contact your System Manager.,"Рискованно удалять этот файл: {0}. Пожалуйста, обратитесь к менеджеру системы.",
Item cannot be added to its own descendents,Продукт не может быть добавлен к своим подпродуктам, Item cannot be added to its own descendents,Продукт не может быть добавлен к своим подпродуктам,
JS,JS,
JSON,JSON,
JavaScript Format: frappe.query_reports['REPORTNAME'] = {},Формат JavaScript: frappe.query_reports ['REPORTNAME'] = {}, JavaScript Format: frappe.query_reports['REPORTNAME'] = {},Формат JavaScript: frappe.query_reports ['REPORTNAME'] = {},
Javascript to append to the head section of the page.,Javascript для добавления к головной части страницы., Javascript to append to the head section of the page.,Javascript для добавления к головной части страницы.,
Jinja,Jinja,
John Doe,Джон Доу, John Doe,Джон Доу,
Kanban,Канбан, Kanban,Канбан,
Kanban Board Column,Колонка канбан-доски, Kanban Board Column,Колонка канбан-доски,
@@ -1480,7 +1473,7 @@ List,Список,
List Filter,Фильтр списка, List Filter,Фильтр списка,
List View Setting,Настройка просмотра списка, List View Setting,Настройка просмотра списка,
List a document type,Перечислите тип документа, List a document type,Перечислите тип документа,
"List as [{""label"": _(""Jobs""), ""route"":""jobs""}]","Список как [{""Ярлык"": _(""Работы""), ""маршруты"":""работы""}]",
"List as [{""label"": _(""Jobs""), ""route"":""jobs""}]","Список как [{""Метка"": _(""Работы""), ""маршруты"":""работы""}]",
List of backups available for download,"Список резервных копий, доступных для загрузки", List of backups available for download,"Список резервных копий, доступных для загрузки",
List of patches executed,Список выполненных патчей, List of patches executed,Список выполненных патчей,
List of themes for Website.,Список тем для сайта., List of themes for Website.,Список тем для сайта.,
@@ -1513,7 +1506,7 @@ Long Text,Длинный текст,
Looks like something is wrong with this site's Paypal configuration.,"Похоже, что что-то не так с конфигурацией Paypal этого сайта.", Looks like something is wrong with this site's Paypal configuration.,"Похоже, что что-то не так с конфигурацией Paypal этого сайта.",
Looks like something is wrong with this site's payment gateway configuration. No payment has been made.,"Похоже, что-то не так с конфигурацией платежного шлюза этого сайта. Платеж не был выполнен.", Looks like something is wrong with this site's payment gateway configuration. No payment has been made.,"Похоже, что-то не так с конфигурацией платежного шлюза этого сайта. Платеж не был выполнен.",
"Looks like something went wrong during the transaction. Since we haven't confirmed the payment, Paypal will automatically refund you this amount. If it doesn't, please send us an email and mention the Correlation ID: {0}.","Похоже, что-то пошло не так во время транзакции. Поскольку мы не подтвердили платеж, Paypal автоматически вернет вам эту сумму. Если это не так, отправьте нам электронное письмо и укажите идентификатор корреляции: {0}.", "Looks like something went wrong during the transaction. Since we haven't confirmed the payment, Paypal will automatically refund you this amount. If it doesn't, please send us an email and mention the Correlation ID: {0}.","Похоже, что-то пошло не так во время транзакции. Поскольку мы не подтвердили платеж, Paypal автоматически вернет вам эту сумму. Если это не так, отправьте нам электронное письмо и укажите идентификатор корреляции: {0}.",
Madam,Госпожа,
Madam,Мадам,
Main Section,Основной раздел, Main Section,Основной раздел,
"Make ""name"" searchable in Global Search","Индексировать ""name"" для глобального поиска", "Make ""name"" searchable in Global Search","Индексировать ""name"" для глобального поиска",
Make use of longer keyboard patterns,Используйте более длинных моделей клавиатуры, Make use of longer keyboard patterns,Используйте более длинных моделей клавиатуры,
@@ -1540,7 +1533,7 @@ Max Value,Макс. значение,
Max width for type Currency is 100px in row {0},Макс. ширина для типа валюты 100px в строке {0}, Max width for type Currency is 100px in row {0},Макс. ширина для типа валюты 100px в строке {0},
Maximum Attachment Limit for this record reached.,Достигнут предел вложений для этой записи., Maximum Attachment Limit for this record reached.,Достигнут предел вложений для этой записи.,
Maximum {0} rows allowed,Макс. {0} строк разрешено, Maximum {0} rows allowed,Макс. {0} строк разрешено,
"Meaning of Submit, Cancel, Amend","Значение Провести, Отменить, Изменить",
"Meaning of Submit, Cancel, Amend","Значение Подписать, Отменить, Изменить",
Mention transaction completion page URL,URL-ссылка на страницу-упоминание о завершении транзакции, Mention transaction completion page URL,URL-ссылка на страницу-упоминание о завершении транзакции,
Mentions,Упоминания, Mentions,Упоминания,
Menu,Меню, Menu,Меню,
@@ -1606,14 +1599,14 @@ New Chat,Новый чат,
New Comment on {0}: {1},Новый комментарий к {0}: {1}, New Comment on {0}: {1},Новый комментарий к {0}: {1},
New Connection,Новое соединение, New Connection,Новое соединение,
New Custom Print Format,Новый пользовательский печатный бланк, New Custom Print Format,Новый пользовательский печатный бланк,
New Email,Новая электронная почта,
New Email,Новое письмо,
New Email Account,Новый аккаунт электронной почты, New Email Account,Новый аккаунт электронной почты,
New Event,Новое событие, New Event,Новое событие,
New Folder,Новая папка, New Folder,Новая папка,
New Kanban Board,Новая панель канбан, New Kanban Board,Новая панель канбан,
New Message from Website Contact Page,Новое сообщение с формы обратной связи на сайте, New Message from Website Contact Page,Новое сообщение с формы обратной связи на сайте,
New Name,Новое имя, New Name,Новое имя,
New Newsletter,Новый бюллетень,
New Newsletter,Новая новость,
New Password,Новый пароль, New Password,Новый пароль,
New Password Required.,Требуется новый пароль., New Password Required.,Требуется новый пароль.,
New Print Format Name,Название нового печатного бланка, New Print Format Name,Название нового печатного бланка,
@@ -1672,8 +1665,8 @@ No template found at path: {0},Нет шаблона по адресу: {0},
No {0} found,{0} не найдено, No {0} found,{0} не найдено,
No {0} mail,Нет {0} почта, No {0} mail,Нет {0} почта,
No {0} permission,Нет {0} разрешение, No {0} permission,Нет {0} разрешение,
None: End of Workflow,Ни один: Конец потока,
Not Allowed: Disabled User,Не разрешено: отключен пользователь,
None: End of Workflow,Нет: конец рабочего процесса,
Not Allowed: Disabled User,Не разрешено: пользователь отключен,
Not Ancestors Of,Не предки, Not Ancestors Of,Не предки,
Not Descendants Of,Не потомки, Not Descendants Of,Не потомки,
Not Equals,Не равно, Not Equals,Не равно,
@@ -1688,7 +1681,7 @@ Not a valid Comma Separated Value (CSV File),"Не является допуст
Not a valid User Image.,Недействительный изображение пользователя., Not a valid User Image.,Недействительный изображение пользователя.,
Not a valid Workflow Action,Недоступное действие рабочего-процесса, Not a valid Workflow Action,Недоступное действие рабочего-процесса,
Not a valid user,Не является действительным пользователем, Not a valid user,Не является действительным пользователем,
Not a zip file,Не zip файл,
Not a zip file,Не является zip файлом,
Not allowed for {0}: {1},Не разрешено для {0}: {1}, Not allowed for {0}: {1},Не разрешено для {0}: {1},
Not allowed for {0}: {1} in Row {2}. Restricted field: {3},Недопустимо для {0}: {1} в строке {2}. Запрещенное поле: {3}, Not allowed for {0}: {1} in Row {2}. Restricted field: {3},Недопустимо для {0}: {1} в строке {2}. Запрещенное поле: {3},
Not allowed for {0}: {1}. Restricted field: {2},Не допускается для {0}: {1}. Запрещенное поле: {2}, Not allowed for {0}: {1}. Restricted field: {2},Не допускается для {0}: {1}. Запрещенное поле: {2},
@@ -1732,11 +1725,11 @@ OTP Secret has been reset. Re-registration will be required on next login.,OTP S
OTP secret can only be reset by the Administrator.,Секрет OTP может быть сброшен администратором., OTP secret can only be reset by the Administrator.,Секрет OTP может быть сброшен администратором.,
Office,Офис, Office,Офис,
Office 365,Офис 365, Office 365,Офис 365,
Old Password,Старый Пароль,
Old Password Required.,Требуется старый пароль.,
Old Password,Старый пароль,
Old Password Required.,Старый пароль обязателен.,
Older backups will be automatically deleted,Более старые резервные копии будут автоматически удалены, Older backups will be automatically deleted,Более старые резервные копии будут автоматически удалены,
"On {0}, {1} wrote:","На {0}, {1} писал:", "On {0}, {1} wrote:","На {0}, {1} писал:",
"Once submitted, submittable documents cannot be changed. They can only be Cancelled and Amended.",После отправки поданные документы не могут быть изменены. Они могут быть только отменены и исправлены.,
"Once submitted, submittable documents cannot be changed. They can only be Cancelled and Amended.",После отправки подписанные документы не могут быть изменены. Они могут быть только отменены и исправлены.,
"Once you have set this, the users will only be able access documents (eg. Blog Post) where the link exists (eg. Blogger).","После такой установки, пользователи получат доступ только к документам (например, сообщениям в блоге), связанным с этими разрешениями пользователя (например, блоггера).", "Once you have set this, the users will only be able access documents (eg. Blog Post) where the link exists (eg. Blogger).","После такой установки, пользователи получат доступ только к документам (например, сообщениям в блоге), связанным с этими разрешениями пользователя (например, блоггера).",
One Last Step,Последний шаг, One Last Step,Последний шаг,
One Time Password (OTP) Registration Code from {},Одноразовый пароль (OTP) Регистрационный код от {}, One Time Password (OTP) Registration Code from {},Одноразовый пароль (OTP) Регистрационный код от {},
@@ -1760,7 +1753,7 @@ Open a dialog with mandatory fields to create a new record quickly,"Открой
Open a module or tool,Открыть модуль или инструмент, Open a module or tool,Открыть модуль или инструмент,
Open your authentication app on your mobile phone.,Откройте приложение для проверки подлинности на своем мобильном телефоне., Open your authentication app on your mobile phone.,Откройте приложение для проверки подлинности на своем мобильном телефоне.,
Open {0},Открыть {0}, Open {0},Открыть {0},
Opened,Открыт,
Opened,Открыть,
Operator must be one of {0},Оператор должен быть одним из {0}, Operator must be one of {0},Оператор должен быть одним из {0},
Option 1,Опция 1, Option 1,Опция 1,
Option 2,Опция 2, Option 2,Опция 2,
@@ -1778,9 +1771,7 @@ Org History Heading,Org История Заголовок,
Orientation,Ориентация, Orientation,Ориентация,
Original Value,Первоначальная стоимость, Original Value,Первоначальная стоимость,
Outgoing email account not correct,Исходящая учетная запись электронной почты не верна, Outgoing email account not correct,Исходящая учетная запись электронной почты не верна,
Outlook.com,Outlook.com,
Output,Вывод, Output,Вывод,
PDF,PDF,
PDF Page Size,Размер PDF страницы, PDF Page Size,Размер PDF страницы,
PDF Settings,Настройки PDF, PDF Settings,Настройки PDF,
PDF generation failed,Не удалось сгенерировать PDF-файл, PDF generation failed,Не удалось сгенерировать PDF-файл,
@@ -1790,17 +1781,17 @@ Page HTML,Страница HTML,
Page Length,Длина страницы, Page Length,Длина страницы,
Page Name,Имя страницы, Page Name,Имя страницы,
Page Settings,Настройки страницы, Page Settings,Настройки страницы,
Page has expired!,Страница просрочена!,
Page has expired!,Срок действия страницы истек!,
Page not found,Страница не найдена, Page not found,Страница не найдена,
Page to show on the website\n,Страница для показа на сайте, Page to show on the website\n,Страница для показа на сайте,
Pages in Desk (place holders),Страницы-заглушки, Pages in Desk (place holders),Страницы-заглушки,
Parent,Родитель, Parent,Родитель,
Parent Error Snapshot,Родитель снимка ошибки, Parent Error Snapshot,Родитель снимка ошибки,
Parent Label,Родительская метка, Parent Label,Родительская метка,
Parent Table,Родитель Таблица,
Parent Table,Родительская таблица,
Parent is required to get child table data,Родитель обязан получать данные дочерней таблицы, Parent is required to get child table data,Родитель обязан получать данные дочерней таблицы,
Parent is the name of the document to which the data will get added to.,"Родитель - это имя документа, к которому будут добавлены данные.", Parent is the name of the document to which the data will get added to.,"Родитель - это имя документа, к которому будут добавлены данные.",
Partial Success,Частичный успех,
Partial Success,Выполнено не полностью,
Partially Successful,Частично успешный, Partially Successful,Частично успешный,
Participants,Участники, Participants,Участники,
Passive,Пассивный, Passive,Пассивный,
@@ -1903,14 +1894,14 @@ Please specify which date field must be checked,"Просьба уточнить
Please specify which value field must be checked,"Просьба уточнить, какие значения поля должны быть проверены", Please specify which value field must be checked,"Просьба уточнить, какие значения поля должны быть проверены",
Please try again,"Пожалуйста, попробуйте еще раз", Please try again,"Пожалуйста, попробуйте еще раз",
Please verify your Email Address,"Пожалуйста, подтвердите свой адрес электронной почты", Please verify your Email Address,"Пожалуйста, подтвердите свой адрес электронной почты",
Point Allocation Periodicity,Периодичность распределения точек,
Point Allocation Periodicity,Периодичность распределения баллов,
Points,Баллы, Points,Баллы,
Points Given,Баллы засчитаны, Points Given,Баллы засчитаны,
Port,Порт, Port,Порт,
Portal Menu,Меню портала, Portal Menu,Меню портала,
Portal Menu Item,Портал Пункт меню,
Portal Menu Item,Пункт меню портала,
Post,Опубликовать, Post,Опубликовать,
Post Comment,Оставьте комментарий,
Post Comment,Оставить комментарий,
Postal,Почтовый, Postal,Почтовый,
Postal Code,Почтовый индекс, Postal Code,Почтовый индекс,
Postprocess Method,Метод постпроцесса, Postprocess Method,Метод постпроцесса,
@@ -1966,15 +1957,6 @@ Provider Name,Имя поставщика,
Public Key,Открытый ключ, Public Key,Открытый ключ,
Publishable Key,Ключ для публикации, Publishable Key,Ключ для публикации,
Published On,Опубликовано на, Published On,Опубликовано на,
Pull,Тянуть,
Pull Failed,Не удалось выполнить Pull,
Pull Insert,Вставить вкладыш,
Pull Update,Pull Update,
Push,От себя,
Push Delete,Нажмите Удалить,
Push Failed,Ошибка отправки,
Push Insert,Push Insert,
Push Update,Push Update,
Python Module,Модуль Python, Python Module,Модуль Python,
Pyver,Pyver, Pyver,Pyver,
QR Code,QR код, QR Code,QR код,
@@ -1986,7 +1968,7 @@ Query,Запрос,
Query Report,Отчёт-выборка, Query Report,Отчёт-выборка,
Query must be a SELECT,Запрос должен быть ВЫБОР, Query must be a SELECT,Запрос должен быть ВЫБОР,
Queue should be one of {0},Очередь должна быть одной из {0}, Queue should be one of {0},Очередь должна быть одной из {0},
Queued for backup. It may take a few minutes to an hour.,Queued для резервного копирования. Это может занять несколько минут до часа.,
Queued for backup. It may take a few minutes to an hour.,В очереди для резервного копирования. Это может занять от несколько минут до часа.,
Queued for backup. You will receive an email with the download link,Очередь для резервного копирования. Вы получите электронное письмо с ссылкой для загрузки, Queued for backup. You will receive an email with the download link,Очередь для резервного копирования. Вы получите электронное письмо с ссылкой для загрузки,
Quick Help for Setting Permissions,Быстрая помощь при настройки прав доступа, Quick Help for Setting Permissions,Быстрая помощь при настройки прав доступа,
Rating: ,Рейтинг: , Rating: ,Рейтинг: ,
@@ -2091,8 +2073,8 @@ Retake,пересдавать,
Retry,Повторить, Retry,Повторить,
Return to the Verification screen and enter the code displayed by your authentication app,"Вернитесь на экран проверки и введите код, отображаемый приложением для аутентификации.", Return to the Verification screen and enter the code displayed by your authentication app,"Вернитесь на экран проверки и введите код, отображаемый приложением для аутентификации.",
Reverse Icon Color,Обратный цвет значка, Reverse Icon Color,Обратный цвет значка,
Revert,Вернуть,
Revert Of,Вернуть из,
Revert,Возврат,
Revert Of,Возвращенно из,
Reverted,Отменено, Reverted,Отменено,
Review Level,Уровень обзора, Review Level,Уровень обзора,
Review Levels,Уровни обзора, Review Levels,Уровни обзора,
@@ -2101,9 +2083,8 @@ Reviews,Отзывы,
Revoke,Аннулировать, Revoke,Аннулировать,
Revoked,Аннулировано, Revoked,Аннулировано,
Rich Text,Форматированный текст, Rich Text,Форматированный текст,
Robots.txt,Robots.txt,
Role Name,Имя роли,
Role Permission for Page and Report,Роль Разрешение на страницу и отчет,
Role Name,Название роли,
Role Permission for Page and Report,Разрешение роли для страницы и отчета,
Role Permissions,Разрешения роли, Role Permissions,Разрешения роли,
Role Profile,Профиль ролей, Role Profile,Профиль ролей,
Role and Level,Роль и уровень, Role and Level,Роль и уровень,
@@ -2198,7 +2179,7 @@ Select Print Format,Выберите бланк для печати,
Select Print Format to Edit,Выберите печатный бланк для редактирования, Select Print Format to Edit,Выберите печатный бланк для редактирования,
Select Role,Выберите роль, Select Role,Выберите роль,
Select Table Columns for {0},Выберите столбцы таблицы для {0}, Select Table Columns for {0},Выберите столбцы таблицы для {0},
Select Your Region,Выберите регион,
Select Your Region,Выберите ваш регион,
Select a Brand Image first.,Выберите бренд изображение в первую очередь., Select a Brand Image first.,Выберите бренд изображение в первую очередь.,
Select a DocType to make a new format,"Выберите DOCTYPE, чтобы сделать новый бланк", Select a DocType to make a new format,"Выберите DOCTYPE, чтобы сделать новый бланк",
Select a chat to start messaging.,"Выберите чат, чтобы начать обмен сообщениями.", Select a chat to start messaging.,"Выберите чат, чтобы начать обмен сообщениями.",
@@ -2234,7 +2215,6 @@ Send only if there is any data,"Отправить только если ест
Send unsubscribe message in email,Отправить сообщение об отказе от подписки на электронную почту, Send unsubscribe message in email,Отправить сообщение об отказе от подписки на электронную почту,
Sender,Отправитель, Sender,Отправитель,
Sender Email,Электронная почта отправителя, Sender Email,Электронная почта отправителя,
Sendgrid,Sendgrid,
Sent Read Receipt,Отправлять уведомление о прочтении, Sent Read Receipt,Отправлять уведомление о прочтении,
Sent or Received,Отправлено или получено, Sent or Received,Отправлено или получено,
Sent/Received Email,Отправлено/Получено письмо, Sent/Received Email,Отправлено/Получено письмо,
@@ -2279,8 +2259,8 @@ Setup Reports to be emailed at regular intervals,Настройка регуля
Share,Поделиться, Share,Поделиться,
Share URL,Поделиться URL, Share URL,Поделиться URL,
Share With,Поделиться с, Share With,Поделиться с,
Share this document with,Поделитесь этот документ с,
Share {0} with,Поделиться {0},
Share this document with,Поделиться этим документом с,
Share {0} with,Поделиться {0} с,
Shared,Общий, Shared,Общий,
Shared With,Совместно с, Shared With,Совместно с,
Shared with everyone,Общий для всех, Shared with everyone,Общий для всех,
@@ -2325,8 +2305,6 @@ Single Post (article).,Один пост(статья).,
Single Types have only one record no tables associated. Values are stored in tabSingles,"Холост Типы нет только одна запись не таблицы, связанные. Значения сохраняются в tabSingles", Single Types have only one record no tables associated. Values are stored in tabSingles,"Холост Типы нет только одна запись не таблицы, связанные. Значения сохраняются в tabSingles",
Skip Authorization,Пропустить авторизацию, Skip Authorization,Пропустить авторизацию,
Skip rows with errors,Пропустить строки с ошибками, Skip rows with errors,Пропустить строки с ошибками,
Skype,Skype,
Slack,Slack,
Slack Channel,Slack канал, Slack Channel,Slack канал,
Slack Webhook Error,Slack Webhook ошибка, Slack Webhook Error,Slack Webhook ошибка,
Slack Webhook URL,Неверный URL веб-хостинга, Slack Webhook URL,Неверный URL веб-хостинга,
@@ -2350,14 +2328,13 @@ Something went wrong while generating dropbox access token. Please check error l
Sorry! I could not find what you were looking for.,"Извините! Я не мог найти то, что вы ищете.", Sorry! I could not find what you were looking for.,"Извините! Я не мог найти то, что вы ищете.",
Sorry! Sharing with Website User is prohibited.,Извините! Поделиться с сайта Пользователю запрещается., Sorry! Sharing with Website User is prohibited.,Извините! Поделиться с сайта Пользователю запрещается.,
Sorry! User should have complete access to their own record.,Извините! Пользователь должен иметь полный доступ к своей записи., Sorry! User should have complete access to their own record.,Извините! Пользователь должен иметь полный доступ к своей записи.,
Sorry! You are not permitted to view this page.,Извините! Вам не разрешается для просмотра этой страницы.,
Sorry! You are not permitted to view this page.,Извините! У вас нет разрешений для просмотра этой страницы.,
"Sorry, you're not authorized.","Извините, вы не авторизованы.", "Sorry, you're not authorized.","Извините, вы не авторизованы.",
Sort Field,Сортировать поле, Sort Field,Сортировать поле,
Sort Order,Порядок сортировки, Sort Order,Порядок сортировки,
Sort field {0} must be a valid fieldname,Сортировка поля {0} должен быть действительным имя_поля, Sort field {0} must be a valid fieldname,Сортировка поля {0} должен быть действительным имя_поля,
Source Text,Исходный текст, Source Text,Исходный текст,
Spam,Спам, Spam,Спам,
SparkPost,SparkPost,
Special Characters are not allowed,Спецсимволы не допустимы, Special Characters are not allowed,Спецсимволы не допустимы,
"Standard DocType cannot have default print format, use Customize Form","Стандартный DocType не может иметь формат печати по умолчанию, используйте Настроить форму", "Standard DocType cannot have default print format, use Customize Form","Стандартный DocType не может иметь формат печати по умолчанию, используйте Настроить форму",
Standard Print Format cannot be updated,Стандартный печатный бланк не может быть обновлен, Standard Print Format cannot be updated,Стандартный печатный бланк не может быть обновлен,
@@ -2371,12 +2348,11 @@ Start Date Field,Поле начальной даты,
Start a conversation.,Начните разговор., Start a conversation.,Начните разговор.,
Start entering data below this line,Начните вводить данные ниже этой линии, Start entering data below this line,Начните вводить данные ниже этой линии,
Start new Format,Начать новую Формат, Start new Format,Начать новую Формат,
StartTLS,StartTLS,
Started,Начал, Started,Начал,
Starting Frappe ...,Запуск Frappé ..., Starting Frappe ...,Запуск Frappé ...,
Starts on,Начало, Starts on,Начало,
States,Статусы,
"States for workflow (e.g. Draft, Approved, Cancelled).","Статусы бизнес-процесса (например: черновик, утверждён, отменён).",
States,Состояния,
"States for workflow (e.g. Draft, Approved, Cancelled).","Состояния рабочего-процесса (например: черновик, утверждён, отменён).",
Static Parameters,Статические параметры, Static Parameters,Статические параметры,
Stats based on last month's performance (from {0} to {1}),Статистика на основе результатов прошлого месяца (от {0} до {1}), Stats based on last month's performance (from {0} to {1}),Статистика на основе результатов прошлого месяца (от {0} до {1}),
Stats based on last week's performance (from {0} to {1}),Статистика на основе результатов прошлой недели (от {0} до {1}), Stats based on last week's performance (from {0} to {1}),Статистика на основе результатов прошлой недели (от {0} до {1}),
@@ -2396,7 +2372,7 @@ Subdomain,Субдомен,
Subject Field,Поле темы, Subject Field,Поле темы,
Submit after importing,Отправить после импорта, Submit after importing,Отправить после импорта,
Submit an Issue,Отправить вопрос, Submit an Issue,Отправить вопрос,
Submit this document to confirm,Провести этот документ для подтверждения,
Submit this document to confirm,Подписать этот документ для подтверждения,
Submit {0} documents?,Отправить {0} документы?, Submit {0} documents?,Отправить {0} документы?,
Submitting {0},Помещение {0}, Submitting {0},Помещение {0},
Submitted Document cannot be converted back to draft. Transition row {0},Проведенный Документ не может быть преобразован обратно в проект. Переходная строка {0}, Submitted Document cannot be converted back to draft. Transition row {0},Проведенный Документ не может быть преобразован обратно в проект. Переходная строка {0},
@@ -2437,8 +2413,6 @@ Team Members,Члены команды,
Team Members Heading,Члены команды Возглавлять, Team Members Heading,Члены команды Возглавлять,
Temporarily Disabled,Временно отключен, Temporarily Disabled,Временно отключен,
Test Email Address,Проверить адрес электронной почты, Test Email Address,Проверить адрес электронной почты,
Test Runner,Тест Runner,
Test_Folder,Test_Folder,
Text,Текст, Text,Текст,
Text Align,Выравнивание текста, Text Align,Выравнивание текста,
Text Color,Цвет текста, Text Color,Цвет текста,
@@ -2463,8 +2437,8 @@ The system provides many pre-defined roles. You can add new roles to set finer p
The user from this field will be rewarded points,Пользователь из этого поля будет вознагражден баллами, The user from this field will be rewarded points,Пользователь из этого поля будет вознагражден баллами,
Theme,Тема, Theme,Тема,
Theme URL,URL темы, Theme URL,URL темы,
There can be only one Fold in a form,Там может быть только один Fold в виде,
There is an error in your Address Template {0},Существует ошибка в адресной Шаблон {0},
There can be only one Fold in a form,В форме может быть только один Fold,
There is an error in your Address Template {0},Ошибка в вашем шаблоне адреса {0},
There is no data to be exported,Нет данных для экспорта, There is no data to be exported,Нет данных для экспорта,
There is some problem with the file url: {0},Существует некоторая проблема с файловой URL: {0}, There is some problem with the file url: {0},Существует некоторая проблема с файловой URL: {0},
There must be atleast one permission rule.,Там должно быть по крайней мере один правило разрешения., There must be atleast one permission rule.,Там должно быть по крайней мере один правило разрешения.,
@@ -2502,7 +2476,7 @@ This is similar to a commonly used password.,Это похоже на обычн
This is the template file generated with only the rows having some error. You should use this file for correction and import.,"Это файл шаблона, сгенерированный только строками с некоторой ошибкой. Вы должны использовать этот файл для исправления и импорта.", This is the template file generated with only the rows having some error. You should use this file for correction and import.,"Это файл шаблона, сгенерированный только строками с некоторой ошибкой. Вы должны использовать этот файл для исправления и импорта.",
This link has already been activated for verification.,Эта ссылка уже была активирована для проверки., This link has already been activated for verification.,Эта ссылка уже была активирована для проверки.,
This link is invalid or expired. Please make sure you have pasted correctly.,"Эта ссылка является недействительным или истек. Пожалуйста, убедитесь, что вы вставили правильно.", This link is invalid or expired. Please make sure you have pasted correctly.,"Эта ссылка является недействительным или истек. Пожалуйста, убедитесь, что вы вставили правильно.",
This may get printed on multiple pages,Это может быть напечатано на нескольких страницах,
This may get printed on multiple pages,Это будет напечатано на нескольких страницах,
This month,Этот месяц, This month,Этот месяц,
This query style is discontinued,Этот стиль запроса прекращен, This query style is discontinued,Этот стиль запроса прекращен,
This report was generated on {0},Этот отчет был создан в {0}, This report was generated on {0},Этот отчет был создан в {0},
@@ -2511,7 +2485,6 @@ This request has not yet been approved by the user.,Этот запрос еще
This role update User Permissions for a user,Эта роль обновляет разрешения пользователя для пользователя, This role update User Permissions for a user,Эта роль обновляет разрешения пользователя для пользователя,
This will log out {0} from all other devices,Это выведет {0} из всех других устройств, This will log out {0} from all other devices,Это выведет {0} из всех других устройств,
This will permanently remove your data.,Это навсегда удалит ваши данные., This will permanently remove your data.,Это навсегда удалит ваши данные.,
Throttled,Throttled,
Thumbnail URL,Миниатюра URL, Thumbnail URL,Миниатюра URL,
Time Interval,Интервал времени, Time Interval,Интервал времени,
Time Series,Временные ряды, Time Series,Временные ряды,
@@ -2529,7 +2502,7 @@ Timeseries,Временные ряды,
Timestamp,Временная отметка, Timestamp,Временная отметка,
Title Case,Название дела, Title Case,Название дела,
Title Field,Название поля, Title Field,Название поля,
Title Prefix,Название Приставка,
Title Prefix,Название приставки,
Title field must be a valid fieldname,Название поля должно быть допустимым имя_поля, Title field must be a valid fieldname,Название поля должно быть допустимым имя_поля,
To Date Field,Поле даты, To Date Field,Поле даты,
To Do,Список дел, To Do,Список дел,
@@ -2589,7 +2562,7 @@ Type:,Тип:,
UID,UID, UID,UID,
UIDNEXT,UIDNEXT, UIDNEXT,UIDNEXT,
UIDVALIDITY,UIDVALIDITY, UIDVALIDITY,UIDVALIDITY,
UNSEEN,НЕПРОЧИТАННО,
UNSEEN,НЕПРОЧИТАННЫЕ,
UPPER CASE,ВЕРХНИЙ РЕГИСТР, UPPER CASE,ВЕРХНИЙ РЕГИСТР,
"URIs for receiving authorization code once the user allows access, as well as failure responses. Typically a REST endpoint exposed by the Client App.\n<br>e.g. http://hostname//api/method/frappe.www.login.login_via_facebook","Идентификаторы URI для получения кода авторизации, как только пользователь разрешает доступ, а также ответы недостаточность. Как правило, конечная точка REST подвергается Клиентом App. <br> например, HTTP: //hostname//api/method/frappe.www.login.login_via_facebook", "URIs for receiving authorization code once the user allows access, as well as failure responses. Typically a REST endpoint exposed by the Client App.\n<br>e.g. http://hostname//api/method/frappe.www.login.login_via_facebook","Идентификаторы URI для получения кода авторизации, как только пользователь разрешает доступ, а также ответы недостаточность. Как правило, конечная точка REST подвергается Клиентом App. <br> например, HTTP: //hostname//api/method/frappe.www.login.login_via_facebook",
URLs,URL-адрес, URLs,URL-адрес,
@@ -2691,7 +2664,6 @@ Value for {0} cannot be a list,Значение {0} не может быть с
Value missing for,Нет значения для, Value missing for,Нет значения для,
Value too big,Слишком большое значение, Value too big,Слишком большое значение,
Values Changed,Значения изменено, Values Changed,Значения изменено,
Verdana,Verdana,
Verfication Code,Код проверки, Verfication Code,Код проверки,
Verification Link,Ссылка для проверки, Verification Link,Ссылка для проверки,
Verification code has been sent to your registered email address.,Код подтверждения отправлен на ваш адрес электронной почты регистрации., Verification code has been sent to your registered email address.,Код подтверждения отправлен на ваш адрес электронной почты регистрации.,
@@ -2712,7 +2684,7 @@ View document,Просмотр документа,
View report in your browser,Просмотр отчета в вашем браузере, View report in your browser,Просмотр отчета в вашем браузере,
View this in your browser,Просмотреть это в вашем браузере, View this in your browser,Просмотреть это в вашем браузере,
View {0},Просмотреть {0}, View {0},Просмотреть {0},
Viewed By,просмотрены,
Viewed By,Просмотрено,
Visit,Посетите нас по адресу, Visit,Посетите нас по адресу,
Visitor,Посетитель, Visitor,Посетитель,
We have received a request for deletion of {0} data associated with: {1},"Мы получили запрос на удаление {0} данных, связанных с: {1}", We have received a request for deletion of {0} data associated with: {1},"Мы получили запрос на удаление {0} данных, связанных с: {1}",
@@ -2721,7 +2693,7 @@ Web Form,Веб форма,
Web Form Field,Поле веб формы, Web Form Field,Поле веб формы,
Web Form Fields,Поля веб формы, Web Form Fields,Поля веб формы,
Web Page,Веб-страница, Web Page,Веб-страница,
Web Page Link Text,Web Текст Ссылка на страницу,
Web Page Link Text,Текст ссылки веб-страницы,
Web Site,Веб-сайт, Web Site,Веб-сайт,
Web View,Web View, Web View,Web View,
Webhook,Webhook, Webhook,Webhook,
@@ -2765,9 +2737,7 @@ Workflow state represents the current state of a document.,Состояние р
Write,Написать, Write,Написать,
Wrong fieldname <b>{0}</b> in add_fetch configuration of custom script,Неверное имя поля <b>{0}</b> в конфигурации add_fetch пользовательского скрипта, Wrong fieldname <b>{0}</b> in add_fetch configuration of custom script,Неверное имя поля <b>{0}</b> в конфигурации add_fetch пользовательского скрипта,
X Axis Field,Поле оси X, X Axis Field,Поле оси X,
XLSX,XLSX,
Y Axis Fields,Поля оси Y, Y Axis Fields,Поля оси Y,
Yahoo Mail,Yahoo Mail,
Yandex.Mail,Яндекс.Почта, Yandex.Mail,Яндекс.Почта,
Yesterday,Вчера, Yesterday,Вчера,
You are connected to internet.,Вы подключены к Интернету., You are connected to internet.,Вы подключены к Интернету.,
@@ -2783,14 +2753,14 @@ You are not permitted to view the newsletter.,У Вас нет прав для
You are now following this document. You will receive daily updates via email. You can change this in User Settings.,Вы подписаны на обновления данного документа. Вы будете получать ежедневные обновления по электронной почте. Вы можете изменить это в настройках пользователя., You are now following this document. You will receive daily updates via email. You can change this in User Settings.,Вы подписаны на обновления данного документа. Вы будете получать ежедневные обновления по электронной почте. Вы можете изменить это в настройках пользователя.,
You can add dynamic properties from the document by using Jinja templating.,Вы можете добавить динамические свойства из документа с помощью шаблонов Jinja., You can add dynamic properties from the document by using Jinja templating.,Вы можете добавить динамические свойства из документа с помощью шаблонов Jinja.,
You can also copy-paste this ,Вы также можете скопировать и вставить это , You can also copy-paste this ,Вы также можете скопировать и вставить это ,
"You can change Submitted documents by cancelling them and then, amending them.","Вы можете изменить Проведенные (Submitted) документы, отменив их, а затем изменив их.",
"You can change Submitted documents by cancelling them and then, amending them.","Вы можете изменить утвержденные документы, отменив их, а затем отредактировав.",
You can find things by asking 'find orange in customers',Можно искать что-либо написав «найти апельсин у клиентов», You can find things by asking 'find orange in customers',Можно искать что-либо написав «найти апельсин у клиентов»,
You can only upload upto 5000 records in one go. (may be less in some cases),"Вы можете загружать одновременно до 5000 записей. (Возможно меньше, в некоторых случаях)", You can only upload upto 5000 records in one go. (may be less in some cases),"Вы можете загружать одновременно до 5000 записей. (Возможно меньше, в некоторых случаях)",
You can use Customize Form to set levels on fields.,Вы можете использовать Настройку формы (Customize Form) для установки уровней для полей., You can use Customize Form to set levels on fields.,Вы можете использовать Настройку формы (Customize Form) для установки уровней для полей.,
You can use wildcard %,Вы можете использовать подстановочные %, You can use wildcard %,Вы можете использовать подстановочные %,
You can't set 'Options' for field {0},Нельзя включить «Опции» для поля {0}, You can't set 'Options' for field {0},Нельзя включить «Опции» для поля {0},
You can't set 'Translatable' for field {0},Вы не можете установить «Переводимый» для поля {0}, You can't set 'Translatable' for field {0},Вы не можете установить «Переводимый» для поля {0},
You cannot give review points to yourself,Вы не можете давать оценки себе,
You cannot give review points to yourself,Вы не можете не можете начислять себе баллы обзора,
You cannot unset 'Read Only' for field {0},Нельзя отменить «только чтение» для поля {0}, You cannot unset 'Read Only' for field {0},Нельзя отменить «только чтение» для поля {0},
You do not have enough permissions to access this resource. Please contact your manager to get access.,"У вас нет достаточных прав для доступа к этому ресурсу. Пожалуйста, обратитесь к своему менеджеру, чтобы получить доступ.", You do not have enough permissions to access this resource. Please contact your manager to get access.,"У вас нет достаточных прав для доступа к этому ресурсу. Пожалуйста, обратитесь к своему менеджеру, чтобы получить доступ.",
You do not have enough permissions to complete the action,У Вас нет достаточных прав для завершения действия, You do not have enough permissions to complete the action,У Вас нет достаточных прав для завершения действия,
@@ -2811,13 +2781,13 @@ You need to be in developer mode to edit a Standard Web Form,Вы должны
You need to be logged in and have System Manager Role to be able to access backups.,"Вы должны войти в систему (и иметь роль менеджера системы), чтобы иметь доступ к резервным копиям.", You need to be logged in and have System Manager Role to be able to access backups.,"Вы должны войти в систему (и иметь роль менеджера системы), чтобы иметь доступ к резервным копиям.",
You need to be logged in to access this {0}.,"Вы должны войти, чтобы получить доступ к {0}.", You need to be logged in to access this {0}.,"Вы должны войти, чтобы получить доступ к {0}.",
"You need to have ""Share"" permission","Вы должны иметь разрешение ""Поделиться""", "You need to have ""Share"" permission","Вы должны иметь разрешение ""Поделиться""",
You need write permission to rename,"Вам нужно письменное разрешение, чтобы переименовать",
You need write permission to rename,"Вам нужно разрешение на запись, чтобы переименовать",
You selected Draft or Cancelled documents,Вы выбрали черновик или отмененные документы, You selected Draft or Cancelled documents,Вы выбрали черновик или отмененные документы,
You unfollowed this document,Вы отписались от этого документа, You unfollowed this document,Вы отписались от этого документа,
Your Country,Ваша страна, Your Country,Ваша страна,
Your Language,Ваш язык, Your Language,Ваш язык,
Your Name,Ваше имя, Your Name,Ваше имя,
Your account has been locked and will resume after {0} seconds,Ваша учетная запись заблокирована и возобновится после {0} секунд,
Your account has been locked and will resume after {0} seconds,Ваша учетная запись заблокирована и будет доступна через {0} секунд,
Your connection request to Google Calendar was successfully accepted,Ваш запрос на подключение к Календарю Google был успешно принят, Your connection request to Google Calendar was successfully accepted,Ваш запрос на подключение к Календарю Google был успешно принят,
Your information has been submitted,Ваша информация была представлена, Your information has been submitted,Ваша информация была представлена,
Your login id is,Ваш ID для авторизации, Your login id is,Ваш ID для авторизации,
@@ -2830,28 +2800,26 @@ Your payment was successfully accepted,Ваш платёж был успешно
"Your session has expired, please login again to continue.","Ваша сессия истекла, пожалуйста, войдите снова, чтобы продолжить.", "Your session has expired, please login again to continue.","Ваша сессия истекла, пожалуйста, войдите снова, чтобы продолжить.",
Zero,Ноль, Zero,Ноль,
Zero means send records updated at anytime,При нуле обновленные записи отправляются в любое время, Zero means send records updated at anytime,При нуле обновленные записи отправляются в любое время,
_doctype,_doctype,
_report,_report,
adjust,настроить, adjust,настроить,
after_insert,после_вставки, after_insert,после_вставки,
align-center,выровнять-центр,
align-justify,выровнять-оправдать,
align-left,выровнять левый,
align-right,выровнять правый,
align-center,выровнять-по-центру,
align-justify,выровнять-по-ширине,
align-left,выровнять-по-левой-стороне,
align-right,выровнять-по-правой-стороне,
ap-northeast-1,ар-северо-восток-1, ap-northeast-1,ар-северо-восток-1,
ap-northeast-2,ар-северо-восток-2, ap-northeast-2,ар-северо-восток-2,
ap-northeast-3,ар-северо-восток-3, ap-northeast-3,ар-северо-восток-3,
ap-south-1,ар-юго-1, ap-south-1,ар-юго-1,
ap-southeast-1,ар-юго-восток-1, ap-southeast-1,ар-юго-восток-1,
ap-southeast-2,ар-юго-восток-2, ap-southeast-2,ар-юго-восток-2,
arrow-down,Стрелка вниз,
arrow-left,стрелка налево,
arrow-right,стрелка направо,
arrow-up,Стрелка вверх,
arrow-down,стрелка-вниз,
arrow-left,стрелка-налево,
arrow-right,стрелка-направо,
arrow-up,стрелка-вверх,
asterisk,звёздочка, asterisk,звёздочка,
backward,назад, backward,назад,
ban-circle,Запрет круга,
bell,Накладная,
ban-circle,бан-кружок,
bell,колокольчик,
bookmark,закладка, bookmark,закладка,
briefcase,портфель, briefcase,портфель,
bullhorn,рупор, bullhorn,рупор,
@@ -2909,11 +2877,10 @@ gave {0} points,дал {0} баллов,
gift,подарок, gift,подарок,
glass,стекло, glass,стекло,
globe,глобус, globe,глобус,
hand-down,ручной вниз,
hand-left,ручной левый,
hand-right,ручной право,
hand-up,грабитель,
hdd,жесткий диск,
hand-down,рука-вниз,
hand-left,рука-влево,
hand-right,рука-вправо,
hand-up,рука-вверх,
headphones,наушники, headphones,наушники,
heart,сердце, heart,сердце,
hub,хаб, hub,хаб,
@@ -3011,7 +2978,7 @@ zoom-in,приблизить,
zoom-out,отдалить, zoom-out,отдалить,
{0} Calendar,{0} Календарь, {0} Calendar,{0} Календарь,
{0} Chart,{0} Диаграмма, {0} Chart,{0} Диаграмма,
{0} Dashboard,{0} Панель инструментов,
{0} Dashboard,{0} Показатели,
{0} List,{0} Список, {0} List,{0} Список,
{0} Modules,{0} Модули, {0} Modules,{0} Модули,
{0} Report,{0} Отчет, {0} Report,{0} Отчет,
@@ -3028,7 +2995,7 @@ zoom-out,отдалить,
{0} appreciated {1},{0} признателен {1}, {0} appreciated {1},{0} признателен {1},
{0} appreciation point for {1} {2},{0} благодарность за {1} {2}, {0} appreciation point for {1} {2},{0} благодарность за {1} {2},
{0} appreciation points for {1} {2},{0} баллы за {1} {2}, {0} appreciation points for {1} {2},{0} баллы за {1} {2},
{0} assigned {1}: {2},{0} назначено {1}: {2},
{0} assigned {1}: {2},{0} назначил(а) {1}: {2},
{0} cannot be set for Single types,{0} не может быть установлена для отдельных видов, {0} cannot be set for Single types,{0} не может быть установлена для отдельных видов,
{0} comments,{0} комментариев, {0} comments,{0} комментариев,
{0} created successfully,{0} создано успешно, {0} created successfully,{0} создано успешно,
@@ -3053,7 +3020,7 @@ zoom-out,отдалить,
{0} is not a valid Workflow State. Please update your Workflow and try again.,{0} не является допустимым состоянием рабочего процесса. Обновите свой рабочий процесс и повторите попытку., {0} is not a valid Workflow State. Please update your Workflow and try again.,{0} не является допустимым состоянием рабочего процесса. Обновите свой рабочий процесс и повторите попытку.,
{0} is now default print format for {1} doctype,{0} — теперь формат печати по умолчанию для {1} doctype, {0} is now default print format for {1} doctype,{0} — теперь формат печати по умолчанию для {1} doctype,
{0} is saved,{0} сохранено, {0} is saved,{0} сохранено,
{0} items selected,{0} продуктов выбрано,
{0} items selected,{0} элементов выбрано,
{0} logged in,{0} авторизирован, {0} logged in,{0} авторизирован,
{0} logged out: {1},{0} вышел: {1}, {0} logged out: {1},{0} вышел: {1},
{0} minutes ago,{0} минут назад, {0} minutes ago,{0} минут назад,
@@ -3097,7 +3064,7 @@ zoom-out,отдалить,
{0}: Cannot set Assign Submit if not Submittable,"{0}: Не удается установить Назначить проведение, если не подлежит проведению", {0}: Cannot set Assign Submit if not Submittable,"{0}: Не удается установить Назначить проведение, если не подлежит проведению",
{0}: Cannot set Cancel without Submit,{0}: Не удается установить Отмена без отправки, {0}: Cannot set Cancel without Submit,{0}: Не удается установить Отмена без отправки,
{0}: Cannot set Import without Create,{0}: Не удается установить Импорт без Создать, {0}: Cannot set Import without Create,{0}: Не удается установить Импорт без Создать,
"{0}: Cannot set Submit, Cancel, Amend without Write","{0}: Не удается выполнить Провести, Отменить, Изменить без Записать",
"{0}: Cannot set Submit, Cancel, Amend without Write","{0}: Не удается выполнить Подписать, Отменить, Изменить без Записать",
{0}: Cannot set import as {1} is not importable,{0}: Не удается установить импорт как {1} не является ввозу, {0}: Cannot set import as {1} is not importable,{0}: Не удается установить импорт как {1} не является ввозу,
{0}: No basic permissions set,{0}: Не установлен базовый набор разрешений, {0}: No basic permissions set,{0}: Не установлен базовый набор разрешений,
"{0}: Only one rule allowed with the same Role, Level and {1}","{0}: только одно правило допускается для той же роли, уровня и {1}", "{0}: Only one rule allowed with the same Role, Level and {1}","{0}: только одно правило допускается для той же роли, уровня и {1}",
@@ -3146,7 +3113,7 @@ About {0} seconds remaining,Осталось {0} секунд,
Access Log,Журнал доступа, Access Log,Журнал доступа,
Access not allowed from this IP Address,Доступ с этого IP-адреса запрещен, Access not allowed from this IP Address,Доступ с этого IP-адреса запрещен,
Action Type,Тип действия, Action Type,Тип действия,
Activity Log by ,Активность Журнал по,
Activity Log by ,Журнал активности по ,
Add Fields,Добавить поля, Add Fields,Добавить поля,
Administration,Администрирование, Administration,Администрирование,
After Cancel,После отмены, After Cancel,После отмены,
@@ -3162,7 +3129,7 @@ Allow Auto Repeat,Разрешить автоматическое повторе
Allow Google Calendar Access,Разрешить доступ к календарю Google, Allow Google Calendar Access,Разрешить доступ к календарю Google,
Allow Google Contacts Access,Разрешить доступ к контактам Google, Allow Google Contacts Access,Разрешить доступ к контактам Google,
Allow Google Drive Access,Разрешить доступ Google Drive, Allow Google Drive Access,Разрешить доступ Google Drive,
Allow Guest,Разрешить гость,
Allow Guest,Разрешить гостя,
Allow Guests to Upload Files,Разрешить гостям загружать файлы, Allow Guests to Upload Files,Разрешить гостям загружать файлы,
Also adding the status dependency field {0},Также добавляем поле зависимости статуса {0}, Also adding the status dependency field {0},Также добавляем поле зависимости статуса {0},
An error occurred while setting Session Defaults,Произошла ошибка при настройке параметров сеанса по умолчанию, An error occurred while setting Session Defaults,Произошла ошибка при настройке параметров сеанса по умолчанию,
@@ -3216,8 +3183,8 @@ Click on the link below to approve the request,"Нажмите на ссылку
Click on the lock icon to toggle public/private,"Нажмите на значок замка, чтобы переключить публичный / приватный", Click on the lock icon to toggle public/private,"Нажмите на значок замка, чтобы переключить публичный / приватный",
Click on {0} to generate Refresh Token.,"Нажмите {0}, чтобы сгенерировать токен обновления.", Click on {0} to generate Refresh Token.,"Нажмите {0}, чтобы сгенерировать токен обновления.",
Close Condition,Закрыть условие, Close Condition,Закрыть условие,
Column {0},Столбец {0},
Columns / Fields,Колонны / Поля,
Column {0},Колонка {0},
Columns / Fields,Колонки / Поля,
"Configure notifications for mentions, assignments, energy points and more.","Настройте уведомления для упоминаний, назначений, энергетических очков и многое другое.", "Configure notifications for mentions, assignments, energy points and more.","Настройте уведомления для упоминаний, назначений, энергетических очков и многое другое.",
Contact Email,Эл.почта для связи, Contact Email,Эл.почта для связи,
Contact Numbers,Контактные номера, Contact Numbers,Контактные номера,
@@ -3234,8 +3201,7 @@ Could not create razorpay order,Не удалось создать заказ н
Create Log,Создать журнал, Create Log,Создать журнал,
Create your first {0},Создайте свой первый {0}, Create your first {0},Создайте свой первый {0},
Created {0} records successfully.,Создано {0} записей успешно., Created {0} records successfully.,Создано {0} записей успешно.,
Cron,Cron,
Cron Format,Крон Формат,
Cron Format,Cron формат,
Daily Events should finish on the Same Day.,Ежедневные события должны заканчиваться в тот же день., Daily Events should finish on the Same Day.,Ежедневные события должны заканчиваться в тот же день.,
Daily Long,Ежедневно, Daily Long,Ежедневно,
Default Role on Creation,Роль по умолчанию при создании, Default Role on Creation,Роль по умолчанию при создании,
@@ -3257,10 +3223,10 @@ Document type is required to create a dashboard chart,Тип документа
Documentation Link,Документация Ссылка, Documentation Link,Документация Ссылка,
Don't Import,Не импортировать, Don't Import,Не импортировать,
Don't Send Emails,Не отправлять электронные письма, Don't Send Emails,Не отправлять электронные письма,
"Drag and drop files, ","Перетащите файлы,",
"Drag and drop files, ","Перетащите файлы, ",
Drop,Бросить, Drop,Бросить,
Drop Here,Бросить тут, Drop Here,Бросить тут,
Drop files here,Перетащите файлы сюда,
Drop files here,Поместите файлы сюда,
Dynamic Template,Динамический шаблон, Dynamic Template,Динамический шаблон,
ERPNext Role,ERPNext роль, ERPNext Role,ERPNext роль,
Email / Notifications,Уведомления по электронной почте, Email / Notifications,Уведомления по электронной почте,
@@ -3407,7 +3373,7 @@ Last Update,Последнее обновление,
Last refreshed,Последнее обновление, Last refreshed,Последнее обновление,
Link Document Type,Тип документа ссылки, Link Document Type,Тип документа ссылки,
Link Fieldname,Имя поля ссылки, Link Fieldname,Имя поля ссылки,
Loading import file...,Загрузка файла импорта...,
Loading import file...,Загрузка импортируемого файла...,
Local Document Type,Локальный тип документа, Local Document Type,Локальный тип документа,
Log Data,Данные журнала, Log Data,Данные журнала,
Main Section (HTML),Основной раздел (HTML), Main Section (HTML),Основной раздел (HTML),
@@ -3415,7 +3381,7 @@ Main Section (Markdown),Основной раздел (Markdown),
"Maintains a Log of all inserts, updates and deletions on Event Producer site for documents that have consumers.","Ведение журнала всех вставок, обновлений и удалений на сайте Event Producer для документов, имеющих потребителей.", "Maintains a Log of all inserts, updates and deletions on Event Producer site for documents that have consumers.","Ведение журнала всех вставок, обновлений и удалений на сайте Event Producer для документов, имеющих потребителей.",
Maintains a log of every event consumed along with the status of the sync and a Resync button in case sync fails.,"Ведение журнала всех использованных событий, а также состояния синхронизации и кнопки Resync в случае сбоя синхронизации.", Maintains a log of every event consumed along with the status of the sync and a Resync button in case sync fails.,"Ведение журнала всех использованных событий, а также состояния синхронизации и кнопки Resync в случае сбоя синхронизации.",
Make all attachments private,Сделайте все вложения приватными, Make all attachments private,Сделайте все вложения приватными,
Mandatory Depends On,Обязательно зависит от,
Mandatory Depends On,Обязателен - зависит от,
Map Columns,Столбцы карты, Map Columns,Столбцы карты,
Map columns from {0} to fields in {1},Сопоставить столбцы из {0} с полями в {1}, Map columns from {0} to fields in {1},Сопоставить столбцы из {0} с полями в {1},
Mapping column {0} to field {1},Отображение столбца {0} в поле {1}, Mapping column {0} to field {1},Отображение столбца {0} в поле {1},
@@ -3426,7 +3392,7 @@ Me,Мне,
Mention,Упоминание, Mention,Упоминание,
Modules,Модули, Modules,Модули,
Monthly Long,Ежемесячно долго, Monthly Long,Ежемесячно долго,
Naming Series,Идентификация по Имени,
Naming Series,Именование серии,
Navigate Home,Навигация Домой, Navigate Home,Навигация Домой,
Navigate list down,Переместиться вниз по списку, Navigate list down,Переместиться вниз по списку,
Navigate list up,Навигация по списку вверх, Navigate list up,Навигация по списку вверх,
@@ -3483,7 +3449,7 @@ Press Alt Key to trigger additional shortcuts in Menu and Sidebar,"Нажмит
Print Settings...,Настройки печати..., Print Settings...,Настройки печати...,
Producer Document Name,Название документа производителя, Producer Document Name,Название документа производителя,
Producer URL,URL производителя, Producer URL,URL производителя,
Property Depends On,Недвижимость зависит от,
Property Depends On,Свойства зависят от,
Pull from Google Calendar,Загрузить из календаря Google, Pull from Google Calendar,Загрузить из календаря Google,
Pull from Google Contacts,Загрузить из контактов Google, Pull from Google Contacts,Загрузить из контактов Google,
Pulled from Google Calendar,Загружено из календаря Google, Pulled from Google Calendar,Загружено из календаря Google,
@@ -3493,7 +3459,7 @@ Push to Google Contacts,Нажмите на контакты Google,
Queue / Worker,Очередь / Рабочий, Queue / Worker,Очередь / Рабочий,
RAW Information Log,Необработанный информационный журнал, RAW Information Log,Необработанный информационный журнал,
Raw Printing Settings...,Настройки необработанной печати..., Raw Printing Settings...,Настройки необработанной печати...,
Read Only Depends On,Только чтение зависит от,
Read Only Depends On,Только для чтения - зависит от,
Recent Activity,Недавняя активность, Recent Activity,Недавняя активность,
Reference document has been cancelled,Справочный документ был отменен, Reference document has been cancelled,Справочный документ был отменен,
Reload File,Перезагрузить файл, Reload File,Перезагрузить файл,
@@ -3526,6 +3492,7 @@ Select Date Range,Выберите диапазон дат,
Select Field,Выберите поле, Select Field,Выберите поле,
Select Field...,Выберите поле..., Select Field...,Выберите поле...,
Select Filters,Выберите фильтры, Select Filters,Выберите фильтры,
Edit Filters,Изменить фильтры,
Select Google Calendar to which event should be synced.,"Выберите календарь Google, к которому нужно синхронизировать событие.", Select Google Calendar to which event should be synced.,"Выберите календарь Google, к которому нужно синхронизировать событие.",
Select Google Contacts to which contact should be synced.,"Выберите Google Контакты, с которыми контакт должен быть синхронизирован.", Select Google Contacts to which contact should be synced.,"Выберите Google Контакты, с которыми контакт должен быть синхронизирован.",
Select Group By...,Выбрать группу по..., Select Group By...,Выбрать группу по...,
@@ -3646,7 +3613,7 @@ Workflow Status,Состояние рабочего процесса,
You are not allowed to export {} doctype,Вы не можете экспортировать {} doctype, You are not allowed to export {} doctype,Вы не можете экспортировать {} doctype,
You can try changing the filters of your report.,Вы можете попробовать изменить фильтры вашего отчета., You can try changing the filters of your report.,Вы можете попробовать изменить фильтры вашего отчета.,
You do not have permissions to cancel all linked documents.,У вас нет прав для отмены всех связанных документов., You do not have permissions to cancel all linked documents.,У вас нет прав для отмены всех связанных документов.,
You need to create these first: ,Вам нужно сначала создать это: ,
You need to create these first: ,Вам сначала нужно создать: ,
You need to enable JavaScript for your app to work.,Вам нужно включить JavaScript для вашего приложения для работы., You need to enable JavaScript for your app to work.,Вам нужно включить JavaScript для вашего приложения для работы.,
You need to install pycups to use this feature!,"Вам нужно установить pycups, чтобы использовать эту функцию!", You need to install pycups to use this feature!,"Вам нужно установить pycups, чтобы использовать эту функцию!",
Your Target,Ваша цель, Your Target,Ваша цель,
@@ -3708,7 +3675,7 @@ Currency,Валюта,
Customize,Настроить, Customize,Настроить,
Daily,Ежедневно, Daily,Ежедневно,
Date,Дата, Date,Дата,
Dear,Уважаемый (ая),
Dear,Уважаемый(ая),
Default,По умолчанию, Default,По умолчанию,
Delete,Удалить, Delete,Удалить,
Description,Описание, Description,Описание,
@@ -3727,7 +3694,7 @@ Entity Type,Тип объекта,
Error,Ошибка, Error,Ошибка,
Expired,Истек срок действия, Expired,Истек срок действия,
Export,Экспорт, Export,Экспорт,
Export not allowed. You need {0} role to export.,Экспорт не допускается. Вам нужно {0} роль для экспорта.,
Export not allowed. You need {0} role to export.,Экспорт не допускается. Вам нужна роль {0} для экспорта.,
Fetching...,Получение..., Fetching...,Получение...,
Field,Поле, Field,Поле,
File Manager,Файловый менеджер, File Manager,Файловый менеджер,
@@ -3788,7 +3755,6 @@ Set,Задать,
Setup,Настройки, Setup,Настройки,
Setup Wizard,Мастер установки, Setup Wizard,Мастер установки,
Size,Размер, Size,Размер,
Sr,Sr,
Start,Начать, Start,Начать,
Start Time,Стартовое время, Start Time,Стартовое время,
Status,Статус, Status,Статус,
@@ -3798,7 +3764,7 @@ Template,Шаблон,
Thursday,Четверг, Thursday,Четверг,
Title,Заголовок, Title,Заголовок,
Total,Общая сумма, Total,Общая сумма,
Totals,Всего:,
Totals,Всего,
Tuesday,Вторник, Tuesday,Вторник,
Type,Тип, Type,Тип,
Update,Обновить, Update,Обновить,
@@ -3809,10 +3775,10 @@ Welcome to {0},Добро пожаловать в {0},
Year,Год, Year,Год,
Yearly,Ежегодно, Yearly,Ежегодно,
You,Вы, You,Вы,
You can also copy-paste this link in your browser,Ещё можно скопировать эту ссылку в браузер,
You can also copy-paste this link in your browser,Вы также можете скопировать и вставить эту ссылку в свой браузер,
and,и, and,и,
{0} Name,{0} Имя, {0} Name,{0} Имя,
{0} is required,{0} требуется,
{0} is required,{0} является обязательным,
ALL,ВСЕ, ALL,ВСЕ,
Attach File,Прикрепить файл, Attach File,Прикрепить файл,
Barcode,Штрих-код, Barcode,Штрих-код,
@@ -3887,7 +3853,6 @@ Hidden,Скрытый,
Javascript,Javascript, Javascript,Javascript,
Ldap settings,Настройки Ldap, Ldap settings,Настройки Ldap,
Mobile number,Мобильный номер, Mobile number,Мобильный номер,
Mx,Mx,
No,Нет, No,Нет,
Not found,Не обнаружена, Not found,Не обнаружена,
Notes:,Примечания:, Notes:,Примечания:,
@@ -3994,7 +3959,7 @@ Desk Page,Рабочий стол,
Desk Shortcut,Сочетание клавиш, Desk Shortcut,Сочетание клавиш,
Developer Mode Only,Только режим разработчика, Developer Mode Only,Только режим разработчика,
Disable User Customization,Отключить настройку пользователя, Disable User Customization,Отключить настройку пользователя,
For example: {} Open,Например: {} Open,
For example: {} Open,Например: {} Открыто,
Link Cards,Карты ссылок, Link Cards,Карты ссылок,
Link To,Ссылка к, Link To,Ссылка к,
Onboarding,Вводный, Onboarding,Вводный,
@@ -4026,6 +3991,7 @@ Select Language,Выбрать язык,
Confirm Translations,Подтвердить перевод, Confirm Translations,Подтвердить перевод,
Contributed Translations,Добавленные переводы, Contributed Translations,Добавленные переводы,
Show Tags,Показать теги, Show Tags,Показать теги,
Hide Tags,Скрыть теги,
Do not have permission to access {0} bucket.,У вас нет разрешения на доступ к сегменту {0}., Do not have permission to access {0} bucket.,У вас нет разрешения на доступ к сегменту {0}.,
Allow document creation via Email,Разрешить создание документов по электронной почте, Allow document creation via Email,Разрешить создание документов по электронной почте,
Sender Field,Поле отправителя, Sender Field,Поле отправителя,
@@ -4218,12 +4184,12 @@ since last year,с прошлого года,
Show,Показать, Show,Показать,
New Number Card,Карточка с новым номером, New Number Card,Карточка с новым номером,
Your Shortcuts,Ваши ярлыки, Your Shortcuts,Ваши ярлыки,
You haven't added any Dashboard Charts or Number Cards yet.,Вы еще не добавили диаграммы или карточки с цифрами.,
Click On Customize to add your first widget,"Нажмите &quot;Настроить&quot;, чтобы добавить свой первый виджет.",
You haven't added any Dashboard Charts or Number Cards yet.,Вы еще не добавили диаграммы или карточки с показателями.,
Click On Customize to add your first widget,"Нажмите Настроить, чтобы добавить свой первый виджет.",
Are you sure you want to reset all customizations?,"Вы уверены, что хотите сбросить все настройки?", Are you sure you want to reset all customizations?,"Вы уверены, что хотите сбросить все настройки?",
"Couldn't save, please check the data you have entered","Не удалось сохранить, проверьте данные, которые вы ввели", "Couldn't save, please check the data you have entered","Не удалось сохранить, проверьте данные, которые вы ввели",
Validation Error,Ошибка проверки, Validation Error,Ошибка проверки,
"You can only upload JPG, PNG, PDF, or Microsoft documents.","Вы можете загружать только документы в форматах JPG, PNG, PDF или Microsoft.",
"You can only upload JPG, PNG, PDF, or Microsoft documents.","Вы можете загружать только документы в форматах JPG, PNG, PDF или документы Microsoft.",
Reverting length to {0} for '{1}' in '{2}'. Setting the length as {3} will cause truncation of data.,Возврат длины к {0} для &#39;{1}&#39; в &#39;{2}&#39;. Установка длины как {3} вызовет усечение данных., Reverting length to {0} for '{1}' in '{2}'. Setting the length as {3} will cause truncation of data.,Возврат длины к {0} для &#39;{1}&#39; в &#39;{2}&#39;. Установка длины как {3} вызовет усечение данных.,
'{0}' not allowed for type {1} in row {2},&#39;{0}&#39; не разрешено для типа {1} в строке {2}, '{0}' not allowed for type {1} in row {2},&#39;{0}&#39; не разрешено для типа {1} в строке {2},
Option {0} for field {1} is not a child table,Вариант {0} для поля {1} не является дочерней таблицей, Option {0} for field {1} is not a child table,Вариант {0} для поля {1} не является дочерней таблицей,
@@ -4437,7 +4403,7 @@ CTA,CTA,
CTA Label,Метка CTA, CTA Label,Метка CTA,
CTA URL,CTA URL, CTA URL,CTA URL,
Default Portal Home,Главная страница портала по умолчанию, Default Portal Home,Главная страница портала по умолчанию,
"Example: ""/desk""",Пример: &quot;/ стол&quot;,
"Example: ""/desk""","Пример: ""/desk""",
Social Link Settings,Настройки социальных ссылок, Social Link Settings,Настройки социальных ссылок,
Social Link Type,Тип социальной ссылки, Social Link Type,Тип социальной ссылки,
facebook,facebook, facebook,facebook,
@@ -4543,11 +4509,11 @@ Too Many Requests,Слишком много запросов,
{} is not a valid date string.,{} не является допустимой строкой даты., {} is not a valid date string.,{} не является допустимой строкой даты.,
Invalid Date,Недействительная дата, Invalid Date,Недействительная дата,
Please select a valid date filter,"Пожалуйста, выберите действующий фильтр даты", Please select a valid date filter,"Пожалуйста, выберите действующий фильтр даты",
Value {0} must be in the valid duration format: d h m s,Значение {0} должно иметь допустимый формат продолжительности: dhms.,
Value {0} must be in the valid duration format: d h m s,Значение {0} должно иметь допустимый формат продолжительности: д ч м с,
Google Sheets URL is invalid or not publicly accessible.,URL-адрес Google Таблиц недействителен или не является общедоступным., Google Sheets URL is invalid or not publicly accessible.,URL-адрес Google Таблиц недействителен или не является общедоступным.,
"Google Sheets URL must end with ""gid={number}"". Copy and paste the URL from the browser address bar and try again.",URL-адрес Google Таблиц должен заканчиваться на &quot;gid = {number}&quot;. Скопируйте и вставьте URL-адрес из адресной строки браузера и повторите попытку., "Google Sheets URL must end with ""gid={number}"". Copy and paste the URL from the browser address bar and try again.",URL-адрес Google Таблиц должен заканчиваться на &quot;gid = {number}&quot;. Скопируйте и вставьте URL-адрес из адресной строки браузера и повторите попытку.,
Incorrect URL,Неверный URL, Incorrect URL,Неверный URL,
"""{0}"" is not a valid Google Sheets URL",&quot;{0}&quot; не является действительным URL-адресом Google Таблиц,
"""{0}"" is not a valid Google Sheets URL","""{0}"" не является действительным URL-адресом Google Таблиц",
Duplicate Name,Повторяющееся имя, Duplicate Name,Повторяющееся имя,
"Please check the value of ""Fetch From"" set for field {0}","Проверьте значение параметра &quot;Получить из&quot;, установленное для поля {0}.", "Please check the value of ""Fetch From"" set for field {0}","Проверьте значение параметра &quot;Получить из&quot;, установленное для поля {0}.",
Wrong Fetch From value,Неверное значение Fetch From, Wrong Fetch From value,Неверное значение Fetch From,
@@ -4567,7 +4533,7 @@ Hourly comment limit reached for: {0},Достигнут лимит почасо
Please add a valid comment.,"Пожалуйста, добавьте действительный комментарий.", Please add a valid comment.,"Пожалуйста, добавьте действительный комментарий.",
Document {0} Already Restored,Документ {0} уже восстановлен, Document {0} Already Restored,Документ {0} уже восстановлен,
Restoring Deleted Document,Восстановление удаленного документа, Restoring Deleted Document,Восстановление удаленного документа,
{function} of {fieldlabel},{функция} из {fieldlabel},
{function} of {fieldlabel},{function} из {fieldlabel},
Invalid template file for import,Неверный файл шаблона для импорта, Invalid template file for import,Неверный файл шаблона для импорта,
Invalid or corrupted content for import,Недействительный или поврежденный контент для импорта, Invalid or corrupted content for import,Недействительный или поврежденный контент для импорта,
Value {0} must in {1} format,Значение {0} должно быть в формате {1}, Value {0} must in {1} format,Значение {0} должно быть в формате {1},
@@ -4575,7 +4541,7 @@ Value {0} must in {1} format,Значение {0} должно быть в фо
Could not map column {0} to field {1},Не удалось сопоставить столбец {0} с полем {1}, Could not map column {0} to field {1},Не удалось сопоставить столбец {0} с полем {1},
Skipping Duplicate Column {0},Пропуск повторяющегося столбца {0}, Skipping Duplicate Column {0},Пропуск повторяющегося столбца {0},
The column {0} has {1} different date formats. Automatically setting {2} as the default format as it is the most common. Please change other values in this column to this format.,"Столбец {0} имеет {1} разные форматы даты. Автоматическая установка {2} в качестве формата по умолчанию, поскольку он является наиболее распространенным. Измените другие значения в этом столбце на этот формат.", The column {0} has {1} different date formats. Automatically setting {2} as the default format as it is the most common. Please change other values in this column to this format.,"Столбец {0} имеет {1} разные форматы даты. Автоматическая установка {2} в качестве формата по умолчанию, поскольку он является наиболее распространенным. Измените другие значения в этом столбце на этот формат.",
You have reached the hourly limit for generating password reset links. Please try again later.,"Вы достигли почасового лимита для создания ссылок для сброса пароля. Пожалуйста, попробуйте позже.",
You have reached the hourly limit for generating password reset links. Please try again later.,"Вы достигли лимита на создание ссылок для сброса пароля. Пожалуйста, попробуйте позже.",
Please hide the standard navbar items instead of deleting them,Скройте стандартные элементы навигационной панели вместо их удаления, Please hide the standard navbar items instead of deleting them,Скройте стандартные элементы навигационной панели вместо их удаления,
DocType's name should not start or end with whitespace,Имя DocType не должно начинаться или заканчиваться пробелом, DocType's name should not start or end with whitespace,Имя DocType не должно начинаться или заканчиваться пробелом,
File name cannot have {0},Имя файла не может содержать {0}, File name cannot have {0},Имя файла не может содержать {0},
@@ -4600,14 +4566,14 @@ Delivery Failed,Доставка не удалась,
Twilio WhatsApp Message Error,Ошибка сообщения Twilio WhatsApp, Twilio WhatsApp Message Error,Ошибка сообщения Twilio WhatsApp,
A featured post must have a cover image,В избранном посте должна быть обложка., A featured post must have a cover image,В избранном посте должна быть обложка.,
Load More,Показать больше, Load More,Показать больше,
Published on,Опубликован в,
Published on,Опубликован,
Enable developer mode to create a standard Web Template,"Включите режим разработчика, чтобы создать стандартный веб-шаблон", Enable developer mode to create a standard Web Template,"Включите режим разработчика, чтобы создать стандартный веб-шаблон",
Was this article helpful?,Эта статья была полезной?, Was this article helpful?,Эта статья была полезной?,
Thank you for your feedback!,Спасибо за ваш отзыв!, Thank you for your feedback!,Спасибо за ваш отзыв!,
New Mention on {0},Новое упоминание о {0}, New Mention on {0},Новое упоминание о {0},
Assignment Update on {0},Обновление задания на {0}, Assignment Update on {0},Обновление задания на {0},
New Document Shared {0},Новый документ опубликован {0}, New Document Shared {0},Новый документ опубликован {0},
Energy Point Update on {0},Обновление Energy Point от {0},
Energy Point Update on {0},Обновление баллов активности от {0},
You cannot create a dashboard chart from single DocTypes,Вы не можете создать диаграмму панели мониторинга из одного типа документов, You cannot create a dashboard chart from single DocTypes,Вы не можете создать диаграмму панели мониторинга из одного типа документов,
Invalid json added in the custom options: {0},В настраиваемые параметры добавлен недопустимый json: {0}, Invalid json added in the custom options: {0},В настраиваемые параметры добавлен недопустимый json: {0},
Invalid JSON in card links for {0},Недействительный JSON в ссылках на карточки для {0}, Invalid JSON in card links for {0},Недействительный JSON в ссылках на карточки для {0},
@@ -4629,7 +4595,7 @@ Worflow States Don't Exist,Состояния Worflow не существуют,
Save Anyway,Все равно сохранить, Save Anyway,Все равно сохранить,
Energy Points:,Баллы активности:, Energy Points:,Баллы активности:,
Review Points:,Баллы обзора:, Review Points:,Баллы обзора:,
Rank:,Ранг:,
Rank:,Рейтинг:,
Monthly Rank:,Месячный рейтинг:, Monthly Rank:,Месячный рейтинг:,
Invalid expression set in filter {0} ({1}),Недопустимое выражение в фильтре {0} ({1}), Invalid expression set in filter {0} ({1}),Недопустимое выражение в фильтре {0} ({1}),
Invalid expression set in filter {0},В фильтре {0} задано недопустимое выражение, Invalid expression set in filter {0},В фильтре {0} задано недопустимое выражение,
@@ -4688,12 +4654,12 @@ Open URL in a New Tab,Открыть URL в новой вкладке,
Align Right,Выровнять по правому краю, Align Right,Выровнять по правому краю,
Loading Filters...,Загрузка фильтров..., Loading Filters...,Загрузка фильтров...,
Count Customizations,Подсчет настроек, Count Customizations,Подсчет настроек,
For Example: {} Open,Например: {} Открыть,
For Example: {} Open,Например: {} Открыто,
Choose Existing Card or create New Card,Выберите существующую карту или создайте новую карту, Choose Existing Card or create New Card,Выберите существующую карту или создайте новую карту,
Number Cards,Числовые карты, Number Cards,Числовые карты,
Function Based On,Функция на основе, Function Based On,Функция на основе,
Add Filters,Добавить фильтры, Add Filters,Добавить фильтры,
Skip,Пропускать,
Skip,Пропустить,
Dismiss,Отклонить, Dismiss,Отклонить,
Value cannot be negative for,Значение не может быть отрицательным для, Value cannot be negative for,Значение не может быть отрицательным для,
Value cannot be negative for {0}: {1},Значение не может быть отрицательным для {0}: {1}, Value cannot be negative for {0}: {1},Значение не может быть отрицательным для {0}: {1},
@@ -4702,9 +4668,28 @@ Authentication failed while receiving emails from Email Account: {0}.,Ошибк
Message from server: {0},Сообщение с сервера: {0}, Message from server: {0},Сообщение с сервера: {0},
Documentation,Документация, Documentation,Документация,
User Forum,Форум пользователей, User Forum,Форум пользователей,
Report an issue,Сообщить об ошибке,
Report an Issue,Сообщить об ошибке,
About,О системе,
My Profile,Мой профиль, My Profile,Мой профиль,
My Settings,Мои настройки, My Settings,Мои настройки,
Toggle Full Width,Переключить ширину, Toggle Full Width,Переключить ширину,
Toggle Theme,Переключить тему, Toggle Theme,Переключить тему,
Modules,Модули, Modules,Модули,
You created this {0},Вы создали это {0},
{0} created this {1},{0} создал(а) это {1},
You edited this {0},Вы отредактировали это {0},
{0} edited this {1},{0} отредактировал(а) это {1},
You viewed this {0},Вы просмотрели это {0},
{0} viewed this {1},{0} просмотрел(а) это {1},
Apply Filters,Применить фильтры,
+ Add a Filter,+ Добавить фильтр,
Is Template,Является шаблоном,
Show Saved,Показать сохраненные,
Hide Saved,Скрыть сохраненные,
Add Tags,Добавить теги,
Page Size,Формат страницы,
Set all public,Сделать публичными,
Set all private,Сделать приватными,
Drag and drop files here or upload from,Перетащите файлы сюда или загрузите из,
My Device,Моё устройство,
Library,Библиотека,

+ 9
- 13
frappe/twofactor.py 查看文件

@@ -43,10 +43,10 @@ def toggle_two_factor_auth(state, roles=None):


def two_factor_is_enabled(user=None): def two_factor_is_enabled(user=None):
"""Returns True if 2FA is enabled.""" """Returns True if 2FA is enabled."""
enabled = int(frappe.db.get_value("System Settings", None, "enable_two_factor_auth") or 0)
enabled = int(frappe.db.get_single_value("System Settings", "enable_two_factor_auth") or 0)
if enabled: if enabled:
bypass_two_factor_auth = int( bypass_two_factor_auth = int(
frappe.db.get_value("System Settings", None, "bypass_2fa_for_retricted_ip_users") or 0
frappe.db.get_single_value("System Settings", "bypass_2fa_for_retricted_ip_users") or 0
) )
if bypass_two_factor_auth and user: if bypass_two_factor_auth and user:
user_doc = frappe.get_doc("User", user) user_doc = frappe.get_doc("User", user)
@@ -145,7 +145,7 @@ def get_otpsecret_for_(user):




def get_verification_method(): def get_verification_method():
return frappe.db.get_value("System Settings", None, "two_factor_method")
return frappe.db.get_single_value("System Settings", "two_factor_method")




def confirm_otp_token(login_manager, otp=None, tmp_id=None): def confirm_otp_token(login_manager, otp=None, tmp_id=None):
@@ -191,7 +191,7 @@ def confirm_otp_token(login_manager, otp=None, tmp_id=None):




def get_verification_obj(user, token, otp_secret): def get_verification_obj(user, token, otp_secret):
otp_issuer = frappe.db.get_value("System Settings", "System Settings", "otp_issuer_name")
otp_issuer = frappe.db.get_single_value("System Settings", "otp_issuer_name")
verification_method = get_verification_method() verification_method = get_verification_method()
verification_obj = None verification_obj = None
if verification_method == "SMS": if verification_method == "SMS":
@@ -267,7 +267,7 @@ def process_2fa_for_email(user, token, otp_secret, otp_issuer, method="Email"):
def get_email_subject_for_2fa(kwargs_dict): def get_email_subject_for_2fa(kwargs_dict):
"""Get email subject for 2fa.""" """Get email subject for 2fa."""
subject_template = _("Login Verification Code from {}").format( subject_template = _("Login Verification Code from {}").format(
frappe.db.get_value("System Settings", "System Settings", "otp_issuer_name")
frappe.db.get_single_value("System Settings", "otp_issuer_name")
) )
subject = frappe.render_template(subject_template, kwargs_dict) subject = frappe.render_template(subject_template, kwargs_dict)
return subject return subject
@@ -287,7 +287,7 @@ def get_email_body_for_2fa(kwargs_dict):
def get_email_subject_for_qr_code(kwargs_dict): def get_email_subject_for_qr_code(kwargs_dict):
"""Get QRCode email subject.""" """Get QRCode email subject."""
subject_template = _("One Time Password (OTP) Registration Code from {}").format( subject_template = _("One Time Password (OTP) Registration Code from {}").format(
frappe.db.get_value("System Settings", "System Settings", "otp_issuer_name")
frappe.db.get_single_value("System Settings", "otp_issuer_name")
) )
subject = frappe.render_template(subject_template, kwargs_dict) subject = frappe.render_template(subject_template, kwargs_dict)
return subject return subject
@@ -307,9 +307,7 @@ def get_link_for_qrcode(user, totp_uri):
key = frappe.generate_hash(length=20) key = frappe.generate_hash(length=20)
key_user = f"{key}_user" key_user = f"{key}_user"
key_uri = f"{key}_uri" key_uri = f"{key}_uri"
lifespan = (
int(frappe.db.get_value("System Settings", "System Settings", "lifespan_qrcode_image")) or 240
)
lifespan = int(frappe.db.get_single_value("System Settings", "lifespan_qrcode_image")) or 240
frappe.cache().set_value(key_uri, totp_uri, expires_in_sec=lifespan) frappe.cache().set_value(key_uri, totp_uri, expires_in_sec=lifespan)
frappe.cache().set_value(key_user, user, expires_in_sec=lifespan) frappe.cache().set_value(key_user, user, expires_in_sec=lifespan)
return get_url(f"/qrcode?k={key}") return get_url(f"/qrcode?k={key}")
@@ -465,9 +463,7 @@ def should_remove_barcode_image(barcode):
"""Check if it's time to delete barcode image from server.""" """Check if it's time to delete barcode image from server."""
if isinstance(barcode, str): if isinstance(barcode, str):
barcode = frappe.get_doc("File", barcode) barcode = frappe.get_doc("File", barcode)
lifespan = (
frappe.db.get_value("System Settings", "System Settings", "lifespan_qrcode_image") or 240
)
lifespan = frappe.db.get_single_value("System Settings", "lifespan_qrcode_image") or 240
if time_diff_in_seconds(get_datetime(), barcode.creation) > int(lifespan): if time_diff_in_seconds(get_datetime(), barcode.creation) > int(lifespan):
return True return True
return False return False
@@ -482,7 +478,7 @@ def reset_otp_secret(user):
if frappe.session.user != user: if frappe.session.user != user:
frappe.only_for("System Manager", message=True) frappe.only_for("System Manager", message=True)


otp_issuer = frappe.db.get_value("System Settings", "System Settings", "otp_issuer_name")
otp_issuer = frappe.db.get_single_value("System Settings", "otp_issuer_name")
user_email = frappe.db.get_value("User", user, "email") user_email = frappe.db.get_value("User", user, "email")


clear_default(user + "_otplogin") clear_default(user + "_otplogin")


+ 1
- 1
frappe/utils/data.py 查看文件

@@ -1545,7 +1545,7 @@ def get_url(uri: str | None = None, full_address: bool = False) -> str:
host_name = protocol + frappe.local.site host_name = protocol + frappe.local.site


else: else:
host_name = frappe.db.get_value("Website Settings", "Website Settings", "subdomain")
host_name = frappe.db.get_single_value("Website Settings", "subdomain")


if not host_name: if not host_name:
host_name = "http://localhost" host_name = "http://localhost"


+ 1
- 0
frappe/utils/safe_exec.py 查看文件

@@ -152,6 +152,7 @@ def get_safe_globals():
enqueue=safe_enqueue, enqueue=safe_enqueue,
sanitize_html=frappe.utils.sanitize_html, sanitize_html=frappe.utils.sanitize_html,
log_error=frappe.log_error, log_error=frappe.log_error,
log=frappe.log,
db=NamespaceDict( db=NamespaceDict(
get_list=frappe.get_list, get_list=frappe.get_list,
get_all=frappe.get_all, get_all=frappe.get_all,


+ 9
- 9
frappe/website/doctype/blog_post/blog_post.json 查看文件

@@ -19,7 +19,7 @@
"hide_cta", "hide_cta",
"enable_email_notification", "enable_email_notification",
"disable_comments", "disable_comments",
"disable_feedback",
"disable_likes",
"section_break_5", "section_break_5",
"blog_intro", "blog_intro",
"content_type", "content_type",
@@ -194,18 +194,18 @@
"label": "Meta Title", "label": "Meta Title",
"length": 60 "length": 60
}, },
{
"default": "0",
"fieldname": "disable_feedback",
"fieldtype": "Check",
"label": "Disable Feedback"
},
{ {
"default": "1", "default": "1",
"description": "Enable email notification for any comment or feedback on your Blog Post.",
"description": "Enable email notification for any comment or likes received on your Blog Post.",
"fieldname": "enable_email_notification", "fieldname": "enable_email_notification",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Enable Email Notification" "label": "Enable Email Notification"
},
{
"default": "0",
"fieldname": "disable_likes",
"fieldtype": "Check",
"label": "Disable Likes"
} }
], ],
"has_web_view": 1, "has_web_view": 1,
@@ -214,7 +214,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_published_field": "published", "is_published_field": "published",
"links": [], "links": [],
"modified": "2022-03-21 14:42:19.282612",
"modified": "2022-07-12 17:40:10.221000",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Website", "module": "Website",
"name": "Blog Post", "name": "Blog Post",


+ 25
- 27
frappe/website/doctype/blog_post/blog_post.py 查看文件

@@ -116,7 +116,7 @@ class BlogPost(WebsiteGenerator):
context.metatags["image"] = self.meta_image or image or None context.metatags["image"] = self.meta_image or image or None


self.load_comments(context) self.load_comments(context)
self.load_feedback(context)
self.load_likes(context)


context.category = frappe.db.get_value( context.category = frappe.db.get_value(
"Blog Category", context.doc.blog_category, ["title", "route"], as_dict=1 "Blog Category", context.doc.blog_category, ["title", "route"], as_dict=1
@@ -164,33 +164,27 @@ class BlogPost(WebsiteGenerator):
context.comment_list = get_comment_list(self.doctype, self.name) context.comment_list = get_comment_list(self.doctype, self.name)


if not context.comment_list: if not context.comment_list:
context.comment_text = 0
context.comment_count = 0
else: else:
context.comment_text = len(context.comment_list)
context.comment_count = len(context.comment_list)


def load_feedback(self, context):
def load_likes(self, context):
user = frappe.session.user user = frappe.session.user


feedback = frappe.get_all(
"Feedback",
fields=["like"],
filters=dict(
reference_doctype=self.doctype,
reference_name=self.name,
ip_address=frappe.local.request_ip,
owner=user,
),
)
filters = {
"comment_type": "Like",
"reference_doctype": self.doctype,
"reference_name": self.name,
}


like_count = 0
context.like_count = frappe.db.count("Comment", filters) or 0


if frappe.db.count("Feedback"):
like_count = frappe.db.count(
"Feedback", filters=dict(reference_doctype=self.doctype, reference_name=self.name, like=True)
)
filters["comment_email"] = user


context.user_feedback = feedback[0] if feedback else ""
context.like_count = like_count
if user == "Guest":
filters["ip_address"] = frappe.local.request_ip

context.like = frappe.db.count("Comment", filters) or 0


def set_read_time(self): def set_read_time(self):
content = self.content or self.content_html or "" content = self.content or self.content_html or ""
@@ -279,12 +273,16 @@ def get_blog_list(
doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by=None doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by=None
): ):
conditions = [] conditions = []
category = filters.blog_category or frappe.utils.escape_html(
frappe.local.form_dict.blog_category or frappe.local.form_dict.category
)
if filters:
if filters.blogger:
conditions.append("t1.blogger=%s" % frappe.db.escape(filters.blogger))
if filters and filters.get("blog_category"):
category = filters.get("blog_category")
else:
category = frappe.utils.escape_html(
frappe.local.form_dict.blog_category or frappe.local.form_dict.category
)

if filters and filters.get("blogger"):
conditions.append("t1.blogger=%s" % frappe.db.escape(filters.get("blogger")))

if category: if category:
conditions.append("t1.blog_category=%s" % frappe.db.escape(category)) conditions.append("t1.blog_category=%s" % frappe.db.escape(category))




+ 16
- 3
frappe/website/doctype/blog_post/templates/blog_post.html 查看文件

@@ -44,8 +44,8 @@
{%- endif -%} {%- endif -%}
<div class="blog-footer"> <div class="blog-footer">
<div class="blog-feedback"> <div class="blog-feedback">
{% if not disable_feedback %}
{% include 'templates/includes/feedback/feedback.html' %}
{% if not disable_likes %}
{% include 'templates/includes/likes/likes.html' %}
{% endif %} {% endif %}
</div> </div>
{% if social_links %} {% if social_links %}
@@ -73,6 +73,19 @@


</div> </div>
<script> <script>
frappe.ready(() => frappe.set_search_path("/blog"))
frappe.ready(() => {
frappe.set_search_path("/blog");

// scroll to comment or like section if url contain hash
if (window.location.hash) {
var hash = window.location.hash;

if ($(hash).length) {
$('html, body').animate({
scrollTop: $(hash).offset().top - 100
}, 900, 'swing');
}
}
})
</script> </script>
{% endblock %} {% endblock %}

+ 23
- 0
frappe/website/doctype/blog_post/test_blog_post.py 查看文件

@@ -152,6 +152,29 @@ class TestBlogPost(FrappeTestCase):
frappe.delete_doc("Blog Post", blog.name) frappe.delete_doc("Blog Post", blog.name)
frappe.delete_doc("Blog Category", blog.blog_category) frappe.delete_doc("Blog Category", blog.blog_category)


def test_like_dislike(self):
test_blog = make_test_blog()

frappe.db.delete("Comment", {"comment_type": "Like", "reference_doctype": "Blog Post"})

from frappe.templates.includes.likes.likes import like

frappe.form_dict.reference_doctype = "Blog Post"
frappe.form_dict.reference_name = test_blog.name
frappe.form_dict.like = True
frappe.local.request_ip = "127.0.0.1"

liked = like()
self.assertEqual(liked, True)

frappe.form_dict.like = False

disliked = like()
self.assertEqual(disliked, False)

frappe.db.delete("Comment", {"comment_type": "Like", "reference_doctype": "Blog Post"})
test_blog.delete()



def scrub(text): def scrub(text):
return WebsiteGenerator.scrub(None, text) return WebsiteGenerator.scrub(None, text)


+ 9
- 9
frappe/website/doctype/blog_settings/blog_settings.json 查看文件

@@ -19,7 +19,7 @@
"cta_label", "cta_label",
"cta_url", "cta_url",
"section_break_12", "section_break_12",
"feedback_limit",
"like_limit",
"column_break_14", "column_break_14",
"comment_limit" "comment_limit"
], ],
@@ -89,13 +89,6 @@
"fieldname": "section_break_12", "fieldname": "section_break_12",
"fieldtype": "Section Break" "fieldtype": "Section Break"
}, },
{
"default": "5",
"description": "Feedback limit per hour",
"fieldname": "feedback_limit",
"fieldtype": "Int",
"label": "Feedback limit"
},
{ {
"default": "5", "default": "5",
"description": "Comment limit per hour", "description": "Comment limit per hour",
@@ -118,13 +111,20 @@
"fieldname": "browse_by_category", "fieldname": "browse_by_category",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Browse by category" "label": "Browse by category"
},
{
"default": "5",
"description": "Like limit per hour",
"fieldname": "like_limit",
"fieldtype": "Int",
"label": "Like limit"
} }
], ],
"icon": "fa fa-cog", "icon": "fa fa-cog",
"idx": 1, "idx": 1,
"issingle": 1, "issingle": 1,
"links": [], "links": [],
"modified": "2021-12-20 13:40:32.312459",
"modified": "2022-07-12 17:45:49.108398",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Website", "module": "Website",
"name": "Blog Settings", "name": "Blog Settings",


+ 2
- 2
frappe/website/doctype/blog_settings/blog_settings.py 查看文件

@@ -15,8 +15,8 @@ class BlogSettings(Document):
clear_cache("writers") clear_cache("writers")




def get_feedback_limit():
return frappe.db.get_single_value("Blog Settings", "feedback_limit") or 5
def get_like_limit():
return frappe.db.get_single_value("Blog Settings", "like_limit") or 5




def get_comment_limit(): def get_comment_limit():


+ 1
- 1
frappe/website/doctype/web_page_view/web_page_view.py 查看文件

@@ -49,4 +49,4 @@ def get_page_view_count(path):




def is_tracking_enabled(): def is_tracking_enabled():
return frappe.db.get_value("Website Settings", "Website Settings", "enable_view_tracking")
return frappe.db.get_single_value("Website Settings", "enable_view_tracking")

+ 1
- 1
frappe/website/doctype/website_settings/google_indexing.py 查看文件

@@ -16,7 +16,7 @@ def authorize_access(reauthorize=False, code=None):
"""If no Authorization code get it from Google and then request for Refresh Token.""" """If no Authorization code get it from Google and then request for Refresh Token."""


oauth_code = ( oauth_code = (
frappe.db.get_value("Website Settings", "Website Settings", "indexing_authorization_code")
frappe.db.get_single_value("Website Settings", "indexing_authorization_code")
if not code if not code
else code else code
) )


+ 1
- 1
frappe/www/contact.py 查看文件

@@ -51,7 +51,7 @@ def send_message(subject="Website Query", message="", sender=""):
return return


# send email # send email
forward_to_email = frappe.db.get_value("Contact Us Settings", None, "forward_to_email")
forward_to_email = frappe.db.get_single_value("Contact Us Settings", "forward_to_email")
if forward_to_email: if forward_to_email:
frappe.sendmail(recipients=forward_to_email, sender=sender, content=message, subject=subject) frappe.sendmail(recipients=forward_to_email, sender=sender, content=message, subject=subject)




+ 1
- 1
package.json 查看文件

@@ -32,7 +32,7 @@
"driver.js": "^0.9.8", "driver.js": "^0.9.8",
"editorjs-undo": "0.1.6", "editorjs-undo": "0.1.6",
"fast-deep-equal": "^2.0.1", "fast-deep-equal": "^2.0.1",
"frappe-charts": "^2.0.0-rc13",
"frappe-charts": "2.0.0-rc22",
"frappe-datatable": "^1.16.4", "frappe-datatable": "^1.16.4",
"frappe-gantt": "^0.6.0", "frappe-gantt": "^0.6.0",
"highlight.js": "^10.4.1", "highlight.js": "^10.4.1",


+ 1
- 0
pyproject.toml 查看文件

@@ -109,3 +109,4 @@ coverage = "~=6.4.1"
Faker = "~=13.12.1" Faker = "~=13.12.1"
pyngrok = "~=5.0.5" pyngrok = "~=5.0.5"
unittest-xml-reporting = "~=3.0.4" unittest-xml-reporting = "~=3.0.4"
watchdog = "~=2.1.9"

+ 4
- 4
yarn.lock 查看文件

@@ -1256,10 +1256,10 @@ fraction.js@^4.1.2:
resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950"
integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==


frappe-charts@^2.0.0-rc13:
version "2.0.0-rc13"
resolved "https://registry.yarnpkg.com/frappe-charts/-/frappe-charts-2.0.0-rc13.tgz#fdb251d7ae311c41e38f90a3ae108070ec6b9072"
integrity sha512-Bv7IfllIrjRbKWHn5b769dOSenqdBixAr6m5kurf8ZUOJSLOgK4HOXItJ7BA8n9PvviH9/k5DaloisjLM2Bm1w==
frappe-charts@^2.0.0-rc22:
version "2.0.0-rc22"
resolved "https://registry.yarnpkg.com/frappe-charts/-/frappe-charts-2.0.0-rc22.tgz#9a5a747febdc381a1d4d7af96e89cf519dfba8c0"
integrity sha512-N7f/8979wJCKjusOinaUYfMxB80YnfuVLrSkjpj4LtyqS0BGS6SuJxUnb7Jl4RWUFEIs7zEhideIKnyLeFZF4Q==


frappe-datatable@^1.16.4: frappe-datatable@^1.16.4:
version "1.16.4" version "1.16.4"


正在加载...
取消
保存