diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Node.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Node.java
index b230b89b1..770bf8ff9 100644
--- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Node.java
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Node.java
@@ -217,6 +217,31 @@ public abstract class Node {
return subElem.getSubscriptions();
}
+ /**
+ * Modify the subscriptions for this PubSub node as owner.
+ *
+ * Note that the subscriptions are _not_ checked against the existing subscriptions
+ * since these are not cached (and indeed could change asynchronously)
+ *
+ *
+ * @param changedSubs subscriptions that have changed
+ * @return null
or a PubSub stanza with additional information on success.
+ * @throws NoResponseException
+ * @throws XMPPErrorException
+ * @throws NotConnectedException
+ * @throws InterruptedException
+ * @see XEP-60 ยง 8.8.2 Modify Subscriptions
+ * @since 4.3
+ */
+ public PubSub modifySubscriptionsAsOwner(List changedSubs)
+ throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
+
+ PubSub pubSub = createPubsubPacket(Type.set,
+ new SubscriptionsExtension(getId(), changedSubs),
+ PubSubNamespace.OWNER);
+ return sendPubsubPacket(pubSub);
+ }
+
/**
* Get the affiliations of this node.
*
diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Subscription.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Subscription.java
index 144dc308d..a00fcc7e1 100644
--- a/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Subscription.java
+++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/pubsub/Subscription.java
@@ -56,6 +56,16 @@ public class Subscription extends NodeExtension {
this(subscriptionJid, nodeId, null, null);
}
+ /**
+ * Construct a subscription change request to the specified state.
+ *
+ * @param subscriptionJid The subscriber JID
+ * @param state The requested new state
+ */
+ public Subscription(Jid subscriptionJid, State state) {
+ this(subscriptionJid, null, null, state);
+ }
+
/**
* Constructs a representation of a subscription reply to the specified node
* and JID. The server will have supplied the subscription id and current state.
diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/PubSubNodeTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/PubSubNodeTest.java
new file mode 100644
index 000000000..28bf82a31
--- /dev/null
+++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/pubsub/PubSubNodeTest.java
@@ -0,0 +1,70 @@
+/**
+ *
+ * Copyright 2018 Timothy Pitt
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jivesoftware.smackx.pubsub;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+
+import org.jivesoftware.smack.SmackException;
+import org.jivesoftware.smack.ThreadedDummyConnection;
+import org.jivesoftware.smack.XMPPException;
+import org.jivesoftware.smack.test.util.TestUtils;
+import org.jivesoftware.smack.util.PacketParserUtils;
+import org.jivesoftware.smackx.pubsub.packet.PubSub;
+
+import org.junit.Test;
+import org.jxmpp.jid.JidTestUtil;
+import org.jxmpp.jid.impl.JidCreate;
+import org.xmlpull.v1.XmlPullParser;
+
+public class PubSubNodeTest {
+
+ @Test
+ public void modifySubscriptionsAsOwnerTest() throws InterruptedException, SmackException, IOException, XMPPException, Exception {
+ ThreadedDummyConnection con = ThreadedDummyConnection.newInstance();
+ PubSubManager mgr = new PubSubManager(con, JidTestUtil.PUBSUB_EXAMPLE_ORG);
+ Node testNode = new LeafNode(mgr, "princely_musings");
+
+ List ChangeSubs = Arrays.asList(
+ new Subscription(JidCreate.from("romeo@montague.org"), Subscription.State.subscribed),
+ new Subscription(JidCreate.from("juliet@capulet.org"), Subscription.State.none)
+ );
+ testNode.modifySubscriptionsAsOwner(ChangeSubs);
+
+ PubSub request = con.getSentPacket();
+
+ assertEquals("http://jabber.org/protocol/pubsub#owner", request.getChildElementNamespace());
+ assertEquals("pubsub", request.getChildElementName());
+
+ XmlPullParser parser = TestUtils.getIQParser(request.toXML().toString());
+ PubSub pubsubResult = (PubSub) PacketParserUtils.parseIQ(parser);
+ SubscriptionsExtension subElem = pubsubResult.getExtension(PubSubElementType.SUBSCRIPTIONS);
+ List subscriptions = subElem.getSubscriptions();
+ assertEquals(2, subscriptions.size());
+
+ Subscription sub1 = subscriptions.get(0);
+ assertEquals("romeo@montague.org", sub1.getJid().toString());
+ assertEquals(Subscription.State.subscribed, sub1.getState());
+
+ Subscription sub2 = subscriptions.get(1);
+ assertEquals("juliet@capulet.org", sub2.getJid().toString());
+ assertEquals(Subscription.State.none, sub2.getState());
+ }
+}