From 30ea88b1a3d78629cdbbf1b7ea7a95f66ab48842 Mon Sep 17 00:00:00 2001
From: battye <battye@phpbb.com>
Date: Sat, 1 Aug 2020 12:02:38 -0400
Subject: [PATCH 1/2] Add listeners for user deletion with functions to delete
 contribs, CDB posts and CDB topics

---
 config/services.yml              |   1 +
 event/main_listener.php          | 155 ++++++++++++++++++++++++++++++-
 language/en/info_acp_titania.php |   2 +
 3 files changed, 157 insertions(+), 1 deletion(-)

diff --git a/config/services.yml b/config/services.yml
index c02e4c95e..8e43163da 100644
--- a/config/services.yml
+++ b/config/services.yml
@@ -214,6 +214,7 @@ services:
             - '@dbal.conn'
             - '@user'
             - '@template'
+            - '@language'
             - '@phpbb.titania.controller.helper'
             - '@phpbb.titania.access'
             - '@phpbb.titania.contribution.type.collection'
diff --git a/event/main_listener.php b/event/main_listener.php
index 8993d730e..220f19439 100644
--- a/event/main_listener.php
+++ b/event/main_listener.php
@@ -16,6 +16,7 @@
 use phpbb\db\driver\driver_interface as db_driver_interface;
 use phpbb\event\data;
 use phpbb\auth\auth;
+use phpbb\language\language;
 use phpbb\template\template;
 use phpbb\titania\controller\helper;
 use phpbb\titania\ext;
@@ -44,6 +45,9 @@ class main_listener implements EventSubscriberInterface
 	/** @var \phpbb\template\template */
 	protected $template;
 
+	/** @var \phpbb\language\language */
+	protected $language;
+
 	/** @var \phpbb\titania\controller\helper */
 	protected $controller_helper;
 
@@ -79,12 +83,13 @@ class main_listener implements EventSubscriberInterface
 	 * @param string $ext_root_path Titania root path
 	 * @param string $php_ext PHP file extension
 	 */
-	public function __construct(request $request, db_driver_interface $db, user $user, template $template, helper $controller_helper, access $access, type_collection $types, $phpbb_root_path, $ext_root_path, $php_ext)
+	public function __construct(request $request, db_driver_interface $db, user $user, template $template, language $language, helper $controller_helper, access $access, type_collection $types, $phpbb_root_path, $ext_root_path, $php_ext)
 	{
 		$this->request = $request;
 		$this->db = $db;
 		$this->user = $user;
 		$this->template = $template;
+		$this->language = $language;
 		$this->controller_helper = $controller_helper;
 		$this->access = $access;
 		$this->types = $types;
@@ -105,6 +110,10 @@ static public function getSubscribedEvents()
 			// Check whether a user is removed from a team
 			'core.group_delete_user_after'				=> 'remove_users_from_subscription',
 
+			// Check whether a user is deleted
+			'core.acp_users_overview_before'			=> 'user_delete',
+			'core.delete_user_before'				=> 'remove_contributions',
+
 			// Include quoted text when private messaging
 			'core.ucp_pm_compose_predefined_message'	=> 'quote_text_upon_pm',
 		);
@@ -406,4 +415,148 @@ public function quote_text_upon_pm($event)
 			}
 		}
 	}
+
+	/**
+	 * Append our message to inform the admin that deleting the user will also delete things in Titania
+	 * @param $event
+	 */
+	public function user_delete($event)
+	{
+		// Add our message to the end of the existing one
+		$this->language->add_lang('info_acp_titania', 'phpbb/titania');
+		$stored_lang = $this->user->lang;
+		$stored_lang['CONFIRM_OPERATION'] .= ' ' . $stored_lang['CONFIRM_DELETE_USER_OPERATION'];
+		$this->user->lang = $stored_lang;
+	}
+
+	/**
+	 * Automatically remove topics, posts and contributions when user is deleted
+	 * @param $event
+	 */
+	public function remove_contributions($event)
+	{
+		if (!defined('TITANIA_CONTRIBS_TABLE'))
+		{
+			// Include Titania so we can access the constants
+			require($this->ext_root_path . 'common.' . $this->php_ext);
+		}
+
+		$event_data = $event->get_data();
+
+		// Get rid of the contribs that only have unapproved revisions.
+		// We'll keep anything that has an approved revision
+		$this->delete_user_contribs_with_unapproved_revisions($event_data);
+
+		// Delete Titania topics and posts by the user(s)
+		if ($event_data['mode'] === 'remove')
+		{
+			// Handle the topics
+			$this->delete_user_titania_topics($event_data);
+
+			// Handle the posts
+			$this->delete_user_titania_posts($event_data);
+		}
+
+// TODO: remove debug code
+die('-- STOP HERE --');
+	}
+
+	/**
+	 * Delete contributions by the selected users that only contain unapproved revisions
+	 * @param $event_data
+	 */
+	private function delete_user_contribs_with_unapproved_revisions($event_data)
+	{
+		// Delete contributions without any approved revisions
+		$sql_array = array(
+			'SELECT'	=> 'c.contrib_id, COUNT(r.revision_id) AS approved_revisions',
+			'FROM'		=> array(
+				TITANIA_CONTRIBS_TABLE => 'c',
+			),
+
+			'LEFT_JOIN'	=> array(
+				array(
+					'FROM'	=> array(TITANIA_REVISIONS_TABLE => 'r'),
+					'ON'	=> 'c.contrib_id = r.contrib_id AND revision_status = ' . ext::TITANIA_REVISION_APPROVED,
+				),
+			),
+
+			'WHERE'		=>  $this->db->sql_in_set('c.contrib_user_id', $event_data['user_ids']),
+
+			'GROUP_BY'	=> 'c.contrib_id',
+		);
+
+		$sql = $this->db->sql_build_query('SELECT', $sql_array);
+		$result = $this->db->sql_query($sql);
+
+		while ($row = $this->db->sql_fetchrow($result))
+		{
+			$contrib_id = (int) $row['contrib_id'];
+			$approved_revisions = (int) $row['approved_revisions'];
+
+			// If there are no approved revisions, then delete the contribution
+			if ($approved_revisions === 0)
+			{
+				$contrib = new \titania_contribution;
+
+				if ($contrib->load($contrib_id))
+				{
+					// Delete now
+					$contrib->delete();
+				}
+			}
+
+// TODO: remove debug code
+echo $contrib_id . ': '. $approved_revisions . '<br />';
+		}
+
+		$this->db->sql_freeresult($result);
+	}
+
+	/**
+	 * Delete Titania topics by the selected users
+	 * @param $event_data
+	 */
+	private function delete_user_titania_topics($event_data)
+	{
+		// Delete topics by the user in Titania
+		$topic = new \titania_topic();
+
+		$sql = 'SELECT * FROM ' . TITANIA_TOPICS_TABLE . '
+				WHERE ' . $this->db->sql_in_set('topic_first_post_user_id', $event_data['user_ids']);
+
+		$result = $this->db->sql_query($sql);
+
+		while ($row = $this->db->sql_fetchrow($result))
+		{
+			// Delete each topic created by the to-be deleted user
+			$topic->__set_array($row);
+			$topic->delete();
+		}
+
+		$this->db->sql_freeresult($result);
+	}
+
+	/**
+	 * Delete Titania posts by the selected users
+	 * @param $event_data
+	 */
+	private function delete_user_titania_posts($event_data)
+	{
+		// Delete posts by the user in Titania
+		$post = new \titania_post();
+
+		$sql = 'SELECT * FROM ' . TITANIA_POSTS_TABLE . '
+				WHERE ' . $this->db->sql_in_set('post_user_id', $event_data['user_ids']);
+
+		$result = $this->db->sql_query($sql);
+
+		while ($row = $this->db->sql_fetchrow($result))
+		{
+			$post->__set_array($row);
+			$post->delete();
+		}
+
+		$this->db->sql_freeresult($result);
+	}
 }
diff --git a/language/en/info_acp_titania.php b/language/en/info_acp_titania.php
index 77487bb5d..eead6fdbd 100644
--- a/language/en/info_acp_titania.php
+++ b/language/en/info_acp_titania.php
@@ -27,6 +27,8 @@
 
 // Merge the following language entries into the lang array
 $lang = array_merge($lang, array(
+	'CONFIRM_DELETE_USER_OPERATION'		=> 'This will also remove all Customisation Database posts, topics and unapproved contributions submitted by this user.',
+
 	'ROLE_TITANIA_MODIFICATION_TEAM'	=> 'Titania Modifications Team Role',
 	'ROLE_TITANIA_STYLE_TEAM'			=> 'Titania Style Team Role',
 	'ROLE_TITANIA_MODERATOR_TEAM'		=> 'Titania Moderation Team Role',

From 9d670ad1fde64156729035d92551084059ae6899 Mon Sep 17 00:00:00 2001
From: battye <battye@phpbb.com>
Date: Sun, 2 Aug 2020 08:37:13 -0400
Subject: [PATCH 2/2] Limit post/topic deletion to the Titania support type

---
 event/main_listener.php | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)

diff --git a/event/main_listener.php b/event/main_listener.php
index 220f19439..34e2de770 100644
--- a/event/main_listener.php
+++ b/event/main_listener.php
@@ -448,7 +448,7 @@ public function remove_contributions($event)
 		$this->delete_user_contribs_with_unapproved_revisions($event_data);
 
 		// Delete Titania topics and posts by the user(s)
-		if ($event_data['mode'] === 'remove')
+		if ($event_data['mode'] === 'remove' || $event_data['mode'] == 'retain')
 		{
 			// Handle the topics
 			$this->delete_user_titania_topics($event_data);
@@ -456,9 +456,6 @@ public function remove_contributions($event)
 			// Handle the posts
 			$this->delete_user_titania_posts($event_data);
 		}
-
-// TODO: remove debug code
-die('-- STOP HERE --');
 	}
 
 	/**
@@ -505,9 +502,6 @@ private function delete_user_contribs_with_unapproved_revisions($event_data)
 					$contrib->delete();
 				}
 			}
-
-// TODO: remove debug code
-echo $contrib_id . ': '. $approved_revisions . '<br />';
 		}
 
 		$this->db->sql_freeresult($result);
@@ -523,7 +517,8 @@ private function delete_user_titania_topics($event_data)
 		$topic = new \titania_topic();
 
 		$sql = 'SELECT * FROM ' . TITANIA_TOPICS_TABLE . '
-				WHERE ' . $this->db->sql_in_set('topic_first_post_user_id', $event_data['user_ids']);
+				WHERE ' . $this->db->sql_in_set('topic_first_post_user_id', $event_data['user_ids']) . '
+				AND topic_type = ' . ext::TITANIA_SUPPORT;
 
 		$result = $this->db->sql_query($sql);
 
@@ -547,7 +542,8 @@ private function delete_user_titania_posts($event_data)
 		$post = new \titania_post();
 
 		$sql = 'SELECT * FROM ' . TITANIA_POSTS_TABLE . '
-				WHERE ' . $this->db->sql_in_set('post_user_id', $event_data['user_ids']);
+				WHERE ' . $this->db->sql_in_set('post_user_id', $event_data['user_ids']) . '
+				AND post_type = ' . ext::TITANIA_SUPPORT;
 
 		$result = $this->db->sql_query($sql);