plugin account, groupblog: user can erase all their microblogs at once
This commit is contained in:
parent
13586ac57b
commit
f6673e0725
3 changed files with 216 additions and 17 deletions
|
|
@ -154,9 +154,12 @@ class Params(object):
|
|||
if not self.storage.hasProfile(profile):
|
||||
info(_('Trying to delete an unknown profile'))
|
||||
return defer.fail(Failure(exceptions.ProfileUnknownError))
|
||||
if not force and self.host.isConnected(profile):
|
||||
info(_("Trying to delete a connected profile"))
|
||||
return defer.fail(Failure(exceptions.ConnectedProfileError))
|
||||
if self.host.isConnected(profile):
|
||||
if force:
|
||||
self.host.disconnect(profile)
|
||||
else:
|
||||
info(_("Trying to delete a connected profile"))
|
||||
return defer.fail(Failure(exceptions.ConnectedProfileError))
|
||||
return self.storage.deleteProfile(profile)
|
||||
|
||||
def getProfileName(self, profile_key, return_profile_keys = False):
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ PLUGIN_INFO = {
|
|||
"type": "MISC",
|
||||
"protocols": [],
|
||||
"dependencies": [],
|
||||
"recommendations": ['GROUPBLOG'],
|
||||
"main": "MiscAccount",
|
||||
"handler": "no",
|
||||
"description": _(u"""SàT account creation""")
|
||||
|
|
@ -133,6 +134,13 @@ class MiscAccount(object):
|
|||
self.__account_cb_id = host.registerCallback(self._accountDialogCb, with_data=True)
|
||||
self.__delete_account_id = host.registerCallback(self.__deleteAccountCb, with_data=True)
|
||||
|
||||
def deleteBlogCallback(posts, comments):
|
||||
return lambda data, profile: self.__deleteBlogPostsCb(posts, comments, data, profile)
|
||||
|
||||
self.__delete_posts_comments_id = host.registerCallback(deleteBlogCallback(True, True), with_data=True)
|
||||
self.__delete_posts_id = host.registerCallback(deleteBlogCallback(True, False), with_data=True)
|
||||
self.__delete_comments_id = host.registerCallback(deleteBlogCallback(False, True), with_data=True)
|
||||
|
||||
def getConfig(self, name):
|
||||
return self.host.memory.getConfig(CONFIG_SECTION, name) or default_conf[name]
|
||||
|
||||
|
|
@ -239,6 +247,7 @@ SàT team""" % {'login': login, 'password': password, 'jid': "%s@%s" % (login, s
|
|||
"""
|
||||
form_ui = xml_tools.XMLUI("form", "tabs", title=D_("Manage your XMPP account"), submit_id=self.__account_cb_id)
|
||||
tab_container = form_ui.current_container
|
||||
|
||||
tab_container.addTab("update", D_("Change your password"), container=xml_tools.PairsContainer)
|
||||
form_ui.addLabel(D_("Current password"))
|
||||
form_ui.addPassword("current_passwd", value="")
|
||||
|
|
@ -246,6 +255,16 @@ SàT team""" % {'login': login, 'password': password, 'jid': "%s@%s" % (login, s
|
|||
form_ui.addPassword("new_passwd1", value="")
|
||||
form_ui.addLabel(D_("New password (again)"))
|
||||
form_ui.addPassword("new_passwd2", value="")
|
||||
|
||||
if 'GROUPBLOG' in self.host.plugins:
|
||||
tab_container.addTab("delete_posts", D_("Delete your posts"), container=xml_tools.PairsContainer)
|
||||
form_ui.addLabel(D_("Current password"))
|
||||
form_ui.addPassword("delete_posts_passwd", value="")
|
||||
form_ui.addLabel(D_("Delete all your posts and their comments"))
|
||||
form_ui.addBool("delete_posts_checkbox", "false")
|
||||
form_ui.addLabel(D_("Delete all your comments on other's posts"))
|
||||
form_ui.addBool("delete_comments_checkbox", "false")
|
||||
|
||||
tab_container.addTab("delete", D_("Delete your account"), container=xml_tools.PairsContainer)
|
||||
form_ui.addLabel(D_("Current password"))
|
||||
form_ui.addPassword("delete_passwd", value="")
|
||||
|
|
@ -261,7 +280,7 @@ SàT team""" % {'login': login, 'password': password, 'jid': "%s@%s" % (login, s
|
|||
password = self.host.memory.getParamA("Password", "Connection", profile_key=profile)
|
||||
|
||||
def error_ui():
|
||||
error_ui = xml_tools.XMLUI("popup", title="Error")
|
||||
error_ui = xml_tools.XMLUI("popup", title=D_("Error"))
|
||||
error_ui.addText(D_("Passwords don't match!"))
|
||||
return defer.succeed({'xmlui': error_ui.toXml()})
|
||||
|
||||
|
|
@ -273,6 +292,18 @@ SàT team""" % {'login': login, 'password': password, 'jid': "%s@%s" % (login, s
|
|||
return self.__deleteAccount(profile)
|
||||
return error_ui()
|
||||
|
||||
# check for blog posts deletion
|
||||
if 'GROUPBLOG' in self.host.plugins:
|
||||
delete_posts_passwd = data[xml_tools.SAT_FORM_PREFIX + 'delete_posts_passwd']
|
||||
delete_posts_checkbox = data[xml_tools.SAT_FORM_PREFIX + 'delete_posts_checkbox']
|
||||
delete_comments_checkbox = data[xml_tools.SAT_FORM_PREFIX + 'delete_comments_checkbox']
|
||||
posts = delete_posts_checkbox == 'true'
|
||||
comments = delete_comments_checkbox == 'true'
|
||||
if posts or comments:
|
||||
if password == delete_posts_passwd:
|
||||
return self.__deleteBlogPosts(posts, comments, profile)
|
||||
return error_ui()
|
||||
|
||||
# check for password modification
|
||||
current_passwd = data[xml_tools.SAT_FORM_PREFIX + 'current_passwd']
|
||||
new_passwd1 = data[xml_tools.SAT_FORM_PREFIX + 'new_passwd1']
|
||||
|
|
@ -291,12 +322,12 @@ SàT team""" % {'login': login, 'password': password, 'jid': "%s@%s" % (login, s
|
|||
"""
|
||||
def passwordChanged(result):
|
||||
self.host.memory.setParam("Password", password, "Connection", profile_key=profile)
|
||||
confirm_ui = xml_tools.XMLUI("popup", title="Confirmation")
|
||||
confirm_ui = xml_tools.XMLUI("popup", title=D_("Confirmation"))
|
||||
confirm_ui.addText(D_("Your password has been changed."))
|
||||
return defer.succeed({'xmlui': confirm_ui.toXml()})
|
||||
|
||||
def errback(failure):
|
||||
error_ui = xml_tools.XMLUI("popup", title="Error")
|
||||
error_ui = xml_tools.XMLUI("popup", title=D_("Error"))
|
||||
error_ui.addText(D_("Your password could not be changed: %s") % failure.getErrorMessage())
|
||||
return defer.succeed({'xmlui': error_ui.toXml()})
|
||||
|
||||
|
|
@ -308,8 +339,10 @@ SàT team""" % {'login': login, 'password': password, 'jid': "%s@%s" % (login, s
|
|||
"""Ask for a confirmation before deleting the XMPP account and SàT profile
|
||||
@param profile
|
||||
"""
|
||||
form_ui = xml_tools.XMLUI("form", title=D_("Delete your account ?"), submit_id=self.__delete_account_id)
|
||||
form_ui = xml_tools.XMLUI("form", title=D_("Delete your account?"), submit_id=self.__delete_account_id)
|
||||
form_ui.addText(D_("If you confirm this dialog, you will be disconnected and then your XMPP account AND your SàT profile will both be DELETED."))
|
||||
target = D_('contact list, messages history, blog posts and comments' if 'GROUPBLOG' in self.host.plugins else D_('contact list and messages history'))
|
||||
form_ui.addText(D_("All your data stored on %(server)s, including your %(target)s will be erased.") % {'server': self._getNewAccountDomain(), 'target': target})
|
||||
form_ui.addText(D_("There is no other confirmation dialog, this is the very last one! Are you sure?"))
|
||||
return {'xmlui': form_ui.toXml()}
|
||||
|
||||
|
|
@ -327,15 +360,77 @@ SàT team""" % {'login': login, 'password': password, 'jid': "%s@%s" % (login, s
|
|||
for jid_ in self.host.memory.getWaitingSub(profile): # delete waiting subscriptions
|
||||
self.host.memory.delWaitingSub(jid_)
|
||||
|
||||
self.host.disconnect(profile)
|
||||
self.host.memory.asyncDeleteProfile(profile, force=True)
|
||||
delete_profile = lambda: self.host.memory.asyncDeleteProfile(profile, force=True)
|
||||
if 'GROUPBLOG' in self.host.plugins:
|
||||
d = self.host.plugins['GROUPBLOG'].deleteAllGroupBlogsAndComments(profile_key=profile)
|
||||
d.addCallback(lambda dummy: delete_profile())
|
||||
else:
|
||||
delete_profile()
|
||||
|
||||
return defer.succeed({})
|
||||
|
||||
def errback(failure):
|
||||
error_ui = xml_tools.XMLUI("popup", title="Error")
|
||||
error_ui = xml_tools.XMLUI("popup", title=D_("Error"))
|
||||
error_ui.addText(D_("Your XMPP account could not be deleted: %s") % failure.getErrorMessage())
|
||||
return defer.succeed({'xmlui': error_ui.toXml()})
|
||||
|
||||
d = ProsodyRegisterProtocol.prosodyctl(self, 'deluser', profile=profile)
|
||||
d.addCallbacks(userDeleted, errback)
|
||||
return d
|
||||
|
||||
def __deleteBlogPosts(self, posts, comments, profile):
|
||||
"""Ask for a confirmation before deleting the blog posts
|
||||
@param posts: delete all posts of the user (and their comments)
|
||||
@param comments: delete all the comments of the user on other's posts
|
||||
@param data
|
||||
@param profile
|
||||
"""
|
||||
if posts:
|
||||
if comments: # delete everything
|
||||
form_ui = xml_tools.XMLUI("form", title=D_("Delete all your (micro-)blog posts and comments?"), submit_id=self.__delete_posts_comments_id)
|
||||
form_ui.addText(D_("If you confirm this dialog, all the (micro-)blog data you submitted will be erased."))
|
||||
form_ui.addText(D_("These are the public and private posts and comments you sent to any group."))
|
||||
form_ui.addText(D_("There is no other confirmation dialog, this is the very last one! Are you sure?"))
|
||||
else: # delete only the posts
|
||||
form_ui = xml_tools.XMLUI("form", title=D_("Delete all your (micro-)blog posts?"), submit_id=self.__delete_posts_id)
|
||||
form_ui.addText(D_("If you confirm this dialog, all the public and private posts you sent to any group will be erased."))
|
||||
form_ui.addText(D_("There is no other confirmation dialog, this is the very last one! Are you sure?"))
|
||||
elif comments: # delete only the comments
|
||||
form_ui = xml_tools.XMLUI("form", title=D_("Delete all your (micro-)blog comments?"), submit_id=self.__delete_comments_id)
|
||||
form_ui.addText(D_("If you confirm this dialog, all the public and private comments you made on other people's posts will be erased."))
|
||||
form_ui.addText(D_("There is no other confirmation dialog, this is the very last one! Are you sure?"))
|
||||
|
||||
return {'xmlui': form_ui.toXml()}
|
||||
|
||||
def __deleteBlogPostsCb(self, posts, comments, data, profile):
|
||||
"""Actually delete the XMPP account and SàT profile
|
||||
@param posts: delete all posts of the user (and their comments)
|
||||
@param comments: delete all the comments of the user on other's posts
|
||||
@param profile
|
||||
"""
|
||||
if posts:
|
||||
if comments:
|
||||
target = D_('blog posts and comments')
|
||||
d = self.host.plugins['GROUPBLOG'].deleteAllGroupBlogsAndComments(profile_key=profile)
|
||||
else:
|
||||
target = D_('blog posts')
|
||||
d = self.host.plugins['GROUPBLOG'].deleteAllGroupBlogs(profile_key=profile)
|
||||
elif comments:
|
||||
target = D_('comments')
|
||||
d = self.host.plugins['GROUPBLOG'].deleteAllGroupBlogsComments(profile_key=profile)
|
||||
|
||||
def deleted(result):
|
||||
ui = xml_tools.XMLUI("popup", title=D_("Deletion confirmation"))
|
||||
# TODO: change the message when delete/retract notifications are done with XEP-0060
|
||||
ui.addText(D_("Your %(target)s have been deleted.") % {'target': target})
|
||||
ui.addText(D_("Known issue of the demo version: you need to refresh the page to make the deleted posts actually disappear."))
|
||||
return defer.succeed({'xmlui': ui.toXml()})
|
||||
|
||||
def errback(failure):
|
||||
error_ui = xml_tools.XMLUI("popup", title=D_("Error"))
|
||||
error_ui.addText(D_("Your %(target)s could not be deleted: %(message)s") % {'target': target, 'message': failure.getErrorMessage()})
|
||||
return defer.succeed({'xmlui': error_ui.toXml()})
|
||||
|
||||
d.addCallbacks(deleted, errback)
|
||||
return d
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ from sat.core.constants import Const as C
|
|||
from logging import debug, info, warning, error
|
||||
from twisted.internet import defer
|
||||
from twisted.words.protocols.jabber import jid
|
||||
from twisted.words.xish.domish import Element
|
||||
from twisted.words.xish.domish import Element, generateElementsNamed
|
||||
from sat.core import exceptions
|
||||
from wokkel import disco, data_form, iwokkel
|
||||
from zope.interface import implements
|
||||
|
|
@ -53,7 +53,7 @@ ACCESS_TYPE_MAP = { 'PUBLIC': 'open',
|
|||
|
||||
PLUGIN_INFO = {
|
||||
"name": "Group blogging throught collections",
|
||||
"import_name": "groupblog",
|
||||
"import_name": "GROUPBLOG",
|
||||
"type": "MISC",
|
||||
"protocols": [],
|
||||
"dependencies": ["XEP-0277"],
|
||||
|
|
@ -415,8 +415,8 @@ class GroupBlog(object):
|
|||
def notify(d):
|
||||
# TODO: this works only on the same host, and notifications for item deletion should be
|
||||
# implemented according to http://xmpp.org/extensions/xep-0060.html#publisher-delete-success-notify
|
||||
# instead. The notification mechanisms implemented in sat_pubsub and wokkel seem not compatible,
|
||||
# see wokkel.pubsub.PubSubClient._onEvent_items and sat_pubsub.backend._doNotifyRetraction
|
||||
# instead. The notification mechanism implemented in sat_pubsub and wokkel have apriori
|
||||
# a problem with retrieving the subscriptions, or something else.
|
||||
service, node, item_id = pub_data
|
||||
publisher = self.host.getJidNStream(profile_key)[0]
|
||||
profile = self.host.memory.getProfileName(profile_key)
|
||||
|
|
@ -572,7 +572,6 @@ class GroupBlog(object):
|
|||
d.addCallback(get_comments)
|
||||
return d
|
||||
|
||||
|
||||
def getLastGroupBlogs(self, pub_jid_s, max_items=10, profile_key=C.PROF_KEY_NONE):
|
||||
"""Get the last published microblogs
|
||||
@param pub_jid_s: jid of the publisher
|
||||
|
|
@ -665,7 +664,7 @@ class GroupBlog(object):
|
|||
"""
|
||||
|
||||
def sendResult(result):
|
||||
"""send result of DeferredList (list of microblogs to the calling method"""
|
||||
"""send result of DeferredList (dict of jid => microblogs) to the calling method"""
|
||||
|
||||
ret = {}
|
||||
|
||||
|
|
@ -693,7 +692,6 @@ class GroupBlog(object):
|
|||
|
||||
mblogs = []
|
||||
|
||||
|
||||
for jid_ in jids:
|
||||
d = self.host.plugins["XEP-0060"].getItems(client.item_access_pubsub, self.getNodeName(jid_),
|
||||
max_items=max_items, profile_key=profile_key)
|
||||
|
|
@ -773,6 +771,109 @@ class GroupBlog(object):
|
|||
return self._initialise(profile_key).addCallback(initialised)
|
||||
#TODO: we need to use the server corresponding the the host of the jid
|
||||
|
||||
def deleteAllGroupBlogsAndComments(self, profile_key=C.PROF_KEY_NONE):
|
||||
"""Delete absolutely all the microblog data that the user has posted"""
|
||||
calls = [self.deleteAllGroupBlogs(profile_key), self.deleteAllGroupBlogsComments(profile_key)]
|
||||
return defer.DeferredList(calls)
|
||||
|
||||
def deleteAllGroupBlogs(self, profile_key=C.PROF_KEY_NONE):
|
||||
"""Delete all the main items and their comments that the user has posted
|
||||
"""
|
||||
def initialised(result):
|
||||
profile, client = result
|
||||
service = client.item_access_pubsub
|
||||
jid_ = client.jid
|
||||
|
||||
main_node = self.getNodeName(jid_)
|
||||
d = self.host.plugins["XEP-0060"].deleteNode(service, main_node, profile_key=profile)
|
||||
d.addCallback(lambda dummy: info(_("All microblog's main items from %s have been deleted!") % jid_.userhost()))
|
||||
d.addErrback(lambda failure: error(_("Deletion of node %(node)s failed: %(message)s") %
|
||||
{'node': main_node, 'message': failure.getErrorMessage()}))
|
||||
return d
|
||||
|
||||
return self._initialise(profile_key).addCallback(initialised)
|
||||
|
||||
def deleteAllGroupBlogsComments(self, profile_key=C.PROF_KEY_NONE):
|
||||
"""Delete all the comments that the user posted on other's main items.
|
||||
We avoid the conversions from item to microblog data as we only need
|
||||
to retrieve some attributes, no need to convert text syntax...
|
||||
"""
|
||||
def initialised(result):
|
||||
"""Get all the main items from our contact list
|
||||
@return: a DeferredList
|
||||
"""
|
||||
profile, client = result
|
||||
service = client.item_access_pubsub
|
||||
jids = [contact.jid.userhostJID() for contact in client.roster.getItems()]
|
||||
blogs = []
|
||||
for jid_ in jids:
|
||||
main_node = self.getNodeName(jid_)
|
||||
d = self.host.plugins["XEP-0060"].getItems(service, main_node, profile_key=profile)
|
||||
d.addCallback(getComments, client)
|
||||
d.addErrback(lambda failure, main_node: error(_("Retrieval of items for node %(node)s failed: %(message)s") %
|
||||
{'node': main_node, 'message': failure.getErrorMessage()}), main_node)
|
||||
blogs.append(d)
|
||||
|
||||
return defer.DeferredList(blogs)
|
||||
|
||||
def getComments(items, client):
|
||||
"""Get all the comments for a list of items
|
||||
@param items: a list of main items for one user
|
||||
@param client: the client of the user
|
||||
@return: a DeferredList
|
||||
"""
|
||||
comments = []
|
||||
for item in items:
|
||||
try:
|
||||
entry = generateElementsNamed(item.elements(), 'entry').next()
|
||||
link = generateElementsNamed(entry.elements(), 'link').next()
|
||||
except StopIteration:
|
||||
continue
|
||||
href = link.getAttribute('href')
|
||||
service, node = self.host.plugins['XEP-0277'].parseCommentUrl(href)
|
||||
d = self.host.plugins["XEP-0060"].getItems(service, node, profile_key=profile_key)
|
||||
d.addCallback(lambda items, service, node: (service, node, items), service, node)
|
||||
d.addErrback(lambda failure, node: error(_("Retrieval of comments for node %(node)s failed: %(message)s") %
|
||||
{'node': node, 'message': failure.getErrorMessage()}), node)
|
||||
comments.append(d)
|
||||
dlist = defer.DeferredList(comments)
|
||||
dlist.addCallback(deleteComments, client)
|
||||
return dlist
|
||||
|
||||
def deleteComments(result, client):
|
||||
"""Delete all the comments of the user that are found in result
|
||||
@param result: a list of couple (success, value) with success a
|
||||
boolean and value a tuple (service as JID, node_id, comment_items)
|
||||
@param client: the client of the user
|
||||
@return: a DeferredList with the deletions result
|
||||
"""
|
||||
user_jid_s = client.jid.userhost()
|
||||
for (success, value) in result:
|
||||
if not success:
|
||||
continue
|
||||
service, node_id, comment_items = value
|
||||
item_ids = []
|
||||
for comment_item in comment_items: # for all the comments on one post
|
||||
try:
|
||||
entry = generateElementsNamed(comment_item.elements(), 'entry').next()
|
||||
author = generateElementsNamed(entry.elements(), 'author').next()
|
||||
name = generateElementsNamed(author.elements(), 'name').next()
|
||||
except StopIteration:
|
||||
continue
|
||||
if name.children[0] == user_jid_s:
|
||||
item_ids.append(comment_item.getAttribute('id'))
|
||||
deletions = []
|
||||
if item_ids: # remove the comments of the user on the given post
|
||||
d = self.host.plugins['XEP-0060'].retractItems(service, node_id, item_ids, profile_key=profile_key)
|
||||
d.addCallback(lambda dummy, node_id: debug(_('Comments of user %(user)s in node %(node)s have been retracted') %
|
||||
{'user': user_jid_s, 'node': node_id}), node_id)
|
||||
d.addErrback(lambda failure, node_id: error(_("Retraction of comments from %(user)s in node %(node)s failed: %(message)s") %
|
||||
{'user': user_jid_s, 'node': node_id, 'message': failure.getErrorMessage()}), node_id)
|
||||
deletions.append(d)
|
||||
return defer.DeferredList(deletions)
|
||||
|
||||
return self._initialise(profile_key).addCallback(initialised)
|
||||
|
||||
|
||||
class GroupBlog_handler(XMPPHandler):
|
||||
implements(iwokkel.IDisco)
|
||||
|
|
|
|||
Loading…
Reference in a new issue