Merged from develop
This commit is contained in:
commit
c03e6a08e9
42 changed files with 1018 additions and 677 deletions
|
|
@ -56,6 +56,7 @@
|
|||
"root": true,
|
||||
"globals": {
|
||||
"frappe": true,
|
||||
"Vue": true,
|
||||
"__": true,
|
||||
"_p": true,
|
||||
"_f": true,
|
||||
|
|
@ -112,11 +113,9 @@
|
|||
"getCookie": true,
|
||||
"getCookies": true,
|
||||
"get_url_arg": true,
|
||||
|
||||
"md5": true,
|
||||
"$": true,
|
||||
"jQuery": true,
|
||||
"Vue": true,
|
||||
"moment": true,
|
||||
"hljs": true,
|
||||
"Awesomplete": true,
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ def handle():
|
|||
|
||||
if call=="method":
|
||||
frappe.local.form_dict.cmd = doctype
|
||||
frappe.local.form_dict.pop("data", None)
|
||||
return frappe.handler.handle()
|
||||
|
||||
elif call=="resource":
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ def load_conf_settings(bootinfo):
|
|||
def load_desktop_icons(bootinfo):
|
||||
from frappe.config import get_modules_from_all_apps_for_user
|
||||
bootinfo.allowed_modules = get_modules_from_all_apps_for_user()
|
||||
bootinfo.home_settings = frappe.db.get_value("User", frappe.session.user, 'home_settings','')
|
||||
|
||||
def get_allowed_pages():
|
||||
return get_user_pages_or_reports('Page')
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ from frappe import _
|
|||
import frappe.model
|
||||
import frappe.utils
|
||||
import json, os
|
||||
from frappe.utils import get_safe_filters
|
||||
|
||||
from six import iteritems, string_types, integer_types
|
||||
|
||||
|
|
@ -370,17 +371,4 @@ def check_parent_permission(parent, child_doctype):
|
|||
if frappe.permissions.has_permission(parent):
|
||||
return
|
||||
# Either parent not passed or the user doesn't have permission on parent doctype of child table!
|
||||
raise frappe.PermissionError
|
||||
|
||||
def get_safe_filters(filters):
|
||||
try:
|
||||
filters = json.loads(filters)
|
||||
|
||||
if isinstance(filters, (integer_types, float)):
|
||||
filters = frappe.as_unicode(filters)
|
||||
|
||||
except (TypeError, ValueError):
|
||||
# filters are not passesd, not json
|
||||
pass
|
||||
|
||||
return filters
|
||||
raise frappe.PermissionError
|
||||
|
|
@ -11,28 +11,22 @@ def get_modules_from_all_apps_for_user(user=None):
|
|||
|
||||
all_modules = get_modules_from_all_apps()
|
||||
user_blocked_modules = frappe.get_doc('User', user).get_blocked_modules()
|
||||
|
||||
allowed_modules_list = [m for m in all_modules if m.get("module_name") not in user_blocked_modules]
|
||||
|
||||
empty_tables_by_module = get_all_empty_tables_by_module()
|
||||
|
||||
home_settings = frappe.db.get_value("User", frappe.session.user, 'home_settings')
|
||||
if home_settings:
|
||||
home_settings = json.loads(home_settings)
|
||||
|
||||
for module in allowed_modules_list:
|
||||
module_name = module["module_name"]
|
||||
module_name = module.get("module_name")
|
||||
|
||||
# Apply onboarding status
|
||||
if module_name in empty_tables_by_module:
|
||||
module["onboard_present"] = 1
|
||||
|
||||
if home_settings:
|
||||
category_settings = home_settings.get(module.get("category"), {}) if module.get("category") else {}
|
||||
if module_name not in category_settings:
|
||||
module["hidden"] = 1
|
||||
else:
|
||||
links = category_settings[module_name]["links"]
|
||||
if links:
|
||||
module["links"] = get_module_link_items_from_list(module["app"], module_name, links.split(","))
|
||||
|
||||
|
||||
|
||||
# Set defaults links
|
||||
module["links"] = get_onboard_items(module["app"], frappe.scrub(module_name))[:5]
|
||||
|
||||
else:
|
||||
module["links"] = get_onboard_items(module["app"], frappe.scrub(module_name))[:6]
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "viewed_by",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
|
|
@ -40,7 +41,7 @@
|
|||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"search_index": 1,
|
||||
"set_only_once": 1,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
|
|
@ -52,6 +53,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "reference_doctype",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
|
|
@ -73,7 +75,7 @@
|
|||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"search_index": 1,
|
||||
"set_only_once": 1,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
|
|
@ -85,6 +87,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "reference_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
|
|
@ -106,7 +109,7 @@
|
|||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"search_index": 1,
|
||||
"set_only_once": 1,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
|
|
@ -122,7 +125,7 @@
|
|||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-01-03 13:04:31.389182",
|
||||
"modified": "2019-03-11 18:28:33.277683",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "View Log",
|
||||
|
|
|
|||
|
|
@ -17,6 +17,9 @@ def get_notification_config():
|
|||
"for_other": {
|
||||
"Likes": "frappe.core.notifications.get_unseen_likes",
|
||||
"Email": "frappe.core.notifications.get_unread_emails",
|
||||
},
|
||||
"for_module": {
|
||||
"Social": "frappe.social.doctype.post.post.get_unseen_post_count"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,21 +10,14 @@ frappe.listview_settings['ToDo'] = {
|
|||
},
|
||||
hide_name_column: true,
|
||||
refresh: function(me) {
|
||||
// override assigned to me by owner
|
||||
if (me.todo_sidebar_setup) return;
|
||||
|
||||
me.page.sidebar.find(".assigned-to-me a").off("click").on("click", function() {
|
||||
me.filter_area.remove("assigned_by");
|
||||
me.filter_area.add([[me.doctype, "owner", '=', frappe.session.user]]);
|
||||
});
|
||||
|
||||
// add assigned by me
|
||||
me.page.add_sidebar_item(__("Assigned By Me"), function() {
|
||||
me.filter_area.remove("owner");
|
||||
me.filter_area.add([[me.doctype, "assigned_by", '=', frappe.session.user]]);
|
||||
}, ".assigned-to-me");
|
||||
}, ('.list-link[data-view="Kanban"]'));
|
||||
|
||||
me.todo_sidebar_setup = true;
|
||||
},
|
||||
add_fields: ["reference_type", "reference_name"],
|
||||
}
|
||||
}
|
||||
|
|
@ -24,3 +24,10 @@ def set_list_settings(doctype, values):
|
|||
frappe.clear_messages()
|
||||
doc.update(json.loads(values))
|
||||
doc.save()
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_user_assignments_and_count():
|
||||
user_list = frappe.get_list("User", filters={"user_type": "System User"})
|
||||
assignment_data = sorted([{"count":frappe.db.count('ToDo', filters = {'reference_type': 'Issue', 'owner': user['name'], 'status': 'Open'}),
|
||||
"name": user['name']} for user in user_list], key=lambda k: k['count'], reverse = True)
|
||||
return assignment_data
|
||||
|
|
@ -300,18 +300,32 @@ def get_links(app, module):
|
|||
for section in sections:
|
||||
for item in section["items"]:
|
||||
link_names.append(item.get("label"))
|
||||
print(link_names)
|
||||
return link_names
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_module_link_items_from_dict(module_link_list_map):
|
||||
module_link_list_map = json.loads(module_link_list_map)
|
||||
module_links = {}
|
||||
for module, data in module_link_list_map.items():
|
||||
print(data)
|
||||
module_links[module] = get_module_link_items_from_list(data["app"], module, data["links"])
|
||||
return module_links
|
||||
def update_desk_section_settings(desk_section, new_settings):
|
||||
home_settings = frappe.db.get_value("User", frappe.session.user, 'home_settings')
|
||||
if home_settings:
|
||||
home_settings = json.loads(home_settings)
|
||||
else:
|
||||
return {}
|
||||
|
||||
new_settings = json.loads(new_settings)
|
||||
|
||||
for module, data in new_settings.items():
|
||||
if data.get("links"):
|
||||
data["links"] = get_module_link_items_from_list(data["app"], module, data.get("links"))
|
||||
data.pop("app", None)
|
||||
|
||||
home_settings[desk_section] = new_settings
|
||||
settings_json_str = json.dumps(home_settings)
|
||||
# # This didn't work
|
||||
# frappe.db.set_value("User", frappe.session.user, 'home_settings', json.dumps(home_settings))
|
||||
frappe.db.sql("""update tabUser set home_settings = %s""", (settings_json_str), debug=True)
|
||||
frappe.db.commit()
|
||||
|
||||
return new_settings
|
||||
|
||||
|
||||
def get_module_link_items_from_list(app, module, list_of_link_names):
|
||||
|
|
|
|||
|
|
@ -1154,7 +1154,7 @@ class Document(BaseDocument):
|
|||
frappe.local.flags.commit = True
|
||||
|
||||
def add_viewed(self, user=None):
|
||||
'''add log to communication when a user viewes a document'''
|
||||
'''add log to communication when a user views a document'''
|
||||
if not user:
|
||||
user = frappe.session.user
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ execute:frappe.reload_doc('core', 'doctype', 'comment')
|
|||
frappe.patches.v8_0.drop_is_custom_from_docperm
|
||||
execute:frappe.reload_doc('core', 'doctype', 'module_def') #2017-09-22
|
||||
execute:frappe.reload_doc('core', 'doctype', 'version') #2017-04-01
|
||||
execute:frappe.reload_doc('email', 'doctype', 'document_follow')
|
||||
frappe.patches.v11_0.replicate_old_user_permissions
|
||||
frappe.patches.v11_0.reload_and_rename_view_log #2019-01-03
|
||||
frappe.patches.v7_1.rename_scheduler_log_to_error_log
|
||||
|
|
@ -200,7 +201,6 @@ frappe.patches.v9_1.move_feed_to_activity_log
|
|||
execute:frappe.delete_doc('Page', 'data-import-tool', ignore_missing=True)
|
||||
frappe.patches.v10_0.reload_countries_and_currencies
|
||||
frappe.patches.v10_0.refactor_social_login_keys
|
||||
execute:frappe.reload_doc('email', 'doctype', 'document_follow')
|
||||
frappe.patches.v10_0.enable_chat_by_default_within_system_settings
|
||||
frappe.patches.v10_0.remove_custom_field_for_disabled_domain
|
||||
execute:frappe.delete_doc("Page", "chat")
|
||||
|
|
@ -232,8 +232,7 @@ frappe.patches.v11_0.set_missing_creation_and_modified_value_for_user_permission
|
|||
frappe.patches.v11_0.remove_doctype_user_permissions_for_page_and_report
|
||||
frappe.patches.v11_0.set_default_letter_head_source
|
||||
frappe.patches.v12_0.set_primary_key_in_series
|
||||
execute:frappe.reload_doc('email', 'doctype', 'document_follow')
|
||||
execute:frappe.delete_doc("Page", "modules", ignore_missing=True)
|
||||
frappe.patches.v11_0.set_default_letter_head_source
|
||||
frappe.patches.v12_0.setup_comments_from_communications
|
||||
frappe.patches.v12_0.init_desk_settings
|
||||
frappe.patches.v12_0.init_desk_settings #11-03-2019
|
||||
|
|
|
|||
|
|
@ -6,29 +6,6 @@ from frappe.config import get_modules_from_all_apps_for_user
|
|||
from frappe.desk.moduleview import get_onboard_items
|
||||
|
||||
def execute():
|
||||
"""Set the initial customizations for desk, with modules, indices and links."""
|
||||
"""Reset the initial customizations for desk, with modules, indices and links."""
|
||||
frappe.reload_doc("core", "doctype", "user")
|
||||
all_modules = get_modules_from_all_apps_for_user()
|
||||
|
||||
settings = {}
|
||||
|
||||
for module in all_modules:
|
||||
if not module.get("app"): continue
|
||||
|
||||
links = get_onboard_items(module["app"], frappe.scrub(module["module_name"]))[:5]
|
||||
module_settings = {
|
||||
"links": ",".join([d["label"] for d in links])
|
||||
}
|
||||
category_dict = settings.get(module.get("category", ""), None)
|
||||
if category_dict:
|
||||
module_settings["index"] = len(category_dict)
|
||||
category_dict[module.get("module_name")] = module_settings
|
||||
else:
|
||||
module_settings["index"] = 0
|
||||
settings[module.get("category", "")] = {
|
||||
module.get("module_name"): module_settings
|
||||
}
|
||||
|
||||
settings_json_str = json.dumps(settings)
|
||||
|
||||
frappe.db.sql("""update tabUser set home_settings = %s""", (settings_json_str), debug=True)
|
||||
frappe.db.sql("""update tabUser set home_settings = %s""", (''), debug=True)
|
||||
|
|
|
|||
|
|
@ -22,9 +22,6 @@
|
|||
"js/chat.js": [
|
||||
"public/js/frappe/chat.js"
|
||||
],
|
||||
"js/frappe-vue.min.js": [
|
||||
"public/js/frappe_vue.js"
|
||||
],
|
||||
"js/frappe-recorder.min.js": [
|
||||
"public/js/frappe/recorder/recorder.js"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ frappe.Application = Class.extend({
|
|||
throw 'boot failed';
|
||||
}
|
||||
|
||||
this.setup_frappe_vue();
|
||||
this.load_bootinfo();
|
||||
this.load_user_permissions();
|
||||
this.make_nav_bar();
|
||||
|
|
@ -106,6 +107,8 @@ frappe.Application = Class.extend({
|
|||
dialog.get_close_btn().toggle(false);
|
||||
});
|
||||
|
||||
this.setup_social_listeners();
|
||||
|
||||
// listen to build errors
|
||||
this.setup_build_error_listener();
|
||||
|
||||
|
|
@ -119,6 +122,12 @@ frappe.Application = Class.extend({
|
|||
}
|
||||
|
||||
},
|
||||
|
||||
setup_frappe_vue() {
|
||||
Vue.prototype.__ = window.__;
|
||||
Vue.prototype.frappe = window.frappe;
|
||||
},
|
||||
|
||||
set_password: function(user) {
|
||||
var me=this;
|
||||
frappe.call({
|
||||
|
|
@ -530,6 +539,14 @@ frappe.Application = Class.extend({
|
|||
console.log(data);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
setup_social_listeners() {
|
||||
frappe.realtime.on('mention', (message) => {
|
||||
if (frappe.get_route()[0] !== 'social') {
|
||||
frappe.show_alert(message);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ frappe.dom = {
|
|||
return txt;
|
||||
}
|
||||
},
|
||||
is_element_in_viewport: function (el) {
|
||||
is_element_in_viewport: function (el, tolerance=0) {
|
||||
|
||||
//special bonus for those using jQuery
|
||||
if (typeof jQuery === "function" && el instanceof jQuery) {
|
||||
|
|
@ -78,10 +78,10 @@ frappe.dom = {
|
|||
var rect = el.getBoundingClientRect();
|
||||
|
||||
return (
|
||||
rect.top >= 0
|
||||
&& rect.left >= 0
|
||||
// && rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
|
||||
// && rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
|
||||
rect.top + tolerance >= 0
|
||||
&& rect.left + tolerance >= 0
|
||||
&& rect.bottom - tolerance <= $(window).height()
|
||||
&& rect.right - tolerance <= $(window).width()
|
||||
);
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -368,7 +368,6 @@ frappe.ui.form.Timeline = class Timeline {
|
|||
} else {
|
||||
c.content_html = c.content;
|
||||
c.content_html = frappe.utils.strip_whitespace(c.content_html);
|
||||
c.content_html = c.content_html.replace(/</g,"<").replace(/>/g,">");
|
||||
}
|
||||
|
||||
// bold @mentions
|
||||
|
|
|
|||
|
|
@ -45,8 +45,12 @@
|
|||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
<li class="assigned-to-me">
|
||||
<a>{%= __("Assigned To Me") %}</a>
|
||||
<li class="assigned-to">
|
||||
<a class = "dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
{%= __("Assigned To") %} <span class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu assigned-dropdown" style="max-height: 300px; overflow-y: auto;">
|
||||
</ul>
|
||||
</li>
|
||||
{% if(frappe.help.has_help(doctype)) { %}
|
||||
<li><a class="help-link" data-doctype="{{ doctype }}">{{ __("Help") }}</a></li>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ frappe.views.ListSidebar = class ListSidebar {
|
|||
|
||||
this.setup_reports();
|
||||
this.setup_list_filter();
|
||||
this.setup_assigned_to_me();
|
||||
this.setup_assigned_to();
|
||||
this.setup_views();
|
||||
this.setup_kanban_boards();
|
||||
this.setup_calendar_view();
|
||||
|
|
@ -216,12 +216,35 @@ frappe.views.ListSidebar = class ListSidebar {
|
|||
});
|
||||
}
|
||||
|
||||
setup_assigned_to_me() {
|
||||
this.page.sidebar.find(".assigned-to-me a").on("click", () => {
|
||||
this.list_view.filter_area.add(this.list_view.doctype, "_assign", "like", `%${frappe.session.user}%`);
|
||||
setup_assigned_to() {
|
||||
let dropdown = this.page.sidebar.find('.assigned-dropdown');
|
||||
if(this.doctype === 'ToDo') {
|
||||
$('.assigned-to').remove();
|
||||
}
|
||||
frappe.call('frappe.desk.listview.get_user_assignments_and_count').then((data) => {
|
||||
let current_user_count = data.message.find(user => user.name === frappe.session.user).count;
|
||||
this.get_html_for_assigned(frappe.session.user, current_user_count).appendTo(dropdown);
|
||||
let user_list = data.message.filter(user => !['Guest', frappe.session.user, 'Administrator'].includes(user.name));
|
||||
user_list.forEach((user) => {
|
||||
this.get_html_for_assigned(user.name, user.count).appendTo(dropdown);
|
||||
});
|
||||
$(".assigned-dropdown li a").on("click", (e) => {
|
||||
let assigned_user = $(e.currentTarget).find($('.assigned-user')).text();
|
||||
if(assigned_user === 'Me') assigned_user = frappe.session.user;
|
||||
this.list_view.filter_area.remove('_assign');
|
||||
this.list_view.filter_area.add(this.list_view.doctype, "_assign", "like", `%${assigned_user}%`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
get_html_for_assigned(name, count) {
|
||||
if (name === frappe.session.user) name='Me';
|
||||
if (count > 99) count='99+';
|
||||
let html = $('<li class="assigned"><a class="badge-hover" role="assigned-item"><span class="assigned-user">'
|
||||
+ name + '</span><span class="badge pull-right" style="position:relative">' + count + '</span></a></li>');
|
||||
return html;
|
||||
}
|
||||
|
||||
setup_upgrade_box() {
|
||||
let upgrade_list = $(`<ul class="list-unstyled sidebar-menu"></ul>`).appendTo(this.sidebar);
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
import Wall from './pages/Wall.vue';
|
||||
import Profile from './pages/Profile.vue';
|
||||
import UserList from './pages/UserList.vue';
|
||||
import NotFound from './components/NotFound.vue';
|
||||
import ImageViewer from './components/ImageViewer.vue';
|
||||
|
||||
|
|
@ -27,6 +28,10 @@ function get_route_map() {
|
|||
'key': frappe.get_route()[2]
|
||||
}
|
||||
},
|
||||
'social/users': {
|
||||
'component': UserList,
|
||||
'props': {}
|
||||
},
|
||||
'not_found': {
|
||||
'component': NotFound,
|
||||
}
|
||||
|
|
@ -58,11 +63,14 @@ export default {
|
|||
frappe.app_updates.on('user_image_updated', () => {
|
||||
this.$root.$emit('user_image_updated')
|
||||
})
|
||||
|
||||
this.update_primary_action(frappe.get_route()[1])
|
||||
},
|
||||
mounted() {
|
||||
frappe.route.on('change', () => {
|
||||
if (frappe.get_route()[0] === 'social') {
|
||||
this.set_current_page();
|
||||
this.update_primary_action(frappe.get_route()[1])
|
||||
frappe.utils.scroll_to(0);
|
||||
$("body").attr("data-route", frappe.get_route_str());
|
||||
}
|
||||
|
|
@ -73,6 +81,15 @@ export default {
|
|||
set_current_page() {
|
||||
this.current_page = this.get_current_page();
|
||||
},
|
||||
update_primary_action(current_route) {
|
||||
if (current_route === 'home') {
|
||||
this.$root.page.set_primary_action(__('Post'), () => {
|
||||
frappe.social.post_dialog.show();
|
||||
});
|
||||
} else {
|
||||
this.$root.page.clear_primary_action()
|
||||
}
|
||||
},
|
||||
get_current_page() {
|
||||
const route_map = get_route_map();
|
||||
const route = frappe.get_route_str();
|
||||
|
|
|
|||
|
|
@ -12,6 +12,12 @@
|
|||
v-if="options.length"
|
||||
:options="options"
|
||||
/>
|
||||
<transition name="fade">
|
||||
<span
|
||||
class="indicator blue"
|
||||
v-if="!this.post.seen">
|
||||
</span>
|
||||
</transition>
|
||||
</div>
|
||||
<div class="user-avatar" v-html="user_avatar" @click="goto_profile(post.owner)"></div>
|
||||
<a class="user-name" @click="goto_profile(post.owner)">{{ user_name }}</a>
|
||||
|
|
@ -170,6 +176,11 @@ export default {
|
|||
}).then(frappe.dom.unfreeze)
|
||||
})
|
||||
},
|
||||
update_seen() {
|
||||
frappe.xcall('frappe.social.doctype.post.post.set_seen', {
|
||||
post_name: this.post.name
|
||||
}).then(() => this.post.seen = true)
|
||||
},
|
||||
generate_preview(link_element) {
|
||||
// TODO: move the code to separate component
|
||||
frappe.xcall('frappe.social.doctype.post.post.get_link_info', {
|
||||
|
|
@ -203,5 +214,14 @@ export default {
|
|||
padding-top: 0px;
|
||||
background: #F6F6F6;
|
||||
}
|
||||
.indicator {
|
||||
margin-left: 15px;
|
||||
}
|
||||
.fade-enter-active, .fade-leave-active {
|
||||
transition: opacity .8s;
|
||||
}
|
||||
.fade-enter, .fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ export default {
|
|||
cursor: pointer;
|
||||
color: #8d99a6;
|
||||
span {
|
||||
padding-left: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
&:hover {
|
||||
color: darken(#8d99a6, 10%);
|
||||
|
|
|
|||
|
|
@ -2,26 +2,30 @@
|
|||
<div>
|
||||
<div class="comment-box flex-column">
|
||||
<div class="text-muted comment-label">{{ __('Add a comment') }}</div>
|
||||
<textarea v-model="comment_content"></textarea>
|
||||
<button
|
||||
:disabled="comment_content === ''"
|
||||
class="btn btn-primary btn-sm"
|
||||
@click="create_comment">
|
||||
{{ __('Comment') }}
|
||||
</button>
|
||||
<div ref="comment-section"></div>
|
||||
<div class="flex justify-between">
|
||||
<div class="text-muted small">
|
||||
{{ __("Ctrl+Enter to add comment") }}
|
||||
</div>
|
||||
<button
|
||||
class="btn btn-primary btn-sm"
|
||||
@click="create_comment">
|
||||
{{ __('Comment') }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="comments.length" class="comment-list">
|
||||
<div ref="comments" v-if="comments.length" class="comment-list">
|
||||
<div class="comment" v-for="comment in comments" :key="comment.name">
|
||||
<span
|
||||
class="pull-right text-muted"
|
||||
v-html="get_time(comment.creation)">
|
||||
</span>
|
||||
<span
|
||||
class="cursor-pointer"
|
||||
@click="go_to_profile_page(comment.owner)"
|
||||
v-html="get_avatar(comment.owner)">
|
||||
</span>
|
||||
<span>{{ comment.content }}</span>
|
||||
<span class="content" v-html="comment.content"/>
|
||||
<span
|
||||
class="text-muted"
|
||||
v-html="get_time(comment.creation)">
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -29,10 +33,9 @@
|
|||
<script>
|
||||
export default {
|
||||
props: ['comments'],
|
||||
data() {
|
||||
return {
|
||||
comment_content: ''
|
||||
}
|
||||
mounted() {
|
||||
this.make_comment_section();
|
||||
this.make_mentions_clickable(this.$refs['comments']);
|
||||
},
|
||||
methods: {
|
||||
get_avatar(user) {
|
||||
|
|
@ -41,12 +44,45 @@ export default {
|
|||
get_time(timestamp) {
|
||||
return comment_when(timestamp, true)
|
||||
},
|
||||
create_comment() {
|
||||
this.$emit('create_comment', this.comment_content);
|
||||
this.comment_content = '';
|
||||
},
|
||||
go_to_profile_page(user) {
|
||||
frappe.set_route('social', 'profile', user)
|
||||
},
|
||||
make_comment_section() {
|
||||
this.comment_section = frappe.ui.form.make_control({
|
||||
parent: this.$refs['comment-section'],
|
||||
only_input: true,
|
||||
render_input: true,
|
||||
no_wrapper: true,
|
||||
mentions: this.get_names_for_mentions(),
|
||||
df: {
|
||||
fieldtype: 'Comment',
|
||||
fieldname: 'comment'
|
||||
},
|
||||
on_submit: this.create_comment.bind(this)
|
||||
});
|
||||
},
|
||||
create_comment() {
|
||||
const message = this.comment_section.get_value().replace('<div><br></div>', '');
|
||||
if (!strip_html(message)) return
|
||||
frappe.utils.play_sound("click");
|
||||
this.$emit('create_comment', message);
|
||||
this.comment_section.clear();
|
||||
},
|
||||
get_names_for_mentions() {
|
||||
var valid_users = Object.keys(frappe.boot.user_info)
|
||||
.filter(user => !["Administrator", "Guest"].includes(user));
|
||||
valid_users = valid_users
|
||||
.filter(user => frappe.boot.user_info[user].allowed_in_mentions==1);
|
||||
return valid_users.map(user => frappe.boot.user_info[user].name);
|
||||
},
|
||||
make_mentions_clickable(parent_element) {
|
||||
Array.from(parent_element.getElementsByClassName('mention'))
|
||||
.forEach((mention) => {
|
||||
mention.classList.add('cursor-pointer');
|
||||
mention.addEventListener('click', () => {
|
||||
this.go_to_profile_page(mention.dataset.value)
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -56,16 +92,11 @@ export default {
|
|||
.comment-label {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
textarea {
|
||||
width: 100%;
|
||||
::v-deep .ql-editor {
|
||||
background: white;
|
||||
border-radius: 4px;
|
||||
outline: none;
|
||||
border: none;
|
||||
margin-bottom: 15px;
|
||||
height: 60px;
|
||||
padding: 5px;
|
||||
min-height: 60px !important;
|
||||
border: 1px solid #d1d8dd;
|
||||
resize: none;
|
||||
}
|
||||
button {
|
||||
padding: 2px 5px;
|
||||
|
|
@ -76,7 +107,17 @@ export default {
|
|||
.comment-list {
|
||||
margin-top: 10px;
|
||||
.comment {
|
||||
.comment-input-wrapper {
|
||||
margin-top: -6px;
|
||||
font-size: 11px;
|
||||
}
|
||||
display: flex;
|
||||
padding: 5px 0;
|
||||
.content {
|
||||
align-self: center;
|
||||
font-size: 12px;
|
||||
flex: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
<template>
|
||||
<div>
|
||||
<post-skeleton v-for="index in 5" :key="index" v-if="loading_posts && !posts.length"/>
|
||||
<div v-if="loading_posts && !posts.length">
|
||||
<post-skeleton v-for="index in 5" :key="index"/>
|
||||
</div>
|
||||
<transition-group name="flip-list">
|
||||
<post
|
||||
<post ref="posts"
|
||||
:post="post"
|
||||
v-for="(post, index) in posts"
|
||||
:key="post.name"
|
||||
|
|
@ -61,11 +63,9 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
get_posts(filters, load_old) {
|
||||
return frappe.db.get_list('Post', {
|
||||
fields: ['name', 'content', 'owner', 'creation', 'liked_by', 'is_pinned', 'is_globally_pinned'],
|
||||
filters: filters,
|
||||
limit_start: load_old ? this.posts.length : 0,
|
||||
order_by: 'is_globally_pinned desc, creation desc',
|
||||
return frappe.xcall('frappe.social.doctype.post.post.get_posts', {
|
||||
filters,
|
||||
limit_start: load_old ? this.posts.length : 0
|
||||
})
|
||||
},
|
||||
update_posts(load_old = false) {
|
||||
|
|
@ -85,9 +85,11 @@ export default {
|
|||
}
|
||||
}).finally(() => {
|
||||
this.loading_posts = false;
|
||||
this.track_seen()
|
||||
});
|
||||
},
|
||||
handle_scroll: frappe.utils.debounce(function() {
|
||||
this.track_seen()
|
||||
const screen_bottom = document.documentElement.scrollTop + window.innerHeight === document.documentElement.offsetHeight;
|
||||
if (screen_bottom && this.more_posts_available) {
|
||||
if (!this.loading_posts) {
|
||||
|
|
@ -95,6 +97,15 @@ export default {
|
|||
}
|
||||
}
|
||||
}, 500),
|
||||
track_seen() {
|
||||
const posts = this.$refs.posts || []
|
||||
posts.forEach((post_component) => {
|
||||
if(!post_component.post.seen
|
||||
&& frappe.dom.is_element_in_viewport(post_component.$el, 50)) {
|
||||
post_component.update_seen()
|
||||
}
|
||||
})
|
||||
},
|
||||
delete_post(index) {
|
||||
this.posts.splice(index, 1);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
<template>
|
||||
<div class="flex flex-column">
|
||||
<div class="user-details">
|
||||
<div class="user-avatar" v-html="user_avatar"></div>
|
||||
<a class="user_name" @click="go_to_profile_page()">{{ user.fullname }}</a>
|
||||
</div>
|
||||
<a class="route-link"
|
||||
@click.prevent="go_to_user_list()">
|
||||
{{ __('All Users') }}
|
||||
</a>
|
||||
<div class="links" v-if="frequently_visited_list.length">
|
||||
<div class="muted-title">
|
||||
{{ __('Frequently Visited Links') }}
|
||||
|
|
@ -24,8 +24,6 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
frequently_visited_list: [],
|
||||
user: frappe.user_info(frappe.session.user),
|
||||
user_avatar: frappe.avatar(this.user_id, 'avatar-xl')
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
|
@ -45,7 +43,10 @@ export default {
|
|||
return frappe.utils.get_route_label(route);
|
||||
},
|
||||
go_to_profile_page() {
|
||||
frappe.set_route('social', 'profile', this.user.name)
|
||||
frappe.set_route('social', 'profile', frappe.session.user)
|
||||
},
|
||||
go_to_user_list() {
|
||||
frappe.set_route('social', 'users')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -58,9 +59,6 @@ export default {
|
|||
.stats {
|
||||
min-height: 150px
|
||||
}
|
||||
.links {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.user-details {
|
||||
.user-avatar {
|
||||
/deep/.avatar-xl {
|
||||
|
|
|
|||
95
frappe/public/js/frappe/social/pages/UserList.vue
Normal file
95
frappe/public/js/frappe/social/pages/UserList.vue
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
<template>
|
||||
<div class="flex justify-center">
|
||||
<div class="col-md-6">
|
||||
<div class="flex justify-between padding search-bar">
|
||||
<div class="flex col-md-6">
|
||||
<button class="btn" @click="frappe.set_route('social', 'home')">← {{ __('Back') }}</button>
|
||||
<input type="text" class="form-control" :placeholder="__('Search for a user...')" v-model="search_text">
|
||||
</div>
|
||||
</div>
|
||||
<ul class="list-unstyled user-list">
|
||||
<li
|
||||
class="padding cursor-pointer flex user-card"
|
||||
v-for="user in filtered_users" :key="user.name"
|
||||
@click="go_to_profile_page(user.name)">
|
||||
<span v-html="get_avatar(user.name)"></span>
|
||||
<div class="user-details">
|
||||
{{ user.fullname }}
|
||||
<div class="text-muted text-medium" :class="{'italic': !user.bio}">{{ frappe.ellipsis(user.bio, 100) || 'No Bio'}}</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="text-muted" v-if="!filtered_users.length">{{__('No user found')}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
users: [],
|
||||
search_text: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filtered_users() {
|
||||
let filtered = this.users;
|
||||
if (this.search_text !== '') {
|
||||
filtered = filtered.filter(user => {
|
||||
if (user.fullname.toLowerCase().includes(this.search_text.toLowerCase())) {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const standard_users = ['Administrator', 'Guest', 'guest@example.com'];
|
||||
this.users = frappe.boot.user_info;
|
||||
// delete standard users from the list
|
||||
standard_users.forEach(user => delete this.users[user]);
|
||||
this.users = Object.values(this.users);
|
||||
},
|
||||
methods: {
|
||||
get_avatar(user) {
|
||||
return frappe.avatar(user, 'avatar-medium')
|
||||
},
|
||||
go_to_profile_page(user) {
|
||||
frappe.set_route('social', 'profile', user)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.user-list {
|
||||
// similar to search bar height
|
||||
margin-top: 75px;
|
||||
.user-card {
|
||||
&:hover {
|
||||
border: 1px solid #d1d8dd;
|
||||
}
|
||||
border-radius: 5px;
|
||||
.user-details {
|
||||
margin-left: 10px;
|
||||
.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.search-bar {
|
||||
position: fixed;
|
||||
background: white;
|
||||
height: 75px;
|
||||
text-align: center;
|
||||
div {
|
||||
margin: auto;
|
||||
}
|
||||
width: 100%;
|
||||
left: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
|
|
@ -8,25 +8,20 @@ frappe.social.Home = class SocialHome {
|
|||
this.page = parent.page;
|
||||
this.setup_header();
|
||||
this.make_body();
|
||||
this.set_primary_action();
|
||||
}
|
||||
make_body() {
|
||||
this.$social_container = this.$parent.find('.layout-main');
|
||||
frappe.require('/assets/js/frappe-vue.min.js', () => {
|
||||
new Vue({
|
||||
el: this.$social_container[0],
|
||||
render: h => h(Home)
|
||||
});
|
||||
new Vue({
|
||||
el: this.$social_container[0],
|
||||
render: h => h(Home),
|
||||
data: {
|
||||
'page': this.page
|
||||
}
|
||||
});
|
||||
}
|
||||
setup_header() {
|
||||
this.page.set_title(__('Social'));
|
||||
}
|
||||
set_primary_action() {
|
||||
this.page.set_primary_action(__('Post'), () => {
|
||||
frappe.social.post_dialog.show();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
frappe.social.post_dialog = new frappe.ui.Dialog({
|
||||
|
|
|
|||
|
|
@ -85,26 +85,30 @@ frappe.ui.click_toggle_like = function() {
|
|||
return false;
|
||||
}
|
||||
|
||||
frappe.ui.setup_like_popover = function($parent, selector, check_not_liked=true) {
|
||||
frappe.ui.setup_like_popover = ($parent, selector, check_not_liked=true) => {
|
||||
if (frappe.dom.is_touchscreen()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$parent.on("mouseover", selector, function() {
|
||||
var $wrapper = $(this);
|
||||
|
||||
$wrapper.popover({
|
||||
$parent.on('mouseover', selector, function() {
|
||||
const target_element = $(this);
|
||||
target_element.popover({
|
||||
animation: true,
|
||||
placement: "right",
|
||||
content: function() {
|
||||
var liked_by = $wrapper.attr('data-liked-by');
|
||||
placement: 'right',
|
||||
trigger: 'manual',
|
||||
template:`<div class="liked-by-popover popover">
|
||||
<div class="arrow"></div>
|
||||
<div class="popover-content"></div>
|
||||
</div>`,
|
||||
content: () => {
|
||||
let liked_by = target_element.attr('data-liked-by');
|
||||
liked_by = liked_by ? decodeURI(liked_by) : '[]';
|
||||
liked_by = JSON.parse(liked_by);
|
||||
|
||||
var user = frappe.session.user;
|
||||
const user = frappe.session.user;
|
||||
// hack
|
||||
if (check_not_liked) {
|
||||
if ($wrapper.find(".not-liked").length) {
|
||||
if (target_element.find(".not-liked").length) {
|
||||
if (liked_by.indexOf(user)!==-1) {
|
||||
liked_by.splice(liked_by.indexOf(user), 1);
|
||||
}
|
||||
|
|
@ -118,16 +122,46 @@ frappe.ui.setup_like_popover = function($parent, selector, check_not_liked=true)
|
|||
if (!liked_by.length) {
|
||||
return "";
|
||||
}
|
||||
return frappe.render_template("liked_by", {"liked_by": liked_by});
|
||||
|
||||
let liked_by_list = $(`<ul class="list-unstyled"></ul>`);
|
||||
|
||||
// to show social profile of the user
|
||||
let link_base = '#social/profile/';
|
||||
|
||||
liked_by.forEach(user => {
|
||||
// append user list item
|
||||
liked_by_list.append(`
|
||||
<li data-user=${user}>${frappe.avatar(user)}
|
||||
<span>${frappe.user.full_name(user)}</span>
|
||||
</li>
|
||||
`);
|
||||
});
|
||||
|
||||
liked_by_list.children('li').click(ev => {
|
||||
let user = ev.currentTarget.dataset.user;
|
||||
target_element.popover('hide');
|
||||
frappe.set_route(link_base + user);
|
||||
});
|
||||
|
||||
return liked_by_list;
|
||||
},
|
||||
html: true,
|
||||
container: 'body'
|
||||
});
|
||||
|
||||
$wrapper.popover('show');
|
||||
target_element.popover('show');
|
||||
|
||||
$(".popover").on("mouseleave", () => {
|
||||
target_element.popover('hide');
|
||||
});
|
||||
|
||||
target_element.on('mouseout', () => {
|
||||
setTimeout(() => {
|
||||
if (!$('.popover:hover').length) {
|
||||
target_element.popover('hide');
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
});
|
||||
|
||||
$parent.on("mouseout", selector, function() {
|
||||
$(this).popover('destroy');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
<ul class="list-unstyled liked-by-popover">
|
||||
{% for (var i in liked_by) { var liked_by_user = liked_by[i]; %}
|
||||
<li>
|
||||
{%= frappe.avatar(liked_by_user) %}
|
||||
<span>{%= frappe.user.full_name(liked_by_user) %}</span>
|
||||
</li>
|
||||
{% } %}
|
||||
</ul>
|
||||
|
|
@ -28,192 +28,216 @@ import DeskModuleBox from "./DeskModuleBox.vue";
|
|||
import { generate_route } from "./utils.js";
|
||||
|
||||
export default {
|
||||
props: ["category", "all_modules"],
|
||||
components: {
|
||||
DeskModuleBox
|
||||
},
|
||||
data() {
|
||||
let template_modules = this.all_modules;
|
||||
template_modules.forEach(module => {
|
||||
if (module.links) {
|
||||
module.links.forEach(link => {
|
||||
link.route = generate_route(link);
|
||||
});
|
||||
}
|
||||
});
|
||||
props: ['category', 'all_modules', 'customization_settings'],
|
||||
components: {
|
||||
DeskModuleBox
|
||||
},
|
||||
data() {
|
||||
let default_modules = this.all_modules;
|
||||
let modules = this.get_customized_modules(default_modules, this.customization_settings);
|
||||
|
||||
return {
|
||||
template_modules: template_modules,
|
||||
modules: template_modules.slice(),
|
||||
settings: {},
|
||||
all_settings: {},
|
||||
dragged_index: -1,
|
||||
hovered_index: -1
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
show_customize_dialog() {
|
||||
if (!this.dialog) {
|
||||
this.get_settings().then(() => {
|
||||
const fields = this.make_fields();
|
||||
this.make_and_show_dialog(fields);
|
||||
});
|
||||
} else {
|
||||
this.dialog.show();
|
||||
}
|
||||
},
|
||||
get_settings() {
|
||||
return frappe.db.get_value("User", user, "home_settings").then(resp => {
|
||||
this.all_settings = JSON.parse(resp.message["home_settings"]);
|
||||
this.settings = this.all_settings[this.category];
|
||||
});
|
||||
},
|
||||
make_fields() {
|
||||
let fields = [];
|
||||
let template_modules = this.template_modules;
|
||||
let selected_modules = Object.keys(this.settings);
|
||||
return {
|
||||
default_modules: default_modules,
|
||||
modules: modules,
|
||||
new_settings: {},
|
||||
dragged_index: -1,
|
||||
hovered_index: -1,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
show_customize_dialog() {
|
||||
if(!this.dialog) {
|
||||
const fields = this.make_fields();
|
||||
this.make_and_show_dialog(fields);
|
||||
} else {
|
||||
this.dialog.show();
|
||||
}
|
||||
},
|
||||
make_fields() {
|
||||
let fields = [];
|
||||
this.modules.forEach(module => {
|
||||
fields.push(this.get_module_select_field(module));
|
||||
|
||||
template_modules.forEach(module => {
|
||||
fields.push(this.get_module_select_field(module, selected_modules));
|
||||
if(module.links) {
|
||||
fields.push(this.get_links_multiselect_field(module));
|
||||
}
|
||||
});
|
||||
return fields;
|
||||
},
|
||||
make_and_show_dialog(fields) {
|
||||
this.dialog = new frappe.ui.Dialog({
|
||||
title: __("Customize " + this.category),
|
||||
fields: fields,
|
||||
primary_action_label: __("Update"),
|
||||
primary_action: (values) => {
|
||||
this.update_settings(values);
|
||||
}
|
||||
});
|
||||
|
||||
if (module.links) {
|
||||
fields.push(this.get_links_multiselect_field(module));
|
||||
}
|
||||
});
|
||||
this.dialog.modal_body.find('.clearfix').css({'display': 'none'});
|
||||
this.dialog.modal_body.find('.frappe-control*[data-fieldtype="MultiSelect"]').css({'margin-bottom': '30px'});
|
||||
|
||||
return fields;
|
||||
},
|
||||
make_and_show_dialog(fields) {
|
||||
this.dialog = new frappe.ui.Dialog({
|
||||
title: __("Customize " + this.category),
|
||||
fields: fields,
|
||||
primary_action_label: __("Update"),
|
||||
primary_action: values => {
|
||||
let module_link_list_map = {};
|
||||
this.dialog.show();
|
||||
},
|
||||
|
||||
Object.keys(values).forEach(module_name => {
|
||||
if (!module_name.includes("links") && values[module_name]) {
|
||||
const links_str = values[module_name + "_links"] || "";
|
||||
this.settings[module_name]["links"] = links_str;
|
||||
if (values[module_name]) {
|
||||
module_link_list_map[module_name] = {
|
||||
links: links_str.split(","),
|
||||
app: this.template_modules.filter(
|
||||
m => m.module_name === module_name
|
||||
)[0].app
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
update_settings(values) {
|
||||
// Figure out the diff from the default settings known from modules
|
||||
let new_settings = {};
|
||||
const checkbox_fields = Object.keys(values).filter(f => !f.includes('links'));
|
||||
|
||||
frappe.db
|
||||
.set_value("User", user, "home_settings", this.all_settings)
|
||||
.then(resp => {
|
||||
this.update_modules(module_link_list_map);
|
||||
this.dialog.hide();
|
||||
})
|
||||
.fail(err => {
|
||||
frappe.msgprint(err);
|
||||
});
|
||||
}
|
||||
});
|
||||
checkbox_fields.forEach(module_name => {
|
||||
const default_module = this.default_modules.filter(f => f.module_name === module_name)[0];
|
||||
|
||||
this.dialog.modal_body.find(".clearfix").css({ display: "none" });
|
||||
this.dialog.modal_body
|
||||
.find('.frappe-control*[data-fieldtype="MultiSelect"]')
|
||||
.css({ "margin-bottom": "30px" });
|
||||
// Check if hidden changed
|
||||
const default_hidden = default_module.hidden ? 1 : 0;
|
||||
const new_hidden = !values[module_name] ? 1 : 0;
|
||||
const hidden_changed = new_hidden != default_hidden;
|
||||
|
||||
this.dialog.show();
|
||||
},
|
||||
// Check if links changed
|
||||
let links_changed = 0;
|
||||
let new_links = [];
|
||||
|
||||
update_modules(module_link_list_map) {
|
||||
frappe.call({
|
||||
type: "GET",
|
||||
method: "frappe.desk.moduleview.get_module_link_items_from_dict",
|
||||
freeze: true,
|
||||
args: {
|
||||
module_link_list_map: module_link_list_map
|
||||
},
|
||||
callback: r => {
|
||||
const module_links_dict = r.message;
|
||||
this.template_modules.map((m, i) => {
|
||||
let raw_links = module_links_dict[m.module_name];
|
||||
raw_links.forEach(link => {
|
||||
link.route = generate_route(link);
|
||||
});
|
||||
if (Object.keys(module_link_list_map).includes(m.module_name)) {
|
||||
m.hidden = 0;
|
||||
m.links = raw_links;
|
||||
} else {
|
||||
m.hidden = 1;
|
||||
}
|
||||
});
|
||||
if(!new_hidden) {
|
||||
const default_links = default_module.links.map(l => (l.name || l.label));
|
||||
const new_links_str = values[module_name + '_links'] || '';
|
||||
new_links = new_links_str ? new_links_str.split(",") : [];
|
||||
links_changed = !this.are_arrays_equal(new_links, default_links);
|
||||
}
|
||||
|
||||
this.modules = this.template_modules.filter(m => !m.hidden);
|
||||
}
|
||||
});
|
||||
},
|
||||
// Make new settings
|
||||
let new_module_settings;
|
||||
|
||||
get_module_select_field(module, selected_modules) {
|
||||
return {
|
||||
label: __(module.module_name),
|
||||
fieldname: module.module_name,
|
||||
fieldtype: "Check",
|
||||
default: selected_modules.includes(module.module_name) ? 1 : 0
|
||||
};
|
||||
},
|
||||
if(hidden_changed || links_changed) {
|
||||
new_module_settings = {};
|
||||
if(hidden_changed) {
|
||||
new_module_settings.hidden = new_hidden;
|
||||
}
|
||||
if(links_changed) {
|
||||
new_module_settings.links = new_links;
|
||||
}
|
||||
}
|
||||
|
||||
get_links_multiselect_field(module) {
|
||||
return {
|
||||
label: __(""),
|
||||
fieldname: module.module_name + "_links",
|
||||
fieldtype: "MultiSelect",
|
||||
get_data: function() {
|
||||
let data = [];
|
||||
if(new_module_settings) {
|
||||
new_module_settings.app = this.default_modules.filter(m => m.module_name === module_name)[0].app;
|
||||
new_settings[module_name] = new_module_settings;
|
||||
}
|
||||
});
|
||||
|
||||
frappe.call({
|
||||
type: "GET",
|
||||
method: "frappe.desk.moduleview.get_links",
|
||||
async: false,
|
||||
no_spinner: true,
|
||||
args: {
|
||||
app: module.app,
|
||||
module: module.module_name
|
||||
},
|
||||
callback: function(r) {
|
||||
data = r.message;
|
||||
}
|
||||
});
|
||||
return data;
|
||||
},
|
||||
default: module.links.map(m => m.name || m.label),
|
||||
depends_on: module.module_name
|
||||
};
|
||||
},
|
||||
if(Object.keys(new_settings)) {
|
||||
frappe.call({
|
||||
type: "GET",
|
||||
method:'frappe.desk.moduleview.update_desk_section_settings',
|
||||
freeze: true,
|
||||
args: {
|
||||
desk_section: this.category,
|
||||
new_settings: new_settings
|
||||
},
|
||||
callback: (r) => {
|
||||
let new_settings_with_link_objects = r.message;
|
||||
let home_settings = JSON.parse(frappe.boot.home_settings);
|
||||
home_settings[this.category] = new_settings_with_link_objects;
|
||||
frappe.boot.home_settings = JSON.stringify(home_settings);
|
||||
|
||||
box_dragstart(index) {
|
||||
this.dragged_index = index;
|
||||
},
|
||||
this.modules = this.get_customized_modules(this.default_modules, new_settings_with_link_objects);
|
||||
this.dialog.hide();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.dialog.hide();
|
||||
};
|
||||
},
|
||||
|
||||
box_dragend(index) {
|
||||
this.dragged_index = -1;
|
||||
this.hovered_index = -1;
|
||||
},
|
||||
get_customized_modules(default_modules, customization_settings={}) {
|
||||
return default_modules.map(module => {
|
||||
let customized_module = JSON.parse(JSON.stringify(module));
|
||||
|
||||
box_enter(index) {
|
||||
this.hovered_index = index;
|
||||
},
|
||||
const module_settings = customization_settings[module.module_name];
|
||||
if(module_settings) {
|
||||
if(module_settings.links) {
|
||||
customized_module.links = module_settings.links;
|
||||
}
|
||||
customized_module.hidden = module_settings ? module_settings.hidden : 0;
|
||||
}
|
||||
|
||||
box_drop(index) {
|
||||
let d = this.dragged_index;
|
||||
let h = this.hovered_index;
|
||||
if (d < h) {
|
||||
this.modules.splice(h, 0, this.modules[d]);
|
||||
this.modules.splice(d, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
if(customized_module.links) {
|
||||
customized_module.links.forEach(link => {
|
||||
link.route = generate_route(link);
|
||||
});
|
||||
}
|
||||
|
||||
return customized_module;
|
||||
});
|
||||
},
|
||||
|
||||
get_module_select_field(module) {
|
||||
return {
|
||||
label: __(module.module_name),
|
||||
fieldname: module.module_name,
|
||||
fieldtype: "Check",
|
||||
default: module.hidden ? 0 : 1
|
||||
}
|
||||
},
|
||||
|
||||
get_links_multiselect_field(module) {
|
||||
return {
|
||||
label: __(""),
|
||||
fieldname: module.module_name + "_links",
|
||||
fieldtype: "MultiSelect",
|
||||
get_data: function() {
|
||||
let data = [];
|
||||
|
||||
frappe.call({
|
||||
type: "GET",
|
||||
method:'frappe.desk.moduleview.get_links',
|
||||
async: false,
|
||||
no_spinner: true,
|
||||
args: {
|
||||
app: module.app,
|
||||
module: module.module_name,
|
||||
},
|
||||
callback: function(r) {
|
||||
data = r.message;
|
||||
}
|
||||
});
|
||||
return data;
|
||||
},
|
||||
default: module.links.map(l => (l.name || l.label)),
|
||||
depends_on: module.module_name
|
||||
};
|
||||
},
|
||||
|
||||
are_arrays_equal(arr1, arr2) {
|
||||
if(arr1.length !== arr2.length) return false;
|
||||
let areEqual = true;
|
||||
arr1.map((d, i) => {
|
||||
if(arr2[i] !== d) areEqual = false;
|
||||
});
|
||||
return areEqual;
|
||||
},
|
||||
|
||||
box_dragstart(index) {
|
||||
this.dragged_index = index;
|
||||
},
|
||||
|
||||
box_dragend(index) {
|
||||
this.dragged_index = -1;
|
||||
this.hovered_index = -1;
|
||||
},
|
||||
|
||||
box_enter(index) {
|
||||
this.hovered_index = index;
|
||||
},
|
||||
|
||||
box_drop(index) {
|
||||
let d = this.dragged_index;
|
||||
let h = this.hovered_index;
|
||||
if (d < h) {
|
||||
this.modules.splice(h, 0, this.modules[d]);
|
||||
this.modules.splice(d, 1);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
v-if="modules.filter(m => m.category === category).length"
|
||||
:category="category"
|
||||
:all_modules="modules.filter(m => m.category === category)"
|
||||
:customization_settings="home_settings ? home_settings[category] : {}"
|
||||
>
|
||||
</desk-section>
|
||||
|
||||
|
|
@ -31,14 +32,38 @@ export default {
|
|||
module.count = this.get_module_count(module.module_name);
|
||||
});
|
||||
|
||||
const home_settings = frappe.boot.home_settings || '{}';
|
||||
|
||||
return {
|
||||
route_str: frappe.get_route()[1],
|
||||
module_label: '',
|
||||
module_categories: ["Modules", "Domains", "Places", "Administration"],
|
||||
modules: modules_list
|
||||
modules: modules_list,
|
||||
|
||||
// // Desk customizations. Format of user settings:
|
||||
|
||||
// home_settings = { // <--- Settings
|
||||
// "Domains": { // <--- Category (Desk Section)
|
||||
// "Manufacturing": { // <--- Module
|
||||
// "index": 3,
|
||||
// "links": [],
|
||||
// "hidden": 1,
|
||||
// },
|
||||
|
||||
// },
|
||||
// }
|
||||
|
||||
home_settings: JSON.parse(home_settings)
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
get_settings() {
|
||||
return frappe.db.get_value('User', user, 'home_settings')
|
||||
.then(resp => {
|
||||
this.all_settings = JSON.parse(resp.message['home_settings']);
|
||||
this.settings = this.all_settings[this.category];
|
||||
});
|
||||
},
|
||||
get_module_count(module_name) {
|
||||
var module_doctypes = frappe.boot.notification_info.module_doctypes[module_name];
|
||||
var sum = 0;
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ function generate_route(item) {
|
|||
}
|
||||
|
||||
route = '#' + route;
|
||||
} else {
|
||||
route = item.route;
|
||||
}
|
||||
|
||||
if(item.route_options) {
|
||||
|
|
|
|||
|
|
@ -11,8 +11,7 @@ frappe.modules.Home = class {
|
|||
}
|
||||
make_body() {
|
||||
this.$modules_container = this.$parent.find('.layout-main');
|
||||
Vue.prototype.__ = window.__;
|
||||
Vue.prototype.frappe = window.frappe;
|
||||
|
||||
new Vue({
|
||||
el: this.$modules_container[0],
|
||||
render: h => h(Modules)
|
||||
|
|
|
|||
|
|
@ -48,8 +48,6 @@ frappe.views.pageview = {
|
|||
let container = $('<div class="container"></div>').appendTo(page);
|
||||
container = $('<div></div>').appendTo(container);
|
||||
|
||||
Vue.prototype.__ = window.__;
|
||||
Vue.prototype.frappe = window.frappe;
|
||||
new Vue({
|
||||
el: container[0],
|
||||
render: h => h(Desktop)
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
import Vue from 'vue/dist/vue.js';
|
||||
|
||||
if (!window.Vue) {
|
||||
Vue.prototype.__ = window.__;
|
||||
Vue.prototype.frappe = window.frappe;
|
||||
window.Vue = Vue;
|
||||
}
|
||||
|
|
@ -457,13 +457,23 @@ li.user-progress {
|
|||
|
||||
// like pop-over
|
||||
.liked-by-popover {
|
||||
min-width: 100px;
|
||||
margin-top: -10px;
|
||||
margin-bottom: -10px;
|
||||
|
||||
li {
|
||||
margin: 15px 0px;
|
||||
.popover-content {
|
||||
padding: 0px;
|
||||
overflow: scroll;
|
||||
max-height: 150px;
|
||||
}
|
||||
min-width: 100px;
|
||||
ul {
|
||||
margin: 0px;
|
||||
li {
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: @btn-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.screenshot {
|
||||
|
|
|
|||
|
|
@ -11,9 +11,6 @@ body[data-route*="social"] {
|
|||
}
|
||||
.liked-by-popover {
|
||||
font-size: @text-small;
|
||||
li {
|
||||
margin: 10px 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.wall-container {
|
||||
|
|
@ -56,6 +53,7 @@ body[data-route*="social"] {
|
|||
min-height: 70px;
|
||||
border: 1px solid @border-color;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
.content {
|
||||
font-size: 14px;
|
||||
img, iframe {
|
||||
|
|
|
|||
|
|
@ -1,338 +1,343 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2018-09-25 11:39:04.533626",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2018-09-25 11:39:04.533626",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "content",
|
||||
"fieldtype": "Text Editor",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Content",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "content",
|
||||
"fieldtype": "Text Editor",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Content",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "comments",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "comments",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Post Comment",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "comments",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "comments",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Post Comment",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "liked_by",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Liked By",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "liked_by",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Liked By",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "is_pinned",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Is Pinned",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 2,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "is_pinned",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Is Pinned",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 2,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "is_globally_pinned",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Is Globally Pinned",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 1,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "is_globally_pinned",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Is Globally Pinned",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 1,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-11-01 10:08:33.902009",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Social",
|
||||
"name": "Post",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-03-11 16:32:20.638805",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Social",
|
||||
"name": "Post",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "All",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "All",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "All",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "All",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 1,
|
||||
"import": 0,
|
||||
"permlevel": 2,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 1,
|
||||
"import": 0,
|
||||
"permlevel": 2,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 2,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "All",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 2,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "All",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 1,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "All",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 1,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "All",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 1
|
||||
}
|
||||
|
|
@ -65,4 +65,41 @@ def get_link_info(url):
|
|||
def delete_post(post_name):
|
||||
post = frappe.get_doc('Post', post_name)
|
||||
post.delete()
|
||||
frappe.publish_realtime('delete_post' + post_name, after_commit=True)
|
||||
frappe.publish_realtime('delete_post' + post_name, after_commit=True)
|
||||
|
||||
def get_unseen_post_count():
|
||||
post_count = frappe.db.count('Post')
|
||||
view_post_count = get_viewed_posts(True)
|
||||
|
||||
return post_count - view_post_count
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_posts(filters=None, limit_start=0):
|
||||
filters = frappe.utils.get_safe_filters(filters)
|
||||
posts = frappe.get_list('Post',
|
||||
fields= ['name', 'content', 'owner', 'creation', 'liked_by', 'is_pinned', 'is_globally_pinned'],
|
||||
filters=filters,
|
||||
limit_start=limit_start,
|
||||
limit=20,
|
||||
order_by= 'is_globally_pinned desc, creation desc')
|
||||
viewed_posts = get_viewed_posts()
|
||||
for post in posts:
|
||||
post['seen'] = post.name in viewed_posts
|
||||
return posts
|
||||
|
||||
def get_viewed_posts(only_count=False):
|
||||
view_logs = frappe.db.get_all('View Log', filters={
|
||||
'reference_doctype': 'Post',
|
||||
'viewed_by': frappe.session.user
|
||||
}, fields=['reference_name'])
|
||||
|
||||
return len(view_logs) if only_count else [log.reference_name for log in view_logs]
|
||||
|
||||
@frappe.whitelist()
|
||||
def set_seen(post_name):
|
||||
frappe.get_doc({
|
||||
'doctype': 'View Log',
|
||||
'reference_doctype': 'Post',
|
||||
'reference_name': post_name,
|
||||
'viewed_by': frappe.session.user
|
||||
}).insert(ignore_permissions=True)
|
||||
|
|
@ -5,7 +5,16 @@
|
|||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe.core.doctype.user.user import extract_mentions
|
||||
|
||||
class PostComment(Document):
|
||||
def after_insert(self):
|
||||
mentions = extract_mentions(self.content)
|
||||
for mention in mentions:
|
||||
if mention == self.owner: continue
|
||||
frappe.publish_realtime('mention', """{} mentioned you!
|
||||
<br><a class="text-muted text-small" href="desk#social/home">Check Social<a>"""
|
||||
.format(frappe.utils.get_fullname(self.owner)),
|
||||
user=mention,
|
||||
after_commit=True)
|
||||
frappe.publish_realtime('new_post_comment' + self.parent, self, after_commit=True)
|
||||
|
|
|
|||
|
|
@ -642,3 +642,16 @@ def gzip_decompress(data):
|
|||
"""
|
||||
with GzipFile(fileobj=io.BytesIO(data)) as f:
|
||||
return f.read()
|
||||
|
||||
def get_safe_filters(filters):
|
||||
try:
|
||||
filters = json.loads(filters)
|
||||
|
||||
if isinstance(filters, (integer_types, float)):
|
||||
filters = frappe.as_unicode(filters)
|
||||
|
||||
except (TypeError, ValueError):
|
||||
# filters are not passed, not json
|
||||
pass
|
||||
|
||||
return filters
|
||||
|
|
@ -191,7 +191,13 @@ def send_private_file(path):
|
|||
response = Response(wrap_file(frappe.local.request.environ, f), direct_passthrough=True)
|
||||
|
||||
# no need for content disposition and force download. let browser handle its opening.
|
||||
# response.headers.add(b'Content-Disposition', b'attachment', filename=filename.encode("utf-8"))
|
||||
# Except for those that can be injected with scripts.
|
||||
|
||||
extension = os.path.splitext(path)[1]
|
||||
blacklist = ['.svg', '.html', '.htm', '.xml']
|
||||
|
||||
if extension.lower() in blacklist:
|
||||
response.headers.add(b'Content-Disposition', b'attachment', filename=filename.encode("utf-8"))
|
||||
|
||||
response.mimetype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue