[feature] Share with Everyone

This commit is contained in:
Anand Doshi 2015-07-17 19:41:32 +05:30
parent d69af7bcfe
commit d70968dbc5
13 changed files with 150 additions and 48 deletions

View file

@ -0,0 +1 @@
- Ability to **Share with Everyone** (except Guest) using **Share With**

View file

@ -26,7 +26,7 @@
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"reqd": 0,
"search_index": 1,
"set_only_once": 0
},
@ -129,6 +129,13 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0
},
{
"fieldname": "everyone",
"fieldtype": "Check",
"label": "Everyone",
"permlevel": 0,
"precision": ""
}
],
"hide_heading": 0,
@ -138,7 +145,7 @@
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"modified": "2015-02-12 11:30:52.968078",
"modified": "2015-07-17 07:02:10.632582",
"modified_by": "Administrator",
"module": "Core",
"name": "DocShare",

View file

@ -11,6 +11,7 @@ class DocShare(Document):
no_feed_on_delete = True
def validate(self):
self.validate_user()
self.check_share_permission()
self.cascade_permissions_downwards()
self.get_doc().run_method("validate_share", self)
@ -26,6 +27,12 @@ class DocShare(Document):
self._doc = frappe.get_doc(self.share_doctype, self.share_name)
return self._doc
def validate_user(self):
if self.everyone:
self.user = None
elif not self.user:
frappe.throw(_("User is mandatory for Share"), frappe.MandatoryError)
def check_share_permission(self):
if (not self.flags.ignore_share_permission and
not frappe.has_permission(self.share_doctype, "share", self.get_doc())):

View file

@ -78,3 +78,15 @@ class TestDocShare(unittest.TestCase):
frappe.set_user(self.user)
self.assertFalse(self.event.has_permission("share"))
def test_share_with_everyone(self):
self.assertTrue(self.event.name not in frappe.share.get_shared("Event", self.user))
frappe.share.set_permission("Event", self.event.name, None, "read", everyone=1)
self.assertTrue(self.event.name in frappe.share.get_shared("Event", self.user))
self.assertTrue(self.event.name in frappe.share.get_shared("Event", "test1@example.com"))
self.assertTrue(self.event.name not in frappe.share.get_shared("Event", "Guest"))
frappe.share.set_permission("Event", self.event.name, None, "read", value=0, everyone=1)
self.assertTrue(self.event.name not in frappe.share.get_shared("Event", self.user))
self.assertTrue(self.event.name not in frappe.share.get_shared("Event", "test1@example.com"))
self.assertTrue(self.event.name not in frappe.share.get_shared("Event", "Guest"))

View file

@ -89,7 +89,7 @@ def get_docinfo(doc=None, doctype=None, name=None):
"assignments": get_assignments(doc.doctype, doc.name),
"permissions": get_doc_permissions(doc),
"shared": frappe.share.get_users(doc.doctype, doc.name,
fields=["user", "read", "write", "share"])
fields=["user", "read", "write", "share", "everyone"])
}
def get_user_permissions(meta):

View file

@ -40,15 +40,16 @@ def has_permission(doctype, ptype="read", doc=None, verbose=False, user=None):
def false_if_not_shared():
if ptype in ("read", "write", "share", "email", "print"):
shared = frappe.share.get_shared(doctype, user,
["read" if ptype in ("email", "print") else ptype])
if doc:
doc_name = doc if isinstance(doc, basestring) else doc.name
shared = frappe.share.get_shared(doctype, user,
["read" if ptype in ("email", "print") else ptype])
if doc_name in shared:
if verbose: print "Shared"
if ptype in ("read", "write", "share") or meta.permissions[0].get(ptype):
return True
else:
if verbose: print "Has a shared document"
return True

View file

@ -12,6 +12,7 @@
}
.avatar-empty {
border: 1px dashed #d1d8dd;
border-radius: 4px;
}
.avatar-small {
margin-right: 5px;

View file

@ -140,7 +140,7 @@ body[data-route^="Module"] .main-menu .form-sidebar {
.form-sidebar .form-shared .share-doc-btn {
cursor: pointer;
}
.form-sidebar .form-shared .octicon-plus {
.form-sidebar .form-shared .octicon {
position: relative;
top: 2px;
left: 7px;
@ -148,6 +148,14 @@ body[data-route^="Module"] .main-menu .form-sidebar {
.form-sidebar .form-shared .avatar {
margin-top: 5px;
}
.form-sidebar .form-shared .shared-with-everyone {
border-style: solid;
border-color: #f0f4f7;
background-color: #f0f4f7;
}
.form-sidebar .form-shared .shared-with-everyone .octicon {
color: #36414c !important;
}
.form-sidebar .form-shared .share-doc-btn:hover,
.form-sidebar .form-shared .share-doc-btn:focus,
.form-sidebar .form-shared .share-doc-btn:active {

View file

@ -1,29 +1,35 @@
<div class="padding">
{% if(!shared.length) { %}
<p class="text-muted">{%= __("Not shared with anyone yet.") %}</p>
{% } else { %}
<div class="row">
<div class="col-xs-6"><h6>{%= __("User") %}</h6></div>
<div class="col-xs-2"><h6>{%= __("Can Read") %}</h6></div>
<div class="col-xs-2"><h6>{%= __("Can Write") %}</h6></div>
<div class="col-xs-2"><h6>{%= __("Can Share") %}</h6></div>
</div>
<div class="row">
<div class="col-xs-6"><h6>{%= __("User") %}</h6></div>
<div class="col-xs-2"><h6>{%= __("Can Read") %}</h6></div>
<div class="col-xs-2"><h6>{%= __("Can Write") %}</h6></div>
<div class="col-xs-2"><h6>{%= __("Can Share") %}</h6></div>
</div>
{% for (var i=0, l=shared.length; i < l; i++) {
var s = shared[i]; %}
{% if(s) { %}
<div class="row shared-user" data-user="{%= s.user %}" data-name="{%= s.name %}">
<div class="col-xs-6">{%= s.user %}</div>
<div class="col-xs-2"><input type="checkbox" name="read"
{% if(cint(s.read)) { %}checked{% } %} class="edit-share"></div>
<div class="col-xs-2"><input type="checkbox" name="write"
{% if(cint(s.write)) { %}checked{% } %} class="edit-share"></div>
<div class="col-xs-2"><input type="checkbox" name="share"
{% if(cint(s.share)) { %}checked{% } %} class="edit-share"></div>
</div>
{% } %}
{% } %}
<div class="row shared-user" data-everyone=1>
<div class="col-xs-6 share-all" style="height: 30px;"><b>{{ __("Everyone") }}</b></div>
<div class="col-xs-2"><input type="checkbox" name="read"
{% if(cint(everyone.read)) { %}checked{% } %} class="edit-share"></div>
<div class="col-xs-2"><input type="checkbox" name="write"
{% if(cint(everyone.write)) { %}checked{% } %} class="edit-share"></div>
<div class="col-xs-2"><input type="checkbox" name="share"
{% if(cint(everyone.share)) { %}checked{% } %} class="edit-share"></div>
</div>
{% for (var i=0, l=shared.length; i < l; i++) {
var s = shared[i]; %}
{% if(s && !s.everyone) { %}
<div class="row shared-user" data-user="{%= s.user %}" data-name="{%= s.name %}">
<div class="col-xs-6">{%= s.user %}</div>
<div class="col-xs-2"><input type="checkbox" name="read"
{% if(cint(s.read)) { %}checked{% } %} class="edit-share"></div>
<div class="col-xs-2"><input type="checkbox" name="write"
{% if(cint(s.write)) { %}checked{% } %} class="edit-share"></div>
<div class="col-xs-2"><input type="checkbox" name="share"
{% if(cint(s.share)) { %}checked{% } %} class="edit-share"></div>
</div>
{% } %}
{% } %}
{% if(frappe.model.can_share(null, frm)) { %}

View file

@ -10,26 +10,42 @@ frappe.ui.form.Share = Class.extend({
refresh: function() {
var me = this;
this.parent.empty();
var everyone = null;
var shared = $.map(this.shared || this.frm.get_docinfo().shared, function(s) {
if (s.everyone) {
everyone = s;
}
return s ? s.user : null;
});
if (everyone) {
$(repl('<span><a class="avatar avatar-small avatar-empty share-doc-btn shared-with-everyone" title="%(title)s">\
<i class="octicon octicon-megaphone text-muted"></i></a></span>', {title: __("Shared with everyone")}))
.appendTo(this.parent)
.on("click", function() { me.frm.share_doc(); });
}
for(var i=0; i<shared.length; i++) {
var user_info = frappe.user_info(shared[i])
var user_info = frappe.user_info(shared[i]);
$(repl('<span class="avatar avatar-small" title="'
+__("Shared with {0}", [user_info.fullname])+'">\
<img class="media-object" src="%(image)s" alt="%(fullname)s"></span>',
{image: user_info.image, fullname: user_info.fullname}))
.appendTo(this.parent)
.on("click", function() { me.frm.share_doc(); });;
.on("click", function() { me.frm.share_doc(); });
}
// share
if(!me.frm.doc.__islocal) {
$(repl('<span><a class="avatar avatar-small avatar-empty share-doc-btn" title="%(title)s">\
<i class="octicon octicon-plus text-muted"></i></a></span>', {title: __("Share")}))
.appendTo(this.parent)
.on("click", function() { me.frm.share_doc(); });
}
},
show: function() {
var me = this;
@ -65,7 +81,16 @@ frappe.ui.form.Share = Class.extend({
this.shared = shared;
var d = this.dialog;
$(d.body).empty();
$(frappe.render_template("set_sharing", {frm: this.frm, shared: this.shared}))
var everyone = {};
$.each(this.shared, function(i, s) {
// pullout everyone record from shared list
if (s && s.everyone) {
everyone = s;
}
});
$(frappe.render_template("set_sharing", {frm: this.frm, shared: this.shared, everyone: everyone}))
.appendTo(d.body);
if(frappe.model.can_share(null, this.frm)) {
@ -113,6 +138,7 @@ frappe.ui.form.Share = Class.extend({
write: $(d.body).find(".add-share-write").prop("checked") ? 1 : 0,
share: $(d.body).find(".add-share-share").prop("checked") ? 1 : 0
},
btn: this,
callback: function(r) {
$.each(me.shared, function(i, s) {
if(s && s.user===r.message.user) {
@ -131,9 +157,10 @@ frappe.ui.form.Share = Class.extend({
set_edit_share_events: function() {
var me = this, d = this.dialog;
$(d.body).find(".edit-share").on("click", function() {
var user = $(this).parents(".shared-user:first").attr("data-user"),
var user = $(this).parents(".shared-user:first").attr("data-user") || "",
value = $(this).prop("checked") ? 1 : 0,
property = $(this).attr("name");
property = $(this).attr("name")
everyone = cint($(this).parents(".shared-user:first").attr("data-everyone"));
frappe.call({
method: "frappe.share.set_permission",
@ -142,20 +169,28 @@ frappe.ui.form.Share = Class.extend({
name: me.frm.doc.name,
user: user,
permission_to: property,
value: value
value: value,
everyone: everyone
},
callback: function(r) {
var found = null;
$.each(me.shared, function(i, s) {
// update shared object
if(s && s.user===user) {
if(s && (s.user===user || (everyone && s.everyone===1))) {
if(!r.message) {
delete me.shared[i];
} else {
me.shared[i] = $.extend(s, r.message);
}
found = true;
return false;
}
});
if (!found) {
me.shared.push(r.message);
}
me.dirty = true;
me.render_shared();
me.frm.shared.refresh();

View file

@ -14,6 +14,7 @@
.avatar-empty {
border: 1px dashed #d1d8dd;
border-radius: 4px;
}
.avatar-small {

View file

@ -120,7 +120,7 @@ body[data-route^="Module"] .main-menu {
cursor: pointer;
}
.octicon-plus {
.octicon {
position: relative;
top: 2px;
left: 7px;
@ -129,6 +129,16 @@ body[data-route^="Module"] .main-menu {
.avatar {
margin-top: 5px;
}
.shared-with-everyone {
border-style: solid;
border-color: @btn-bg;
background-color: @btn-bg;
.octicon {
color: @text-color !important;
}
}
}
}

View file

@ -6,13 +6,12 @@ import frappe
from frappe.utils import cint
@frappe.whitelist()
def add(doctype, name, user=None, read=1, write=0, share=0, flags=None):
def add(doctype, name, user=None, read=1, write=0, share=0, everyone=0, flags=None):
"""Share the given document with a user."""
if not user:
user = frappe.session.user
share_name = frappe.db.get_value("DocShare", {"user": user, "share_name": name,
"share_doctype": doctype})
share_name = get_share_name(doctype, name, user, everyone)
if share_name:
doc = frappe.get_doc("DocShare", share_name)
@ -21,7 +20,8 @@ def add(doctype, name, user=None, read=1, write=0, share=0, flags=None):
doc.update({
"user": user,
"share_doctype": doctype,
"share_name": name
"share_name": name,
"everyone": cint(everyone)
})
if flags:
@ -46,14 +46,14 @@ def remove(doctype, name, user, flags=None):
frappe.delete_doc("DocShare", share_name)
@frappe.whitelist()
def set_permission(doctype, name, user, permission_to, value=1):
def set_permission(doctype, name, user, permission_to, value=1, everyone=0):
"""Set share permission."""
share_name = frappe.db.get_value("DocShare", {"user": user, "share_name": name,
"share_doctype": doctype})
share_name = get_share_name(doctype, name, user, everyone)
value = int(value)
if not share_name:
if value:
share = add(doctype, name, user, **{permission_to: 1})
share = add(doctype, name, user, everyone=everyone, **{permission_to: 1})
else:
# no share found, nothing to remove
share = {}
@ -102,7 +102,9 @@ def get_shared(doctype, user=None, rights=None):
condition = " and ".join(["`{0}`=1".format(right) for right in rights])
return frappe.db.sql_list("select share_name from tabDocShare where user=%s and share_doctype=%s and {0}".format(condition),
return frappe.db.sql_list("""select share_name from tabDocShare
where (user=%s {everyone}) and share_doctype=%s and {condition}""".format(
condition=condition, everyone="or everyone=1" if user!="Guest" else ""),
(user, doctype))
def get_shared_doctypes(user=None):
@ -110,4 +112,15 @@ def get_shared_doctypes(user=None):
if not user:
user = frappe.session.user
return frappe.db.sql_list("select distinct share_doctype from tabDocShare where user=%s", user)
return frappe.db.sql_list("select distinct share_doctype from tabDocShare where (user=%s or everyone=1)", user)
def get_share_name(doctype, name, user, everyone):
if cint(everyone):
share_name = frappe.db.get_value("DocShare", {"everyone": 1, "share_name": name,
"share_doctype": doctype})
else:
share_name = frappe.db.get_value("DocShare", {"user": user, "share_name": name,
"share_doctype": doctype})
return share_name