/** * [FOXTOOLS — PATROLLERS HELPER SCRIPT] * * •==============================================• * > Pencipta: Janorovic Volkov * > Pengembang: Janorovic Volkov * > Tipe: JavaScript (Module) * * Lihat [[WP:FT]] untuk informasi selengkapnya * tentang skrip ini * •==============================================• */ // <nowiki> mw.loader.load('mediawiki.ui.button'); (function(){ constFT=window.FoxTools||{}; constapi=newmw.Api(); functionnotiOK(msg){mw.notify?mw.notify(msg,{type:'success'}):alert(msg);} functionnotiWARN(msg){mw.notify?mw.notify(msg,{type:'warn'}):alert(msg);} functionaskComment(){ constc=prompt("🦊 FoxTools — Rollback\n\nKlik batal jika ingin membatalkan pengembalian.\nMasukkan komentar (opsional):",""); if(c===null)returnnull; if(c.trim()==="")return""; return`: ${c.trim()}`; } asyncfunctionfoxUndoRollback(page,undoRev,undoAfterRev,summary){ constparams={ action:'edit', title:page, undo:undoRev, summary, minor:true, tags:'FoxTools', redirect:false }; if(undoAfterRev)params.undoafter=undoAfterRev; returnawaitapi.postWithToken('csrf',params); } functionfindUndoAfter(revs,targetRevId,targetUser){ constidx=revs.findIndex(r=>r.revid===targetRevId); if(idx===-1)returnnull; for(leti=idx+1;i<revs.length;i++){ if(revs[i].user!==targetUser)returnrevs[i].revid; } returnnull; } asyncfunctionrollbackUser(page,targetRevId,user,isANB=false){ constrevs=awaitfetchRevisions(page,'max'); constidx=revs.findIndex(r=>r.revid===targetRevId); if(idx===-1)return; letundoRev=targetRevId; letundoAfter=null; for(leti=idx+1;i<revs.length;i++){ if(revs[i].user!==user){ undoAfter=revs[i].revid; break; } } leteditCount=0; for(leti=idx;i<revs.length;i++){ if(revs[i].user===user)editCount++; elsebreak; } constcomment=askComment(); if(comment===null)return; consttargetUserRev=undoAfter?awaitgetUserOfRevision(undoAfter):null; constsummary=isANB ?summaryANBToTarget(page,user,editCount,targetUserRev,comment) :summaryNormalToTarget(page,user,editCount,targetUserRev,comment); awaitfoxUndoRollback(page,undoRev,undoAfter,summary); notiOK(`🟢 Pengembalian revisi berhasil: ${user} (${editCount})`); location.reload(); } asyncfunctiongetUserOfRevision(revid){ constres=awaitapi.get({ action:'query', prop:'revisions', revids:revid, rvprop:'user', formatversion:2 }); constpage=res.query.pages[0]; constrev=page.revisions&&page.revisions[0]; returnrev?rev.user:null; } asyncfunctiongetLatestRevId(page){ constres=awaitapi.get({ action:'query', prop:'revisions', titles:page, rvlimit:1, rvprop:'ids', formatversion:2 }); constpageObj=res.query.pages&&res.query.pages[0]; returnpageObj&&pageObj.revisions&&pageObj.revisions[0]&&pageObj.revisions[0].revid; } asyncfunctionfetchRevisions(page,limit='max'){ constres=awaitapi.get({ action:'query', prop:'revisions', titles:page, rvlimit:limit, rvprop:'ids|user|timestamp', formatversion:2 }); constrevs=(res.query.pages&&res.query.pages[0]&&res.query.pages[0].revisions)||[]; returnrevs; } functionfindLastGoodRevIdFromRevisions(revs,targetRevId,targetUser){ letidx=revs.findIndex(r=>r.revid===targetRevId); if(idx===-1)returnnull; for(leti=idx+1;i<revs.length;i++){ if(revs[i].user!==targetUser){ returnrevs[i].revid; } } returnnull; } functioncountConsecutiveEdits(revs,targetRevId,targetUser){ letidx=revs.findIndex(r=>r.revid===targetRevId); if(idx===-1)return1; letcount=0; for(leti=idx;i<revs.length;i++){ if(revs[i].user===targetUser)count++; elsebreak; } returnMath.max(1,count); } functionsummaryNormalToTarget(page,sourceUser,count,targetUser,comment){ if(count&&count>1){ return`Mengembalikan ${count} suntingan oleh [[Istimewa:Kontribusi/${sourceUser}|${sourceUser}]] ([[User talk:${sourceUser}|bicara]]) ke revisi terakhir oleh [[Istimewa:Kontribusi/${targetUser}|${targetUser}]]${comment} (${FT.ads})`; } return`Mengembalikan suntingan oleh [[Istimewa:Kontribusi/${sourceUser}|${sourceUser}]] ([[User talk:${sourceUser}|bicara]]) ke revisi terakhir oleh [[Istimewa:Kontribusi/${targetUser}|${targetUser}]]${comment} (${FT.ads})`; } functionsummaryANBToTarget(page,sourceUser,count,targetUser,comment){ if(count&&count>1){ return`Membatalkan ${count} [[WP:ANB|suntingan berniat baik]] oleh [[Istimewa:Kontribusi/${sourceUser}|${sourceUser}]] ([[User talk:${sourceUser}|bicara]]) ke revisi terakhir oleh [[Istimewa:Kontribusi/${targetUser}|${targetUser}]]${comment} (${FT.ads})`; } return`Membatalkan [[WP:ANB|suntingan berniat baik]] oleh [[Istimewa:Kontribusi/${sourceUser}|${sourceUser}]] ([[User talk:${sourceUser}|bicara]]) ke revisi terakhir oleh [[Istimewa:Kontribusi/${targetUser}|${targetUser}]]${comment} (${FT.ads})`; } functionsummaryRestoreToTarget(page,revId,targetUser,comment){ return`Memulihkan ke revisi [[Istimewa:Diff/${revId}|${revId}]] oleh [[Istimewa:Kontribusi/${targetUser}|${targetUser}]]${comment} (${FT.ads})`; } functionmkButton(text,classes='',title=''){ constbtn=document.createElement('button'); btn.className='mw-ui-button '+classes; btn.textContent=text; if(title)btn.title=title; returnbtn; } functionattachButtonsToListItem($li,page,revid,user){ if($li.find('.foxtools-rollback-panel').length)return; constpanel=document.createElement('span'); panel.className='foxtools-rollback-panel'; panel.style.marginLeft='8px'; panel.style.display='inline-flex'; panel.style.gap='6px'; constbtnANB=mkButton('Kembalikan (ANB)','mw-ui-button mw-ui-progressive'); constbtnNormal=mkButton('Kembalikan','mw-ui-button mw-ui-destructive'); btnANB.onclick=async()=>{ constuser=awaitgetUserOfRevision(revid); awaitrollbackUser(page,revid,user,true); }; btnNormal.onclick=async()=>{ constuser=awaitgetUserOfRevision(revid); awaitrollbackUser(page,revid,user,false); }; panel.appendChild(btnANB); panel.appendChild(btnNormal); const$tag=$li.find('.mw-tag-markers').first(); if($tag.length){ $tag.before(panel); }else{ $li.append(panel); } } functionattachHistoryButtons($row,page,revid,user){ if($row.find('.foxtools-rollback-panel-hist').length)return; constpanel=document.createElement('span'); panel.className='foxtools-rollback-panel-hist'; panel.style.marginLeft='8px'; panel.style.display='inline-flex'; panel.style.gap='6px'; constbtnANB=mkButton('Kembalikan (ANB)','mw-ui-button mw-ui-progressive'); constbtnNormal=mkButton('Kembalikan','mw-ui-button mw-ui-destructive'); btnANB.onclick=async(e)=>{ e.preventDefault(); e.stopPropagation(); constuser=awaitgetUserOfRevision(revid); awaitrollbackUser(page,revid,user,true); }; btnNormal.onclick=async(e)=>{ e.preventDefault(); e.stopPropagation(); constuser=awaitgetUserOfRevision(revid); awaitrollbackUser(page,revid,user,false); }; panel.appendChild(btnANB); panel.appendChild(btnNormal); const$tag=$row.find('.mw-tag-markers').first(); if($tag.length){ $tag.before(panel); }else{ $row.append(panel); } } asyncfunctionaddFoxRollbackButtons(){ constpageType=mw.config.get('wgCanonicalSpecialPageName'); if(!['Contributions','IPContributions'].includes(pageType))return; constcfg=mw.config.get(); $('li[data-mw-revid]').each(asyncfunction(){ const$li=$(this); constrevid=$li.data('mw-revid'); if(!revid)return; letpage=mw.config.get('wgPageName'); const$titleEl=$li.find('.mw-contributions-title, .mw-title, .mw-contrib-title').first(); if($titleEl.length){ constt=$titleEl.text().trim(); if(t)page=t.replace(/ /g,'_'); } lettargetUser=mw.config.get('wgRelevantUserName')||''; const$userlink=$li.find('.mw-userlink, .mw-contrib-username, .mw-contributions-username').first(); if($userlink.length)targetUser=$userlink.text().trim()||targetUser; if(!targetUser)return; if($li.data('foxtools-processed'))return; letlatestRevId=null; try{ latestRevId=awaitgetLatestRevId(page); }catch(err){ console.warn('Gagal mendapatkan revisi terbaru untuk',page,err); return; } if(latestRevId!==revid)return; letrevs=[]; try{ revs=awaitfetchRevisions(page,50); }catch(err){ console.warn('Gagal fetch revisions',err); } constlastGoodRevId=findLastGoodRevIdFromRevisions(revs,revid,targetUser); consteditCount=countConsecutiveEdits(revs,revid,targetUser); attachButtonsToListItem($li,page,revid,targetUser,editCount,lastGoodRevId); $li.data('foxtools-processed',true); }); } asyncfunctionaddFoxRollbackInHistory(){ if(mw.config.get('wgAction')!=='history')return; constpage=mw.config.get('wgPageName'); const$rows=$('li[data-mw-revid]'); if(!$rows.length)return; letlatest; try{ latest=awaitgetLatestRevId(page); }catch(err){ console.warn('Gagal mendapatkan revisi terbaru',err); return; } const$top=$rows.first(); consttopRevid=$top.data('mw-revid'); consttopUser=$top.find('.mw-userlink').text().trim(); if(topRevid===latest){ letrevs=[]; try{ revs=awaitfetchRevisions(page,'max'); }catch(err){ console.warn('Gagal fetch revisions for history',err); } constlastGoodTop=findLastGoodRevIdFromRevisions(revs,topRevid,topUser); attachHistoryButtons($top,page,topRevid,topUser,lastGoodTop); }else{ if($top.data('foxtools-restore-added'))return; $top.data('foxtools-restore-added',true); constrevid=topRevid; constrestorePanel=document.createElement('span'); restorePanel.className='foxtools-restore'; restorePanel.style.marginLeft='8px'; constbtnRestore=mkButton('Pulihkan revisi','mw-ui-button mw-ui-progressive'); btnRestore.onclick=async(e)=>{ e.preventDefault(); e.stopPropagation(); try{ constcomment=askComment(); if(comment===null)return; consttargetUser=awaitgetUserOfRevision(revid); constsummary=summaryRestoreToTarget(page,revid,targetUser,comment); awaitfoxUndoRollback(page,latest,revid,summary); notiOK(`🟢 Halaman berhasil dipulihkan ke revisi ${revid}`); location.reload(); }catch(err){ console.error(err); notiWARN(`⚠️ Gagal memulihkan ke revisi ${revid}`); } }; restorePanel.appendChild(btnRestore); const$tag=$top.find('.mw-tag-markers').first(); if($tag.length){ $tag.before(restorePanel); }else{ $top.append(restorePanel); } } $rows.each(function(){ const$row=$(this); if($row.is($top))return; if($row.find('.foxtools-restore-only').length)return; constrevid=$row.data('mw-revid'); constrestorePanel=document.createElement('span'); restorePanel.className='foxtools-restore-only'; restorePanel.style.marginLeft='8px'; constbtnRestore=mkButton('Pulihkan revisi','mw-ui-button mw-ui-progressive'); btnRestore.onclick=async(e)=>{ e.preventDefault(); e.stopPropagation(); try{ constcomment=askComment(); if(comment===null)return; consttargetUser=awaitgetUserOfRevision(revid); constsummary=summaryRestoreToTarget(page,revid,targetUser,comment); awaitfoxUndoRollback(page,latest,revid,summary); notiOK(`🟢 Halaman berhasil dipulihkan ke revisi ${revid}`); location.reload(); }catch(err){ console.error(err); notiWARN(`⚠️ Gagal memulihkan ke revisi ${revid}`); } }; restorePanel.appendChild(btnRestore); const$tag=$row.find('.mw-tag-markers').first(); if($tag.length){ $tag.before(restorePanel); }else{ $row.append(restorePanel); } }); } asyncfunctionattachDiffButtons(){ constpage=mw.config.get('wgPageName'); constdiffId=Number(mw.util.getParamValue('diff')); constoldId=Number(mw.util.getParamValue('oldid')); if(!diffId)return; constlatest=awaitgetLatestRevId(page); constwaitForDiff=()=>newPromise(resolve=>{ constel=document.querySelector('#mw-content-text .diff'); if(el)returnresolve(el); constobs=newMutationObserver((mutations,observer)=>{ constel2=document.querySelector('#mw-content-text .diff'); if(el2){ observer.disconnect(); resolve(el2); } }); obs.observe(document.body,{childList:true,subtree:true}); }); constdiffElement=awaitwaitForDiff(); letpanel=document.querySelector('.foxtools-diff-inline'); if(!panel){ panel=document.createElement('div'); panel.className='foxtools-diff-inline'; Object.assign(panel.style,{ display:'flex', gap:'6px', marginBottom:'6px' }); diffElement.parentNode.insertBefore(panel,diffElement); }else{ panel.innerHTML=''; } constmakeBtn=(label,mwClass,onClick)=>{ constbtn=document.createElement('div'); btn.textContent=label; btn.className=mwClass; Object.assign(btn.style,{ cursor:'pointer', padding:'4px 8px', textAlign:'center' }); btn.onclick=onClick; returnbtn; }; if(diffId===latest){ constbtnANB=makeBtn('Kembalikan (ANB)','mw-ui-button mw-ui-progressive'); constbtnNormal=makeBtn('Kembalikan','mw-ui-button mw-ui-destructive'); btnANB.onclick=async()=>{ constuser=awaitgetUserOfRevision(diffId); awaitrollbackUser(page,diffId,user,true); }; btnNormal.onclick=async()=>{ constuser=awaitgetUserOfRevision(diffId); awaitrollbackUser(page,diffId,user,false); }; panel.appendChild(btnANB); panel.appendChild(btnNormal); }else{ constbtnRestore=makeBtn('Pulihkan revisi','mw-ui-button mw-ui-progressive',async()=>{ constcomment=askComment(); if(comment===null)return; consttargetUser=awaitgetUserOfRevision(diffId); constsummary=summaryRestoreToTarget(page,diffId,targetUser,comment); awaitfoxUndoRollback(page,latest,diffId,summary); notiOK(`🟢 Halaman berhasil dipulihkan ke revisi ${diffId}`); location.reload(); }); panel.appendChild(btnRestore); } } constmodule={ name:'Rollback', init(){ $(addFoxRollbackButtons); $(addFoxRollbackInHistory); $(attachDiffButtons); constobserver=newMutationObserver(()=>{ try{ addFoxRollbackButtons(); addFoxRollbackInHistory(); attachDiffButtons(); }catch(e){} }); observer.observe(document.body,{childList:true,subtree:true}); console.log('[FoxTools] Rollback module initialized.'); }, openPanel(){} }; FT.register&&FT.register('Rollback',module); })(); // </nowiki>
MediaWiki:Gadget-FoxTools-Rollback.js
Konten disalin dari Wikipedia Bahasa Indonesia (lisensi CC BY-SA)
Lihat versi asli di Wikipedia