changeset 403:1dc606612405

implemented experimental "consistent_publisher" option: /!\ pgsql schema needs to be updated /!\ New "consistent_publisher" option has been implemented to allow node owners + admins to modify an item while preserving the original publisher. This way, original publisher can still edit the item. In addition to `consistent_publisher`, `max_items` has been added to PGQSL schema to prepare for future implementation.
author Goffi <goffi@goffi.org>
date Wed, 12 Jun 2019 21:51:50 +0200
parents 724e39d596a9
children 105a0772eedd
files db/pubsub.sql db/sat_pubsub_update_0_1.sql db/sat_pubsub_update_1_2.sql db/sat_pubsub_update_2_3.sql db/sat_pubsub_update_3_4.sql db/sat_pubsub_update_4_5.sql src/backend.py src/const.py src/pgsql_storage.py
diffstat 9 files changed, 72 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/db/pubsub.sql	Thu May 23 08:58:29 2019 +0200
+++ b/db/pubsub.sql	Wed Jun 12 21:51:50 2019 +0200
@@ -13,7 +13,10 @@
         CHECK (access_model IN ('open', 'presence', 'publisher-roster', 'whitelist', 'publish-only', 'self-publisher')),
     persist_items boolean,
     deliver_payloads boolean NOT NULL DEFAULT TRUE,
+    max_items integer NOT NULL DEFAULT 0
+		CHECK (max_items >= 0),
     serial_ids boolean NOT NULL DEFAULT FALSE,
+    consistent_publisher boolean NOT NULL DEFAULT FALSE,
     send_last_published_item text NOT NULL DEFAULT 'on_sub'
         CHECK (send_last_published_item IN ('never', 'on_sub')),
     publish_model text NOT NULL DEFAULT 'publishers'
@@ -102,4 +105,4 @@
 	value text
 );
 
-INSERT INTO metadata VALUES ('version', '4');
+INSERT INTO metadata VALUES ('version', '5');
--- a/db/sat_pubsub_update_0_1.sql	Thu May 23 08:58:29 2019 +0200
+++ b/db/sat_pubsub_update_0_1.sql	Wed Jun 12 21:51:50 2019 +0200
@@ -6,7 +6,7 @@
 BEGIN
     SELECT value INTO ver FROM metadata WHERE key='version';
     IF FOUND THEN
-        RAISE EXCEPTION 'This update file need to be applied on older database, you don''t have to use it';
+        RAISE EXCEPTION 'This update file needs to be applied on older database, you don''t have to use it';
     END IF;
 END$$;
 \unset ON_ERROR_STOP
--- a/db/sat_pubsub_update_1_2.sql	Thu May 23 08:58:29 2019 +0200
+++ b/db/sat_pubsub_update_1_2.sql	Wed Jun 12 21:51:50 2019 +0200
@@ -6,7 +6,7 @@
 BEGIN
     SELECT value INTO ver FROM metadata WHERE key='version';
     IF NOT FOUND OR ver!='1' THEN
-        RAISE EXCEPTION 'This update file need to be applied on database schema version 1, you use version %',ver;
+        RAISE EXCEPTION 'This update file needs to be applied on database schema version 1, you use version %',ver;
     END IF;
 END$$;
 \unset ON_ERROR_STOP
--- a/db/sat_pubsub_update_2_3.sql	Thu May 23 08:58:29 2019 +0200
+++ b/db/sat_pubsub_update_2_3.sql	Wed Jun 12 21:51:50 2019 +0200
@@ -6,7 +6,7 @@
 BEGIN
     SELECT value INTO ver FROM metadata WHERE key='version';
     IF NOT FOUND OR ver!='2' THEN
-        RAISE EXCEPTION 'This update file need to be applied on database schema version 2, you use version %',ver;
+        RAISE EXCEPTION 'This update file needs to be applied on database schema version 2, you use version %',ver;
     END IF;
 END$$;
 \unset ON_ERROR_STOP
--- a/db/sat_pubsub_update_3_4.sql	Thu May 23 08:58:29 2019 +0200
+++ b/db/sat_pubsub_update_3_4.sql	Wed Jun 12 21:51:50 2019 +0200
@@ -6,7 +6,7 @@
 BEGIN
     SELECT value INTO ver FROM metadata WHERE key='version';
     IF NOT FOUND OR ver!='3' THEN
-        RAISE EXCEPTION 'This update file need to be applied on database schema version 3, you use version %',ver;
+        RAISE EXCEPTION 'This update file needs to be applied on database schema version 3, you use version %',ver;
     END IF;
 END$$;
 \unset ON_ERROR_STOP
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/db/sat_pubsub_update_4_5.sql	Wed Jun 12 21:51:50 2019 +0200
@@ -0,0 +1,20 @@
+-- we check version of the database before doing anything
+-- and stop execution if not good
+\set ON_ERROR_STOP
+DO $$
+DECLARE ver text;
+BEGIN
+    SELECT value INTO ver FROM metadata WHERE key='version';
+    IF NOT FOUND OR ver!='4' THEN
+        RAISE EXCEPTION 'This update file needs to be applied on database schema version 4, you use version %',ver;
+    END IF;
+END$$;
+\unset ON_ERROR_STOP
+-- end of version check
+
+/* new "max_items" and "consistent publisher" options */
+ALTER TABLE nodes ADD COLUMN max_items integer NOT NULL DEFAULT 0
+		CHECK (max_items >= 0);
+ALTER TABLE nodes ADD COLUMN consistent_publisher boolean NOT NULL DEFAULT FALSE;
+
+UPDATE metadata SET value='5' WHERE key='version';
--- a/src/backend.py	Thu May 23 08:58:29 2019 +0200
+++ b/src/backend.py	Wed Jun 12 21:51:50 2019 +0200
@@ -176,6 +176,9 @@
             const.OPT_SERIAL_IDS:
                 {"type": "boolean",
                  "label": "Use serial ids"},
+            const.OPT_CONSISTENT_PUBLISHER:
+                {"type": "boolean",
+                 "label": "Keep publisher on update"},
             }
 
     subscriptionOptions = {
@@ -379,8 +382,10 @@
         item_elt.addChild(item_form.toElement())
 
     def _checkOverwrite(self, node, itemIdentifiers, publisher):
-        """Check that the itemIdentifiers correspond to items published
-        by the current publisher"""
+        """Check that publisher can overwrite items
+
+        current publisher must correspond to each item publisher
+        """
         def doCheck(item_pub_map):
             for item_publisher in item_pub_map.itervalues():
                 if item_publisher.userhost() != publisher.userhost():
@@ -445,10 +450,30 @@
 
         if persistItems:
 
-            if check_overwrite and affiliation != 'owner' and not self.isAdmin(requestor):
-                # we don't want a publisher to overwrite the item
-                # of an other publisher
-                yield self._checkOverwrite(node, [item['id'] for item in items if item.getAttribute('id')], requestor)
+            if check_overwrite:
+                itemIdentifiers = [item['id'] for item in items
+                                   if item.getAttribute('id')]
+
+                if affiliation == 'owner' or self.isAdmin(requestor):
+                    if configuration[const.OPT_CONSISTENT_PUBLISHER]:
+                        pub_map = yield node.getItemsPublishers(itemIdentifiers)
+                        publishers = set(pub_map.values())
+                        if len(publishers) != 1:
+                            # TODO: handle multiple items publishing (from several
+                            #       publishers)
+                            raise error.NoPublishing(
+                                u"consistent_publisher is currently only possible when "
+                                u"publishing items from a single publisher. Try to "
+                                u"publish one item at a time")
+                        # we replace requestor and new payload's publisher by original
+                        # item publisher to keep publisher consistent
+                        requestor = publishers.pop()
+                        for item in items:
+                            item['publisher'] = requestor.full()
+                else:
+                    # we don't want a publisher to overwrite the item
+                    # of an other publisher
+                    yield self._checkOverwrite(node, itemIdentifiers, requestor)
 
             # TODO: check conflict and recalculate max id if serial_ids is set
             yield node.storeItems(items_data, requestor)
--- a/src/const.py	Thu May 23 08:58:29 2019 +0200
+++ b/src/const.py	Wed Jun 12 21:51:50 2019 +0200
@@ -66,6 +66,7 @@
 OPT_SEND_LAST_PUBLISHED_ITEM = "pubsub#send_last_published_item"
 OPT_PUBLISH_MODEL = 'pubsub#publish_model'
 OPT_SERIAL_IDS = 'pubsub#serial_ids'
+OPT_CONSISTENT_PUBLISHER = 'pubsub#consistent_publisher'
 VAL_AMODEL_OPEN = 'open'
 VAL_AMODEL_PRESENCE = 'presence'
 VAL_AMODEL_PUBLISHER_ROSTER = 'publisher-roster'
--- a/src/pgsql_storage.py	Thu May 23 08:58:29 2019 +0200
+++ b/src/pgsql_storage.py	Wed Jun 12 21:51:50 2019 +0200
@@ -79,7 +79,7 @@
 parseXml = lambda unicode_data: generic.parseXml(unicode_data.encode('utf-8'))
 ITEMS_SEQ_NAME = u'node_{node_id}_seq'
 PEP_COL_NAME = 'pep'
-CURRENT_VERSION = '4'
+CURRENT_VERSION = '5'
 # retrieve the maximum integer item id + 1
 NEXT_ITEM_ID_QUERY = r"SELECT COALESCE(max(item::integer)+1,1) as val from items where node_id={node_id} and item ~ E'^\\d+$'"
 
@@ -114,6 +114,7 @@
                 const.OPT_ACCESS_MODEL: const.VAL_AMODEL_DEFAULT,
                 const.OPT_PUBLISH_MODEL: const.VAL_PMODEL_DEFAULT,
                 const.OPT_SERIAL_IDS: False,
+                const.OPT_CONSISTENT_PUBLISHER: False,
             },
             'collection': {
                 const.OPT_DELIVER_PAYLOADS: True,
@@ -154,8 +155,9 @@
                     const.OPT_ACCESS_MODEL:row[6],
                     const.OPT_PUBLISH_MODEL:row[7],
                     const.OPT_SERIAL_IDS:row[8],
+                    const.OPT_CONSISTENT_PUBLISHER:row[9],
                     }
-            schema = row[9]
+            schema = row[10]
             if schema is not None:
                 schema = parseXml(schema)
             node = LeafNode(row[0], row[1], configuration, schema)
@@ -191,6 +193,7 @@
                                  access_model,
                                  publish_model,
                                  serial_ids,
+                                 consistent_publisher,
                                  schema::text,
                                  pep
                             FROM nodes
@@ -212,6 +215,7 @@
                                           access_model,
                                           publish_model,
                                           serial_ids,
+                                          consistent_publisher,
                                           schema::text,
                                           pep
                                    FROM nodes
@@ -265,10 +269,11 @@
                                access_model,
                                publish_model,
                                serial_ids,
+                               consistent_publisher,
                                schema,
                                pep)
                               VALUES
-                              (%s, 'leaf', %s, %s, %s, %s, %s, %s, %s, %s)""",
+                              (%s, 'leaf', %s, %s, %s, %s, %s, %s, %s, %s, %s)""",
                            (nodeIdentifier,
                             config['pubsub#persist_items'],
                             config['pubsub#deliver_payloads'],
@@ -276,6 +281,7 @@
                             config[const.OPT_ACCESS_MODEL],
                             config[const.OPT_PUBLISH_MODEL],
                             config[const.OPT_SERIAL_IDS],
+                            config[const.OPT_CONSISTENT_PUBLISHER],
                             schema,
                             recipient.userhost() if pep else None
                             )
@@ -535,7 +541,8 @@
                                            send_last_published_item=%s,
                                            access_model=%s,
                                            publish_model=%s,
-                                           serial_ids=%s
+                                           serial_ids=%s,
+                                           consistent_publisher=%s
                           WHERE node_id=%s""",
                        (config[const.OPT_PERSIST_ITEMS],
                         config[const.OPT_DELIVER_PAYLOADS],
@@ -543,6 +550,7 @@
                         config[const.OPT_ACCESS_MODEL],
                         config[const.OPT_PUBLISH_MODEL],
                         config[const.OPT_SERIAL_IDS],
+                        config[const.OPT_CONSISTENT_PUBLISHER],
                         self.nodeDbId))
 
     def _setCachedConfiguration(self, void, config):