// ==UserScript== // @name Stack Overflow Bookending Helper // @description Easier posts bulk-delete when applying bookending on AI-generated content, forked from Samuel Liew's DeletedUsersHelper (https://github.com/samliew/SO-mod-userscripts/blob/master/DeletedUsersHelper.user.js) // @downloadURL https://raw.githubusercontent.com/blackgreen100/SO-bookend-helper/master/script.user.js // @updateURL https://raw.githubusercontent.com/blackgreen100/SO-bookend-helper/master/script.user.js // @author blackgreen // @version 0.0.3 // // @match https://*.stackoverflow.com/users/* // @exclude https://contests.stackoverflow.com/* // @exclude *chat.* // @exclude *blog.* // @exclude */tour // // @require https://raw.githubusercontent.com/samliew/SO-mod-userscripts/master/lib/se-ajax-common.js // @require https://raw.githubusercontent.com/samliew/SO-mod-userscripts/master/lib/common.js // // @grant GM_xmlhttpRequest // ==/UserScript== /* globals StackExchange, fkey */ /// 'use strict'; // 404 on a specific user page that has no content if (document.body.innerText === 'User not found.') { // Redirect to user profile page location = `/users/${currentUserId}`; return; } // Moderator check, after the redirection for 404 page above // This is a moderator-only userscript if (!isModerator()) return; // Reload functions const reloadWhenDone = () => { // Triggers when all ajax requests have completed $(document).ajaxStop(function () { location.reload(); }); }; let ajaxRequests = 0; // Get deleted user's username function getDeletedUsername(uid) { ajaxRequests++; return new Promise(function (resolve, reject) { if (typeof uid === 'undefined' || uid === null) { reject(); return; } $.get(`${location.origin}/users/${uid}`) .done(function (data) { const page = $(data); const pageTitle = $('title', data).text(); // User not deleted or not found if (pageTitle && pageTitle.indexOf('User deleted') === -1) { reject(); return; } // Get username const details = page.find('#mainbar-full').find('pre').first().text().split(/\r?\n/); const username = details[1].match(/: ([^\(]+)/)[1].trim(); resolve(username); }) .fail(reject) .always(() => ajaxRequests--); }); } // Delete individual post function deletePost(pid) { ajaxRequests++; return new Promise(function (resolve, reject) { if (typeof pid === 'undefined' || pid === null) { reject(); return; } $.post({ url: `${location.origin}/posts/${pid}/vote/10`, data: { 'fkey': fkey } }) .fail(reject) .always(() => ajaxRequests--); }); } // Delete posts function deletePosts(pids) { if (typeof pids === 'undefined' || pids.length === 0) return; pids.forEach(v => deletePost(v)); } // Undelete individual post function undeletePost(pid) { ajaxRequests++; return new Promise(function (resolve, reject) { if (typeof pid === 'undefined' || pid === null) { reject(); return; } $.post({ url: `${location.origin}/posts/${pid}/vote/11`, data: { 'fkey': fkey } }) .fail(reject) .always(() => ajaxRequests--); }); } // Undelete posts function undeletePosts(pids) { if (typeof pids === 'undefined' || pids.length === 0) return; pids.forEach(v => undeletePost(v)); } function getUserDetails(uid) { return new Promise(function (resolve, reject) { if (typeof uid === 'undefined' || uid === null) { reject(); return; } $.post(`${seApiUrl}/users/${uid}?order=desc&sort=reputation&site=${location.hostname.replace(/(\.stackexchange)?\.com$/, '')}&filter=!--1nZv)deGu1&key=lSrVEbQTXrJ4eb4c3NEMXQ((`) .done(function (data) { resolve(data); }) .fail(reject); }); } function initMultiPostsTable() { const table = $('#js-post-summaries'); if (table.length === 0) return; $(this).attr('disabled', true); // Add checkboxes table.find('.s-post-summary:not(.s-post-summary__deleted)').each(function () { const url = $(this).find('a').attr('href'); if (url && /\/\d+\//.test(url)) { const pid = url.match(/\/\d+/g).reverse()[0].substr(1); $(this).prepend(`
`); $(this).toggleClass('deleted-answer', $(this).children().last().text() === 'Yes'); } }); const bookendButton = $('button.so-bookend') const btnDiv = $(`
`).insertAfter(bookendButton); $(``) .appendTo(btnDiv) .on('click', function () { const boxes = $('.selected-post'); const active = $(this).attr('active'); if (!active) { boxes.prop('checked', true); $(this).attr('active', true); $(this).attr('value', 'Deselect all') } else { boxes.prop('checked', false); $(this).attr('active', false); $(this).attr('value', 'Select all'); } }) $(``) .appendTo(btnDiv) .on('click', function () { let selPostIds = $('.selected-post').filter(':checked').map((i, v) => v.value).get(); if (selPostIds.length === 0) { alert('No posts selected!'); return false; } if(!confirm(`Are you sure you want to bulk-delete ${selPostIds.length} selected posts?`)) { return; } $('.action-btn').remove(); deletePosts(selPostIds) reloadWhenDone(); }); // $(``) // .appendTo(btnDiv) // .on('click', function () { // let selPostIds = $('.selected-post').filter(':checked').map((i, v) => v.value).get(); // if (selPostIds.length === 0) { // alert('No posts selected!'); // return false; // } // $('.action-btn').remove(); // undeletePosts(selPostIds); // reloadWhenDone(); // }); } // Append styles addStylesheet(` .orig-username { margin-top: 2px; color: var(--red-700); } .orig-username:before { content: '"'; } .orig-username:after { content: '"'; } .deleted-user, .comment-user .deleted-user { display: inline-block; padding: 3px 5px; background: var(--red-600) !important; color: var(--white) !important; font-style: italic; } .comment-user .deleted-user + div.orig-username { display: inline-block; color: var(--red-700); margin: 0 4px; } table#posts { min-width: 80%; } table#posts td { position: relative; background: none !important; } .action-btn { margin-right: 10px; } .del-user-info { margin: 15px 0; padding: 12px 14px; background: var(--black-050); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; } .del-user-info input { margin: 0; padding: 0; border: none; border-bottom: 1px dashed var(--red-700); font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; background: transparent; color: var(--red-700); } .del-user-info .del-reason { white-space: pre-wrap; margin: 20px 0; } #del-user-links { margin-top: 10px; margin-bottom: 30px; } #del-user-links:before { content: 'User links'; display: block; margin: 0 0 8px -30px; font-weight: bold; } #del-user-links li { margin-bottom: 4px; } #pii-info, #deleteReasonDetails, #destroyReasonDetails { width: 100%; height: calc(8.4em + 20px); line-height: 1.2em; font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif; } /* Network Account container */ #del-user-networkaccs { margin-top: 10px; margin-bottom: 30px; } #del-user-networkaccs:before { content: 'Network accounts'; display: block; margin: 0 0 8px -30px; font-weight: bold; clear: both; } #del-user-networkaccs:after { content: ''; display: block; clear: both; } #del-user-networkaccs.js-loading:after { content: 'loading...'; } #del-user-networkaccs.js-no-accounts:after { content: '(none)'; font-style: italic; color: var(--black-400); } .account-container { float: left; width: 100%; margin-left: -10px; padding: 10px; text-align: left; font-size: 0.9em; border-bottom: 1px solid var(--black-050); clear: both; } .account-container .account-icon { width: 48px; height: 48px; float: left; margin-right: 15px; text-align: center; border-bottom: 1px solid var(--black-075); border-left: 1px solid var(--black-025); border-right: 1px solid var(--black-025); border-top: 1px solid var(--black-025); } .account-container .account-icon img { width: 48px; height: 48px; display: block; -ms-interpolation-mode: bicubic; image-rendering: optimizeQuality; } .account-container .account-site { float: left; width: 424px; } .account-container .account-site h2 { font-size: 16px; line-height: 16px; margin-bottom: 4px; margin-top: 0 !important; } .account-container .account-site p { margin-bottom: 2px; } .account-container .account-stat { width: 80px; height: 52px; text-align: center; color: #A1A1A1; font-size: 12px; float: left; margin-left: 15px; } .account-container .account-stat .account-number { color: var(--black-600); display: inline-block; width: 100%; font-size: 20px; font-family: Arial,Helvetica,sans-serif; line-height: 1.6; background: var(--black-025); } .account-container .account-stat .account-number, .account-container .account-stat .account-badges { height: 32px; } .account-container .account-stat .account-badges { font-size: 15px; line-height: 31px; height: 31px !important; color: var(--black-600); } .account-container .account-stat .account-badges .badgecount { font-size: 15px; } .account-container .account-stat .account-badges .badge1, .account-container .account-stat .account-badges .badge2, .account-container .account-stat .account-badges .badge3 { margin-top: -5px; } .account-container .account-stat.account-stat-wide { width: 138px; } .account-container.hidden { background: var(--black-075); } .account-container.hidden .account-number { background: var(--black-075); } .account-container.hidden .account-icon { border: 1px solid var(--black-075); } `); // end stylesheet // On script run (function init() { if (/\d+/.test(location.pathname) === false) return; // Show posts by deleted user page else if (location.pathname.match('/users/[0-9]+/') && location.search.includes("tab=answers")) { const title = $('h2.fs-title'); const bookendButton = $(``).insertAfter(title); bookendButton.on('click', initMultiPostsTable) } })();