Skip to content

Instantly share code, notes, and snippets.

@Roy-Orbison
Last active October 22, 2018 23:08
Show Gist options
  • Save Roy-Orbison/93806ac74c4dbd66d5ee1cb3b6e3b78e to your computer and use it in GitHub Desktop.
Save Roy-Orbison/93806ac74c4dbd66d5ee1cb3b6e3b78e to your computer and use it in GitHub Desktop.
Sendy duplicate subscription removal (tested with v3.0.3 and PHP v5.3.29)
<?php
include('includes/header.php');
include('includes/login/auth.php');
include('includes/subscribers/main.php');
include('includes/helpers/short.php');
#ini_set('error_reporting', E_ALL & ~E_NOTICE);
#ini_set('display_errors', 1);
$subscribers = $lists = array();
try {
if (!empty($_POST['del'])) {
$emails_del_count_last = 0;
foreach ($_POST['del'] as $list_id => $emails_del_all) {
while ($emails_del_all) {
$emails_del = array_splice($emails_del_all, 0, 20);# delete in batches of 20 at most
$emails_del_count = count($emails_del);
if ($emails_del_count != $emails_del_count_last) {
$mysqli_stmt = $mysqli->prepare(
'DELETE FROM subscribers
WHERE list = ? AND email IN (' . implode(', ', array_fill(0, $emails_del_count, '?')) . ')'
);
$bind_param_types = 'i' . str_repeat('s', $emails_del_count);
$emails_del_count_last = $emails_del_count;
}
$bind_param_args = array(
$bind_param_types,
&$list_id,
);
foreach ($emails_del as $i => $email_del) {
$bind_param_args[] = &$emails_del[$i];
}
call_user_func_array(array(&$mysqli_stmt, 'bind_param'), $bind_param_args);
if (!$mysqli_stmt->execute()) {
throw new Exception($mysqli_stmt->error);
}
}
}
}
$mysqli_result = $mysqli->query(
'SELECT email, GROUP_CONCAT(list) AS lists, COUNT(list) AS list_count, SUM(unsubscribed) AS has_unsubscribed
FROM subscribers GROUP BY email
HAVING list_count > 1 AND has_unsubscribed = 0'
);
if ($mysqli_result) {
while ($dupe = $mysqli_result->fetch_assoc()) {
$dupe_lists = array_fill_keys(explode(',', $dupe['lists']), true);
$subscribers[$dupe['email']] = $dupe_lists;
$lists += $dupe_lists;
}
}
if ($lists) {
$list_ids = array_keys($lists);
$lists = array();
$list_count = count($list_ids);
$mysqli_stmt = $mysqli->prepare(
'SELECT id, name FROM lists
WHERE id IN (' . implode(', ', array_fill(0, $list_count, '?')) . ')
ORDER BY name'
);
$bind_param_args = array(str_repeat('i', $list_count));
foreach ($list_ids as $i => $list_id) {
$bind_param_args[] = &$list_ids[$i];
}
call_user_func_array(array(&$mysqli_stmt, 'bind_param'), $bind_param_args);
if (!$mysqli_stmt->execute()) {
throw new Exception($mysqli_stmt->error);
}
$mysqli_stmt->bind_result($list_id, $list_name);
while ($mysqli_stmt->fetch()) {
$lists[$list_id] = $list_name;
}
}
}
catch (Exception $e) {
?>
<div class="alert alert-error">
<button type="button" class="close" data-dismiss="alert">×</button>
<strong><?php echo htmlspecialchars($e->getMessage());?></strong>
</div>
<?php
}
?>
<style type="text/css">
@media (min-width: 768px) {
.sticky-head {
max-height: calc(100vh - 200px);
overflow: auto;
}
.sticky-head thead {
position: sticky;
top: 0;
background: white;
}
tr:hover {
background: lightgray;
}
}
</style>
<h2><?php echo _('Duplicate removal');?></h2>
<?php if ($subscribers) {?>
<form action="" method="post" class="sticky-head">
<table>
<thead>
<tr>
<th><?php echo _('Subscriber');?></th>
<?php foreach ($lists as $list_id => $list_name) {?>
<th><?php echo htmlspecialchars($list_name);?></th>
<?php }?>
</tr>
</thead>
<tbody>
<?php foreach ($subscribers as $subscriber_email => $subscriber_lists) {?>
<tr>
<td><?php echo htmlspecialchars($subscriber_email);?></td>
<?php foreach ($lists as $list_id => $list_name) {?>
<td><?php if (isset($subscriber_lists[$list_id])) {
?><input type="checkbox" name="del[<?php echo $list_id;?>][]"
value="<?php echo htmlspecialchars($subscriber_email);?>"><?php
}?></td>
<?php }?>
</tr>
<?php }?>
</tbody>
</table>
<button type="submit" class="btn"><?php echo _('Remove selected');?></button>
</form>
<?php } else {?>
<p><?php echo _('None found');?></p>
<?php }
include('includes/footer.php');
@Roy-Orbison
Copy link
Author

Was inspired to do a simpler, thorough version of this http://www.tech-and-dev.com/2017/07/removing-duplicates-from-different-lists-sendy.html

Sendy does not distinguish, schema-wise between subscribers and subscriptions so this is the best you'll get. Warning: it will unceremoniously, and permanently wipe the selected subscribers from your database.

@Roy-Orbison
Copy link
Author

Have changed this to fix styles and to hide those who've unsubscribed from any list, because we use the Sendy setting where an unsubscribe counts as from all lists.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment